refactor(scripts): move Scripts API from scheduler to release-orchestrator
- Fix dual-schema violation (scheduler was writing to scheduler + scripts) - Move ScriptsDataSource, PostgresScriptStore, script endpoints - Update gateway routes and UI references - Each service now owns exactly one schema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.Plugin.DependencyInjection;
|
||||
using StellaOps.Plugin.Hosting;
|
||||
using StellaOps.Router.AspNet;
|
||||
using StellaOps.Scheduler.Plugin;
|
||||
using StellaOps.Scheduler.ImpactIndex;
|
||||
using StellaOps.Scheduler.Models;
|
||||
using StellaOps.Scheduler.Persistence.Extensions;
|
||||
@@ -29,12 +30,8 @@ using StellaOps.Scheduler.WebService.PolicyRuns;
|
||||
using StellaOps.Scheduler.WebService.PolicySimulations;
|
||||
using StellaOps.Scheduler.WebService.Runs;
|
||||
using StellaOps.Scheduler.WebService.Schedules;
|
||||
using StellaOps.Scheduler.WebService.Scripts;
|
||||
using StellaOps.Scheduler.WebService.Exceptions;
|
||||
using StellaOps.Scheduler.WebService.VulnerabilityResolverJobs;
|
||||
using StellaOps.ReleaseOrchestrator.Scripts;
|
||||
using StellaOps.ReleaseOrchestrator.Scripts.Persistence;
|
||||
using StellaOps.ReleaseOrchestrator.Scripts.Search;
|
||||
using StellaOps.Scheduler.Worker.Exceptions;
|
||||
using StellaOps.Scheduler.Worker.Observability;
|
||||
using StellaOps.Scheduler.Worker.Options;
|
||||
@@ -123,16 +120,6 @@ else
|
||||
builder.Services.AddSingleton<ISchedulerAuditService, InMemorySchedulerAuditService>();
|
||||
builder.Services.AddSingleton<IPolicyRunService, InMemoryPolicyRunService>();
|
||||
}
|
||||
// Scripts registry (shares the same Postgres options as Scheduler)
|
||||
builder.Services.AddSingleton<ScriptsDataSource>();
|
||||
builder.Services.AddSingleton<IScriptStore, PostgresScriptStore>();
|
||||
builder.Services.AddSingleton<ISearchIndexer, InMemorySearchIndexer>();
|
||||
builder.Services.AddSingleton<IScriptValidator, ScriptValidator>();
|
||||
builder.Services.AddSingleton<ILanguageValidator, CSharpScriptValidator>();
|
||||
builder.Services.AddSingleton<ILanguageValidator, PythonScriptValidator>();
|
||||
builder.Services.AddSingleton<ILanguageValidator, TypeScriptScriptValidator>();
|
||||
builder.Services.AddSingleton<IScriptRegistry, ScriptRegistry>();
|
||||
|
||||
// Workflow engine HTTP client (starts workflow instances for system schedules)
|
||||
builder.Services.AddHttpClient<StellaOps.Scheduler.WebService.Workflow.WorkflowTriggerClient>((sp, client) =>
|
||||
{
|
||||
@@ -188,6 +175,28 @@ var pluginHostOptions = SchedulerPluginHostFactory.Build(schedulerOptions.Plugin
|
||||
builder.Services.AddSingleton(pluginHostOptions);
|
||||
builder.Services.RegisterPluginRoutines(builder.Configuration, pluginHostOptions);
|
||||
|
||||
// Scheduler plugin registry: discover and register ISchedulerJobPlugin implementations
|
||||
var pluginRegistry = new SchedulerPluginRegistry();
|
||||
|
||||
// Register built-in scan plugin (default for all existing schedules)
|
||||
var scanPlugin = new ScanJobPlugin();
|
||||
pluginRegistry.Register(scanPlugin);
|
||||
|
||||
// Discover ISchedulerJobPlugin implementations from assembly-loaded plugins
|
||||
var loadResult = PluginHost.LoadPlugins(pluginHostOptions);
|
||||
foreach (var pluginAssembly in loadResult.Plugins)
|
||||
{
|
||||
var jobPlugins = StellaOps.Plugin.PluginLoader.LoadPlugins<ISchedulerJobPlugin>(
|
||||
new[] { pluginAssembly.Assembly });
|
||||
foreach (var jobPlugin in jobPlugins)
|
||||
{
|
||||
pluginRegistry.Register(jobPlugin);
|
||||
jobPlugin.ConfigureServices(builder.Services, builder.Configuration);
|
||||
}
|
||||
}
|
||||
|
||||
builder.Services.AddSingleton<ISchedulerPluginRegistry>(pluginRegistry);
|
||||
|
||||
if (authorityOptions.Enabled)
|
||||
{
|
||||
builder.Services.AddHttpContextAccessor();
|
||||
@@ -319,7 +328,13 @@ app.MapFailureSignatureEndpoints();
|
||||
app.MapPolicyRunEndpoints();
|
||||
app.MapPolicySimulationEndpoints();
|
||||
app.MapSchedulerEventWebhookEndpoints();
|
||||
app.MapScriptsEndpoints();
|
||||
|
||||
// Map plugin-provided endpoints (e.g., Doctor trend endpoints)
|
||||
foreach (var (jobKind, _) in pluginRegistry.ListRegistered())
|
||||
{
|
||||
var plugin = pluginRegistry.Resolve(jobKind);
|
||||
plugin?.MapEndpoints(app);
|
||||
}
|
||||
|
||||
// Refresh Router endpoint cache
|
||||
app.TryRefreshStellaRouterEndpoints(routerEnabled);
|
||||
|
||||
@@ -1,408 +0,0 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using StellaOps.Auth.ServerIntegration.Tenancy;
|
||||
using StellaOps.ReleaseOrchestrator.Scripts;
|
||||
using StellaOps.Scheduler.WebService.Auth;
|
||||
using StellaOps.Scheduler.WebService.Security;
|
||||
|
||||
namespace StellaOps.Scheduler.WebService.Scripts;
|
||||
|
||||
/// <summary>
|
||||
/// Minimal API endpoints for the Scripts registry (/api/v2/scripts).
|
||||
/// </summary>
|
||||
internal static class ScriptsEndpoints
|
||||
{
|
||||
private static readonly JsonSerializerOptions s_json = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) },
|
||||
};
|
||||
|
||||
public static IEndpointRouteBuilder MapScriptsEndpoints(this IEndpointRouteBuilder routes)
|
||||
{
|
||||
var group = routes.MapGroup("/api/v2/scripts")
|
||||
.RequireAuthorization(SchedulerPolicies.Read)
|
||||
.RequireTenant();
|
||||
|
||||
group.MapGet("/", ListScriptsAsync)
|
||||
.WithName("ListScripts")
|
||||
.WithDescription("List scripts with optional search, language, and visibility filters.");
|
||||
|
||||
group.MapGet("/{scriptId}", GetScriptAsync)
|
||||
.WithName("GetScript")
|
||||
.WithDescription("Get a single script by ID.");
|
||||
|
||||
group.MapPost("/", CreateScriptAsync)
|
||||
.WithName("CreateScript")
|
||||
.WithDescription("Create a new script.")
|
||||
.RequireAuthorization(SchedulerPolicies.Operate);
|
||||
|
||||
group.MapPatch("/{scriptId}", UpdateScriptAsync)
|
||||
.WithName("UpdateScript")
|
||||
.WithDescription("Update an existing script.")
|
||||
.RequireAuthorization(SchedulerPolicies.Operate);
|
||||
|
||||
group.MapDelete("/{scriptId}", DeleteScriptAsync)
|
||||
.WithName("DeleteScript")
|
||||
.WithDescription("Delete a script.")
|
||||
.RequireAuthorization(SchedulerPolicies.Operate);
|
||||
|
||||
group.MapPost("/validate", ValidateScriptAsync)
|
||||
.WithName("ValidateScript")
|
||||
.WithDescription("Validate script syntax without saving.");
|
||||
|
||||
group.MapGet("/{scriptId}/versions", GetVersionsAsync)
|
||||
.WithName("ListScriptVersions")
|
||||
.WithDescription("List all versions of a script.");
|
||||
|
||||
group.MapGet("/{scriptId}/versions/{version:int}/content", GetVersionContentAsync)
|
||||
.WithName("GetScriptVersionContent")
|
||||
.WithDescription("Get the content of a specific script version.");
|
||||
|
||||
group.MapPost("/{scriptId}/check-compatibility", CheckCompatibilityAsync)
|
||||
.WithName("CheckScriptCompatibility")
|
||||
.WithDescription("Check script compatibility with a target environment.");
|
||||
|
||||
return routes;
|
||||
}
|
||||
|
||||
// ── List ────────────────────────────────────────────────────────────────
|
||||
|
||||
private static async Task<IResult> ListScriptsAsync(
|
||||
HttpContext httpContext,
|
||||
[FromServices] IScriptRegistry registry,
|
||||
CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
var q = httpContext.Request.Query;
|
||||
var criteria = new ScriptSearchCriteria
|
||||
{
|
||||
SearchText = q.TryGetValue("search", out var s) ? s.ToString() : null,
|
||||
Language = q.TryGetValue("language", out var lang) ? ParseLanguage(lang.ToString()) : null,
|
||||
Visibility = q.TryGetValue("visibility", out var vis) ? ParseVisibility(vis.ToString()) : null,
|
||||
Limit = q.TryGetValue("limit", out var lim) && int.TryParse(lim.ToString(), out var l) ? l : 20,
|
||||
Offset = q.TryGetValue("offset", out var off) && int.TryParse(off.ToString(), out var o) ? o : 0,
|
||||
};
|
||||
|
||||
var result = await registry.SearchAsync(criteria, ct).ConfigureAwait(false);
|
||||
var dtos = result.Scripts.Select(ToDto).ToArray();
|
||||
return Results.Json(dtos, s_json);
|
||||
}
|
||||
catch (Exception ex) when (ex is ArgumentException)
|
||||
{
|
||||
return Results.BadRequest(new { error = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
// ── Get ─────────────────────────────────────────────────────────────────
|
||||
|
||||
private static async Task<IResult> GetScriptAsync(
|
||||
string scriptId,
|
||||
[FromServices] IScriptRegistry registry,
|
||||
CancellationToken ct)
|
||||
{
|
||||
var script = await registry.GetScriptAsync(scriptId, ct).ConfigureAwait(false);
|
||||
if (script is null) return Results.NotFound(new { error = $"Script '{scriptId}' not found." });
|
||||
return Results.Json(ToDto(script), s_json);
|
||||
}
|
||||
|
||||
// ── Create ──────────────────────────────────────────────────────────────
|
||||
|
||||
private static async Task<IResult> CreateScriptAsync(
|
||||
HttpContext httpContext,
|
||||
[FromServices] IScriptRegistry registry,
|
||||
[FromServices] ITenantContextAccessor tenantAccessor,
|
||||
CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
var body = await JsonSerializer.DeserializeAsync<CreateScriptDto>(httpContext.Request.Body, s_json, ct).ConfigureAwait(false);
|
||||
if (body is null) return Results.BadRequest(new { error = "Invalid request body." });
|
||||
|
||||
var tenant = tenantAccessor.GetTenant(httpContext);
|
||||
var userId = httpContext.User.FindFirst("sub")?.Value ?? "anonymous";
|
||||
|
||||
var request = new CreateScriptRequest
|
||||
{
|
||||
Name = body.Name,
|
||||
Description = body.Description,
|
||||
Language = ParseLanguageRequired(body.Language),
|
||||
Content = body.Content,
|
||||
Tags = body.Tags?.ToImmutableArray(),
|
||||
Visibility = ParseVisibilityRequired(body.Visibility),
|
||||
};
|
||||
|
||||
var script = await registry.CreateScriptAsync(request, userId, ct: ct).ConfigureAwait(false);
|
||||
|
||||
// Update variables on the created script via store if provided
|
||||
return Results.Json(ToDto(script), s_json, statusCode: StatusCodes.Status201Created);
|
||||
}
|
||||
catch (ScriptValidationException ex)
|
||||
{
|
||||
return Results.BadRequest(new { error = ex.Message, errors = ex.Errors });
|
||||
}
|
||||
catch (Exception ex) when (ex is ArgumentException or JsonException)
|
||||
{
|
||||
return Results.BadRequest(new { error = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
// ── Update ──────────────────────────────────────────────────────────────
|
||||
|
||||
private static async Task<IResult> UpdateScriptAsync(
|
||||
string scriptId,
|
||||
HttpContext httpContext,
|
||||
[FromServices] IScriptRegistry registry,
|
||||
[FromServices] ITenantContextAccessor tenantAccessor,
|
||||
CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
var body = await JsonSerializer.DeserializeAsync<UpdateScriptDto>(httpContext.Request.Body, s_json, ct).ConfigureAwait(false);
|
||||
if (body is null) return Results.BadRequest(new { error = "Invalid request body." });
|
||||
|
||||
var tenant = tenantAccessor.GetTenant(httpContext);
|
||||
var userId = httpContext.User.FindFirst("sub")?.Value ?? "anonymous";
|
||||
|
||||
var request = new UpdateScriptRequest
|
||||
{
|
||||
Name = body.Name,
|
||||
Description = body.Description,
|
||||
Content = body.Content,
|
||||
Tags = body.Tags?.ToImmutableArray(),
|
||||
Visibility = body.Visibility is not null ? ParseVisibilityRequired(body.Visibility) : null,
|
||||
ChangeNote = body.ChangeNotes,
|
||||
};
|
||||
|
||||
var updated = await registry.UpdateScriptAsync(scriptId, request, userId, ct).ConfigureAwait(false);
|
||||
return Results.Json(ToDto(updated), s_json);
|
||||
}
|
||||
catch (ScriptNotFoundException)
|
||||
{
|
||||
return Results.NotFound(new { error = $"Script '{scriptId}' not found." });
|
||||
}
|
||||
catch (ScriptValidationException ex)
|
||||
{
|
||||
return Results.BadRequest(new { error = ex.Message, errors = ex.Errors });
|
||||
}
|
||||
catch (Exception ex) when (ex is ArgumentException or JsonException)
|
||||
{
|
||||
return Results.BadRequest(new { error = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
// ── Delete ──────────────────────────────────────────────────────────────
|
||||
|
||||
private static async Task<IResult> DeleteScriptAsync(
|
||||
string scriptId,
|
||||
[FromServices] IScriptRegistry registry,
|
||||
CancellationToken ct)
|
||||
{
|
||||
var deleted = await registry.DeleteScriptAsync(scriptId, ct).ConfigureAwait(false);
|
||||
return deleted
|
||||
? Results.NoContent()
|
||||
: Results.NotFound(new { error = $"Script '{scriptId}' not found." });
|
||||
}
|
||||
|
||||
// ── Validate ────────────────────────────────────────────────────────────
|
||||
|
||||
private static async Task<IResult> ValidateScriptAsync(
|
||||
HttpContext httpContext,
|
||||
[FromServices] IScriptRegistry registry,
|
||||
CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
var body = await JsonSerializer.DeserializeAsync<ValidateScriptDto>(httpContext.Request.Body, s_json, ct).ConfigureAwait(false);
|
||||
if (body is null) return Results.BadRequest(new { error = "Invalid request body." });
|
||||
|
||||
var language = ParseLanguageRequired(body.Language);
|
||||
var result = await registry.ValidateAsync(language, body.Content, ct).ConfigureAwait(false);
|
||||
|
||||
var response = new
|
||||
{
|
||||
isValid = result.IsValid,
|
||||
errors = result.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error).Select(d => new
|
||||
{
|
||||
line = d.Line,
|
||||
column = d.Column,
|
||||
message = d.Message,
|
||||
severity = "error",
|
||||
category = "syntax",
|
||||
}).ToArray(),
|
||||
warnings = result.Diagnostics.Where(d => d.Severity != DiagnosticSeverity.Error).Select(d => new
|
||||
{
|
||||
line = d.Line,
|
||||
column = d.Column,
|
||||
message = d.Message,
|
||||
severity = d.Severity == DiagnosticSeverity.Warning ? "warning" : "info",
|
||||
category = "syntax",
|
||||
}).ToArray(),
|
||||
};
|
||||
return Results.Json(response, s_json);
|
||||
}
|
||||
catch (Exception ex) when (ex is ArgumentException or JsonException)
|
||||
{
|
||||
return Results.BadRequest(new { error = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
// ── Versions ────────────────────────────────────────────────────────────
|
||||
|
||||
private static async Task<IResult> GetVersionsAsync(
|
||||
string scriptId,
|
||||
[FromServices] IScriptRegistry registry,
|
||||
CancellationToken ct)
|
||||
{
|
||||
var versions = await registry.GetScriptVersionsAsync(scriptId, ct).ConfigureAwait(false);
|
||||
var dtos = versions.Select(v => new
|
||||
{
|
||||
version = v.Version,
|
||||
contentHash = v.ContentHash,
|
||||
createdBy = v.CreatedBy,
|
||||
createdAt = v.CreatedAt,
|
||||
changeNotes = v.ChangeNote,
|
||||
}).ToArray();
|
||||
return Results.Json(dtos, s_json);
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetVersionContentAsync(
|
||||
string scriptId,
|
||||
int version,
|
||||
[FromServices] IScriptRegistry registry,
|
||||
CancellationToken ct)
|
||||
{
|
||||
var ver = await registry.GetScriptVersionAsync(scriptId, version, ct).ConfigureAwait(false);
|
||||
if (ver is null) return Results.NotFound(new { error = $"Version {version} not found for script '{scriptId}'." });
|
||||
|
||||
var dto = new
|
||||
{
|
||||
version = ver.Version,
|
||||
contentHash = ver.ContentHash,
|
||||
createdBy = ver.CreatedBy,
|
||||
createdAt = ver.CreatedAt,
|
||||
changeNotes = ver.ChangeNote,
|
||||
content = ver.Content,
|
||||
};
|
||||
return Results.Json(dto, s_json);
|
||||
}
|
||||
|
||||
// ── Compatibility ───────────────────────────────────────────────────────
|
||||
|
||||
private static async Task<IResult> CheckCompatibilityAsync(
|
||||
string scriptId,
|
||||
HttpContext httpContext,
|
||||
[FromServices] IScriptRegistry registry,
|
||||
CancellationToken ct)
|
||||
{
|
||||
// Stub: always compatible. Real implementation will check runtime/target matrix.
|
||||
var script = await registry.GetScriptAsync(scriptId, ct).ConfigureAwait(false);
|
||||
if (script is null) return Results.NotFound(new { error = $"Script '{scriptId}' not found." });
|
||||
|
||||
var response = new { isCompatible = true, issues = Array.Empty<object>() };
|
||||
return Results.Json(response, s_json);
|
||||
}
|
||||
|
||||
// ── DTO mapping ─────────────────────────────────────────────────────────
|
||||
|
||||
private static object ToDto(Script s) => new
|
||||
{
|
||||
id = s.Id,
|
||||
name = s.Name,
|
||||
description = s.Description,
|
||||
language = s.Language.ToString().ToLowerInvariant(),
|
||||
content = s.Content,
|
||||
version = s.Version,
|
||||
visibility = s.Visibility.ToString().ToLowerInvariant(),
|
||||
ownerId = s.OwnerId,
|
||||
teamId = s.TeamId,
|
||||
tags = s.Tags.ToArray(),
|
||||
variables = s.Variables.Select(v => new
|
||||
{
|
||||
name = v.Name,
|
||||
description = v.Description,
|
||||
isRequired = v.IsRequired,
|
||||
defaultValue = v.DefaultValue,
|
||||
isSecret = v.IsSecret,
|
||||
}).ToArray(),
|
||||
contentHash = s.ContentHash,
|
||||
isSample = s.IsSample,
|
||||
sampleCategory = s.SampleCategory,
|
||||
createdAt = s.CreatedAt,
|
||||
updatedAt = s.UpdatedAt,
|
||||
};
|
||||
|
||||
// ── DTO types ───────────────────────────────────────────────────────────
|
||||
|
||||
private sealed record CreateScriptDto
|
||||
{
|
||||
public string Name { get; init; } = "";
|
||||
public string Description { get; init; } = "";
|
||||
public string Language { get; init; } = "";
|
||||
public string Content { get; init; } = "";
|
||||
public string Visibility { get; init; } = "private";
|
||||
public string[]? Tags { get; init; }
|
||||
public ScriptVariableDto[]? Variables { get; init; }
|
||||
}
|
||||
|
||||
private sealed record UpdateScriptDto
|
||||
{
|
||||
public string? Name { get; init; }
|
||||
public string? Description { get; init; }
|
||||
public string? Content { get; init; }
|
||||
public string? Visibility { get; init; }
|
||||
public string[]? Tags { get; init; }
|
||||
public ScriptVariableDto[]? Variables { get; init; }
|
||||
public string? ChangeNotes { get; init; }
|
||||
}
|
||||
|
||||
private sealed record ValidateScriptDto
|
||||
{
|
||||
public string Language { get; init; } = "";
|
||||
public string Content { get; init; } = "";
|
||||
}
|
||||
|
||||
private sealed record ScriptVariableDto
|
||||
{
|
||||
public string Name { get; init; } = "";
|
||||
public string? Description { get; init; }
|
||||
public bool IsRequired { get; init; }
|
||||
public string? DefaultValue { get; init; }
|
||||
public bool IsSecret { get; init; }
|
||||
}
|
||||
|
||||
// ── Enum parsing ────────────────────────────────────────────────────────
|
||||
|
||||
private static ScriptLanguage? ParseLanguage(string? value) => value?.ToLowerInvariant() switch
|
||||
{
|
||||
"csharp" => ScriptLanguage.CSharp,
|
||||
"python" => ScriptLanguage.Python,
|
||||
"java" => ScriptLanguage.Java,
|
||||
"go" => ScriptLanguage.Go,
|
||||
"bash" => ScriptLanguage.Bash,
|
||||
"typescript" => ScriptLanguage.TypeScript,
|
||||
"powershell" => ScriptLanguage.PowerShell,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
private static ScriptLanguage ParseLanguageRequired(string value)
|
||||
=> ParseLanguage(value) ?? throw new ArgumentException($"Unknown language: '{value}'");
|
||||
|
||||
private static ScriptVisibility? ParseVisibility(string? value) => value?.ToLowerInvariant() switch
|
||||
{
|
||||
"private" => ScriptVisibility.Private,
|
||||
"team" => ScriptVisibility.Team,
|
||||
"organization" => ScriptVisibility.Organization,
|
||||
"public" => ScriptVisibility.Public,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
private static ScriptVisibility ParseVisibilityRequired(string value)
|
||||
=> ParseVisibility(value) ?? throw new ArgumentException($"Unknown visibility: '{value}'");
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../StellaOps.Scheduler.__Libraries/StellaOps.Scheduler.Models/StellaOps.Scheduler.Models.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Scheduler.__Libraries/StellaOps.Scheduler.Plugin.Abstractions/StellaOps.Scheduler.Plugin.Abstractions.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Scheduler.__Libraries/StellaOps.Scheduler.ImpactIndex/StellaOps.Scheduler.ImpactIndex.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Scheduler.__Libraries/StellaOps.Scheduler.Queue/StellaOps.Scheduler.Queue.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Scheduler.__Libraries/StellaOps.Scheduler.Persistence/StellaOps.Scheduler.Persistence.csproj" />
|
||||
@@ -23,7 +24,6 @@
|
||||
<ProjectReference Include="../../Router/__Libraries/StellaOps.Messaging/StellaOps.Messaging.csproj" />
|
||||
<ProjectReference Include="../../Router/__Libraries/StellaOps.Router.AspNet/StellaOps.Router.AspNet.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Localization/StellaOps.Localization.csproj" />
|
||||
<ProjectReference Include="../../ReleaseOrchestrator/__Libraries/StellaOps.ReleaseOrchestrator.Scripts/StellaOps.ReleaseOrchestrator.Scripts.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Translations\*.json" />
|
||||
|
||||
Reference in New Issue
Block a user