@treeseed/sdk 0.12.18 → 0.12.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -3308,12 +3308,20 @@ async function resolveRailwayTopologyForScope(input, scope, {
|
|
|
3308
3308
|
includeInstances = ensure,
|
|
3309
3309
|
includeVariables = false
|
|
3310
3310
|
} = {}) {
|
|
3311
|
+
const imageRefValues = resolveReconcileEnvironmentValues(input, scope);
|
|
3312
|
+
const imageRefFingerprint = [
|
|
3313
|
+
"TREESEED_API_IMAGE_REF",
|
|
3314
|
+
"TREESEED_OPERATIONS_RUNNER_IMAGE_REF",
|
|
3315
|
+
"TREESEED_AGENT_MANAGER_IMAGE_REF",
|
|
3316
|
+
"TREESEED_AGENT_RUNNER_IMAGE_REF",
|
|
3317
|
+
"TREESEED_PUBLIC_TREEDX_IMAGE_REF"
|
|
3318
|
+
].map((key) => `${key}=${imageRefValues[key] ?? ""}`).join("|");
|
|
3311
3319
|
const normalizedServiceKeys = Array.isArray(serviceKeys) && serviceKeys.length > 0 ? [...new Set(serviceKeys.map((value) => String(value).trim()).filter(Boolean))].sort() : ["__all__"];
|
|
3312
|
-
const cacheKey = `railway:topology:${scope}:${ensure ? "ensure" : "observe"}:${includeInstances ? "instances" : "no-instances"}:${includeVariables ? "variables" : "no-variables"}:${normalizedServiceKeys.join(",")}`;
|
|
3320
|
+
const cacheKey = `railway:topology:${scope}:${ensure ? "ensure" : "observe"}:${includeInstances ? "instances" : "no-instances"}:${includeVariables ? "variables" : "no-variables"}:${normalizedServiceKeys.join(",")}:${imageRefFingerprint}`;
|
|
3313
3321
|
return await providerCache(input, cacheKey, async () => {
|
|
3314
3322
|
const env = buildRailwayEnv(input, scope);
|
|
3315
3323
|
const deployState = loadDeployState(input.context.tenantRoot, input.context.deployConfig, { target: toDeployTarget(input.context.target) });
|
|
3316
|
-
const services =
|
|
3324
|
+
const services = configuredRailwayServicesForInput(input, scope).filter((service) => normalizedServiceKeys.includes("__all__") || normalizedServiceKeys.includes(service.key));
|
|
3317
3325
|
traceRailwayReconcile(env, "topology:start", `scope=${scope} ensure=${ensure ? "yes" : "no"} services=${services.map((service) => service.key).join(",")}`);
|
|
3318
3326
|
let workspace = null;
|
|
3319
3327
|
const knownProjects = [];
|
|
@@ -190,7 +190,7 @@ function deriveTreeseedDesiredUnits({
|
|
|
190
190
|
});
|
|
191
191
|
}
|
|
192
192
|
const scope = target.kind === "persistent" ? target.scope : "staging";
|
|
193
|
-
for (const configuredService of configuredRailwayServices(tenantRoot, scope)) {
|
|
193
|
+
for (const configuredService of configuredRailwayServices(tenantRoot, scope, env)) {
|
|
194
194
|
const serviceKey = configuredService.key;
|
|
195
195
|
const service = configuredService.serviceConfig ?? deployConfig.services?.[serviceKey];
|
|
196
196
|
const serviceState = legacyState.services?.[serviceKey];
|
|
@@ -217,6 +217,13 @@ function deriveTreeseedDesiredUnits({
|
|
|
217
217
|
serviceId: persistedServiceMatchesDesired ? serviceState?.serviceId ?? configuredService.serviceId : configuredService.serviceId,
|
|
218
218
|
serviceName: desiredServiceName,
|
|
219
219
|
rootDir: configuredService.rootDir ?? serviceState?.rootDir,
|
|
220
|
+
imageRef: configuredService.imageRef,
|
|
221
|
+
imageRefEnv: configuredService.serviceConfig?.railway?.imageRefEnv,
|
|
222
|
+
sourceMode: configuredService.sourceMode,
|
|
223
|
+
sourceRepo: configuredService.sourceRepo,
|
|
224
|
+
sourceBranch: configuredService.sourceBranch,
|
|
225
|
+
sourceCommit: configuredService.sourceCommit,
|
|
226
|
+
sourceRootDirectory: configuredService.sourceRootDirectory,
|
|
220
227
|
environment: normalizeRailwayEnvironmentName(configuredService.railwayEnvironment ?? serviceState?.environment),
|
|
221
228
|
buildCommand: configuredService.buildCommand,
|
|
222
229
|
startCommand: configuredService.startCommand,
|
|
@@ -91,7 +91,7 @@ export declare function workflowReleaseCandidate(helpers: WorkflowOperationHelpe
|
|
|
91
91
|
}>>;
|
|
92
92
|
type PublishedArtifactCheck = {
|
|
93
93
|
id: string;
|
|
94
|
-
kind: 'npm' | 'docker' | 'pypi' | 'crates' | 'hex';
|
|
94
|
+
kind: 'npm' | 'docker' | 'pypi' | 'crates' | 'hex' | 'github-tag';
|
|
95
95
|
name: string;
|
|
96
96
|
version: string;
|
|
97
97
|
url: string;
|
|
@@ -2576,6 +2576,46 @@ async function verifyNpmArtifact(packageName, version) {
|
|
|
2576
2576
|
};
|
|
2577
2577
|
}
|
|
2578
2578
|
}
|
|
2579
|
+
async function fetchDockerRegistryManifestStatus(image, version) {
|
|
2580
|
+
const [namespace, repository] = image.split("/");
|
|
2581
|
+
if (!namespace || !repository) {
|
|
2582
|
+
return { ok: false, status: null, message: `Invalid Docker image name ${image}.` };
|
|
2583
|
+
}
|
|
2584
|
+
const tokenUrl = `https://auth.docker.io/token?service=registry.docker.io&scope=repository:${namespace}/${repository}:pull`;
|
|
2585
|
+
try {
|
|
2586
|
+
const tokenResponse = await fetchJsonForArtifact(tokenUrl);
|
|
2587
|
+
const token = typeof stringRecord(tokenResponse.json)?.token === "string" ? String(stringRecord(tokenResponse.json)?.token) : "";
|
|
2588
|
+
if (!tokenResponse.ok || !token) {
|
|
2589
|
+
return { ok: false, status: tokenResponse.status, message: `Docker registry token request failed for ${image}.` };
|
|
2590
|
+
}
|
|
2591
|
+
const controller = new AbortController();
|
|
2592
|
+
const timeout = setTimeout(() => controller.abort(), 2e4);
|
|
2593
|
+
try {
|
|
2594
|
+
const manifestResponse = await fetch(`https://registry-1.docker.io/v2/${namespace}/${repository}/manifests/${version}`, {
|
|
2595
|
+
method: "HEAD",
|
|
2596
|
+
headers: {
|
|
2597
|
+
accept: [
|
|
2598
|
+
"application/vnd.docker.distribution.manifest.list.v2+json",
|
|
2599
|
+
"application/vnd.oci.image.index.v1+json",
|
|
2600
|
+
"application/vnd.docker.distribution.manifest.v2+json"
|
|
2601
|
+
].join(", "),
|
|
2602
|
+
authorization: `Bearer ${token}`,
|
|
2603
|
+
"user-agent": "treeseed-release-verifier/1.0 (https://treeseed.dev)"
|
|
2604
|
+
},
|
|
2605
|
+
signal: controller.signal
|
|
2606
|
+
});
|
|
2607
|
+
return {
|
|
2608
|
+
ok: manifestResponse.ok,
|
|
2609
|
+
status: manifestResponse.status,
|
|
2610
|
+
...manifestResponse.ok ? {} : { message: `Docker registry manifest for ${image}:${version} is not pullable yet.` }
|
|
2611
|
+
};
|
|
2612
|
+
} finally {
|
|
2613
|
+
clearTimeout(timeout);
|
|
2614
|
+
}
|
|
2615
|
+
} catch (error) {
|
|
2616
|
+
return { ok: false, status: null, message: error instanceof Error ? error.message : String(error) };
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2579
2619
|
async function verifyDockerHubArtifact(image, version) {
|
|
2580
2620
|
const [namespace, repository] = image.split("/");
|
|
2581
2621
|
const url = `https://hub.docker.com/v2/repositories/${namespace}/${repository}/tags/${version}`;
|
|
@@ -2583,7 +2623,8 @@ async function verifyDockerHubArtifact(image, version) {
|
|
|
2583
2623
|
const response = await fetchJsonForArtifact(url);
|
|
2584
2624
|
const images = Array.isArray(stringRecord(response.json)?.images) ? stringRecord(response.json)?.images : [];
|
|
2585
2625
|
const architectures = new Set(images.map((entry) => stringRecord(entry)).map((entry) => typeof entry?.architecture === "string" ? entry.architecture : null).filter((entry) => Boolean(entry)));
|
|
2586
|
-
const
|
|
2626
|
+
const registry = await fetchDockerRegistryManifestStatus(image, version);
|
|
2627
|
+
const ok = response.ok && architectures.has("amd64") && architectures.has("arm64") && registry.ok;
|
|
2587
2628
|
return {
|
|
2588
2629
|
id: `docker:${image}:${version}`,
|
|
2589
2630
|
kind: "docker",
|
|
@@ -2591,8 +2632,8 @@ async function verifyDockerHubArtifact(image, version) {
|
|
|
2591
2632
|
version,
|
|
2592
2633
|
url,
|
|
2593
2634
|
ok,
|
|
2594
|
-
status: response.status,
|
|
2595
|
-
...ok ? {} : { message: `${image}:${version} was not found on Docker Hub with amd64 and arm64 images.` }
|
|
2635
|
+
status: registry.status ?? response.status,
|
|
2636
|
+
...ok ? {} : { message: registry.message ?? `${image}:${version} was not found on Docker Hub with amd64 and arm64 images.` }
|
|
2596
2637
|
};
|
|
2597
2638
|
} catch (error) {
|
|
2598
2639
|
return {
|
|
@@ -2607,6 +2648,33 @@ async function verifyDockerHubArtifact(image, version) {
|
|
|
2607
2648
|
};
|
|
2608
2649
|
}
|
|
2609
2650
|
}
|
|
2651
|
+
async function verifyGitHubTagArtifact(repository, version) {
|
|
2652
|
+
const url = `https://api.github.com/repos/${repository}/git/ref/tags/${encodeURIComponent(version)}`;
|
|
2653
|
+
try {
|
|
2654
|
+
const response = await fetchJsonForArtifact(url);
|
|
2655
|
+
return {
|
|
2656
|
+
id: `github-tag:${repository}:${version}`,
|
|
2657
|
+
kind: "github-tag",
|
|
2658
|
+
name: repository,
|
|
2659
|
+
version,
|
|
2660
|
+
url,
|
|
2661
|
+
ok: response.ok,
|
|
2662
|
+
status: response.status,
|
|
2663
|
+
...response.ok ? {} : { message: `GitHub tag ${repository}@${version} was not found.` }
|
|
2664
|
+
};
|
|
2665
|
+
} catch (error) {
|
|
2666
|
+
return {
|
|
2667
|
+
id: `github-tag:${repository}:${version}`,
|
|
2668
|
+
kind: "github-tag",
|
|
2669
|
+
name: repository,
|
|
2670
|
+
version,
|
|
2671
|
+
url,
|
|
2672
|
+
ok: false,
|
|
2673
|
+
status: null,
|
|
2674
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2675
|
+
};
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2610
2678
|
async function verifySimpleRegistryArtifact(input) {
|
|
2611
2679
|
try {
|
|
2612
2680
|
const response = await fetchJsonForArtifact(input.url);
|
|
@@ -2635,6 +2703,21 @@ async function verifySimpleRegistryArtifact(input) {
|
|
|
2635
2703
|
}
|
|
2636
2704
|
async function collectPublishedReleaseArtifactChecks(selectedVersions) {
|
|
2637
2705
|
const checks = [];
|
|
2706
|
+
const githubRepositories = {
|
|
2707
|
+
"@treeseed/sdk": "treeseed-ai/sdk",
|
|
2708
|
+
"@treeseed/ui": "treeseed-ai/ui",
|
|
2709
|
+
"@treeseed/core": "treeseed-ai/core",
|
|
2710
|
+
"@treeseed/admin": "treeseed-ai/admin",
|
|
2711
|
+
"@treeseed/cli": "treeseed-ai/cli",
|
|
2712
|
+
"@treeseed/agent": "treeseed-ai/agent",
|
|
2713
|
+
"@treeseed/api": "treeseed-ai/api",
|
|
2714
|
+
treedx: "treeseed-ai/treedx",
|
|
2715
|
+
"@treeseed/treedx": "treeseed-ai/treedx"
|
|
2716
|
+
};
|
|
2717
|
+
for (const [packageName, repository] of Object.entries(githubRepositories)) {
|
|
2718
|
+
const version = selectedVersions.get(packageName);
|
|
2719
|
+
if (version) checks.push(await verifyGitHubTagArtifact(repository, version));
|
|
2720
|
+
}
|
|
2638
2721
|
const npmPackages = ["@treeseed/sdk", "@treeseed/ui", "@treeseed/core", "@treeseed/admin", "@treeseed/cli", "@treeseed/agent"];
|
|
2639
2722
|
for (const packageName of npmPackages) {
|
|
2640
2723
|
const version = selectedVersions.get(packageName);
|