feat: expand ci artifact catalog

This commit is contained in:
Codex
2026-05-20 01:20:03 +00:00
parent e0d38d7172
commit 5bb44c9a30
11 changed files with 810 additions and 216 deletions
+170 -72
View File
@@ -1,8 +1,9 @@
import { randomUUID } from "node:crypto";
import { existsSync, readFileSync } from "node:fs";
import { posix as posixPath } from "node:path";
import { blockedCatalogArtifactIds, catalogSummary, findCiCatalogArtifact, loadCiCatalog, supportedSourceBuildArtifactIds, type CiCatalogArtifact, type CiSourceBuildCatalogArtifact, type CiUpstreamImageCatalogArtifact } from "./ci-catalog";
import { runCommand } from "./command";
import { type UniDeskConfig, type UniDeskMicroserviceConfig, repoRoot, rootPath } from "./config";
import { type UniDeskConfig, repoRoot, rootPath } from "./config";
import { ensureGithubSshIdentityForProvider, gitSshHttpConnectProxySource } from "./deploy-ssh-identity";
import { startJob } from "./jobs";
import { coreInternalFetch } from "./microservices";
@@ -29,14 +30,6 @@ const ciRuntimeImages = [
"alpine/git:2.45.2",
ciCodeQueueImage,
];
const publishUserServiceArtifactAllowedServiceIds = new Set([
"baidu-netdisk",
"code-queue-mgr",
"decision-center",
"frontend",
"oa-event-flow",
"project-manager",
]);
interface CiOptions {
repoUrl: string;
@@ -49,6 +42,8 @@ interface CiPublishBackendCoreOptions {
commit: string;
waitMs: number;
sourceHostPath: string;
dockerfile: string;
imageRepository: string;
dryRun: boolean;
}
@@ -59,6 +54,7 @@ interface CiPublishUserServiceArtifactOptions {
sourceHostPath: string;
serviceId: string;
dockerfile: string;
imageRepository: string;
dryRun: boolean;
}
@@ -130,6 +126,7 @@ interface ArtifactSummaryContext {
commit: string;
repoUrl: string;
dockerfile: string;
imageRepository: string;
}
function stringOption(args: string[], name: string): string | null {
@@ -188,6 +185,10 @@ function shellQuote(value: string): string {
return `'${value.replace(/'/gu, "'\\''")}'`;
}
function safePathToken(value: string): string {
return value.replace(/[^a-z0-9._-]/giu, "-").toLowerCase().replace(/^-+|-+$/gu, "").slice(0, 80) || "artifact";
}
function repoSshUrl(repoUrl: string): string {
if (repoUrl.startsWith("git@")) return repoUrl;
if (repoUrl.startsWith("https://github.com/")) {
@@ -197,6 +198,10 @@ function repoSshUrl(repoUrl: string): string {
return repoUrl;
}
function repoNeedsGithubSshIdentity(repoFetchUrl: string): boolean {
return repoFetchUrl.startsWith("git@github.com:");
}
function requireRepoRelativePath(path: string, label: string): string {
if (path.length === 0 || path.startsWith("/") || path.includes("\0") || path.split("/").includes("..")) {
throw new Error(`${label} must be a repo-relative path`);
@@ -206,35 +211,45 @@ function requireRepoRelativePath(path: string, label: string): string {
return normalized;
}
function requireSupportedUserService(config: UniDeskConfig, serviceId: string): UniDeskMicroserviceConfig {
if (serviceId === "backend-core") {
throw new Error("backend-core uses ci publish-backend-core; publish-user-service is for registered user services");
function resolveCatalogArtifact(serviceId: string): CiCatalogArtifact {
const artifact = findCiCatalogArtifact(serviceId);
if (artifact === null) {
const known = loadCiCatalog().artifacts.map((item) => item.serviceId).sort().join(", ");
throw new Error(`unknown CI artifact service: ${serviceId}. Known services: ${known}`);
}
const service = config.microservices.find((item) => item.id === serviceId);
if (service === undefined) throw new Error(`unknown user service: ${serviceId}`);
if (service.repository.artifactSource?.kind === "upstream-image") {
throw new Error(`ci publish-user-service does not build ${serviceId}: it is an upstream image consumer (${service.repository.artifactSource.imageRef}). Use the upstream-image digest/mirror governance path; do not add it to Dockerfile CI artifacts.`);
}
const isD601K3sService = service.providerId === d601ProviderId
&& service.development.providerId === d601ProviderId
&& service.deployment.mode === "k3sctl-managed";
const isMainServerDirectService = service.providerId === "main-server"
&& service.development.providerId === "main-server"
&& service.deployment.mode === "unidesk-direct";
const isMainServerInternalSidecar = service.providerId === "main-server"
&& service.development.providerId === "main-server"
&& service.deployment.mode === "internal-sidecar";
if (!isD601K3sService && !isMainServerDirectService && !isMainServerInternalSidecar) {
throw new Error(`ci publish-user-service supports only reviewed k3sctl-managed D601 services or main-server unidesk-direct services; ${serviceId} is ${service.providerId}/${service.deployment.mode}`);
}
return service;
return artifact;
}
function frontendArtifactTarget(repoOverride: string | null): { repoUrl: string; dockerfile: string } {
return {
repoUrl: repoOverride ?? "https://github.com/pikasTech/unidesk",
dockerfile: "src/components/frontend/Dockerfile",
function blockedArtifactResult(artifact: CiUpstreamImageCatalogArtifact | CiSourceBuildCatalogArtifact, commit: string, note: string): Record<string, unknown> {
const base = {
ok: false,
status: "blocked",
error: "blocked",
serviceId: artifact.serviceId,
commit,
reason: note,
catalogArtifact: artifact,
boundary: "CI catalog marks this service as blocked; it must not be treated as a source-build artifact producer",
};
return artifact.kind === "upstream-image"
? {
...base,
upstream: artifact.upstream,
next: [
`document the upstream image contract in CI.json for ${artifact.serviceId}`,
],
}
: {
...base,
next: [
`unblock ${artifact.serviceId} in CI.json before attempting source-build publication`,
],
};
}
function blockedReason(artifact: CiSourceBuildCatalogArtifact): string {
if (artifact.blockedReason === undefined) throw new Error(`${artifact.serviceId} is blocked in CI.json but has no blockedReason`);
return artifact.blockedReason;
}
function chunks(value: string, size: number): string[] {
@@ -621,6 +636,10 @@ spec:
value: ${JSON.stringify(options.repoUrl)}
- name: revision
value: ${JSON.stringify(options.commit)}
- name: dockerfile
value: ${JSON.stringify(options.dockerfile)}
- name: image-repository
value: ${JSON.stringify(options.imageRepository)}
- name: source-host-path
value: ${JSON.stringify(options.sourceHostPath)}
workspaces:
@@ -656,6 +675,8 @@ spec:
value: ${JSON.stringify(options.serviceId)}
- name: dockerfile
value: ${JSON.stringify(options.dockerfile)}
- name: image-repository
value: ${JSON.stringify(options.imageRepository)}
- name: source-host-path
value: ${JSON.stringify(options.sourceHostPath)}
workspaces:
@@ -674,18 +695,20 @@ function userServiceArtifactSourceHostPath(serviceId: string, commit: string): s
}
async function prepareBackendCoreArtifactSource(config: UniDeskConfig, options: CiPublishBackendCoreOptions): Promise<Record<string, unknown>> {
const sshIdentity = await ensureGithubSshIdentityForProvider(config, d601ProviderId);
if (!sshIdentity.ok) throw new Error(sshIdentity.detail);
const proxyPython = gitSshHttpConnectProxySource();
const sourceRoot = "/home/ubuntu/.unidesk/ci/backend-core-artifacts";
const sourceHostPath = options.sourceHostPath;
const repoCache = "/home/ubuntu/.unidesk/ci/git/unidesk.git";
const repoFetchUrl = repoSshUrl(options.repoUrl);
const sshIdentity = repoNeedsGithubSshIdentity(repoFetchUrl) ? await ensureGithubSshIdentityForProvider(config, d601ProviderId) : null;
if (sshIdentity !== null && !sshIdentity.ok) throw new Error(sshIdentity.detail);
const proxyPython = gitSshHttpConnectProxySource();
const dockerfile = requireRepoRelativePath(options.dockerfile, "CI.json.artifacts.backend-core.source.dockerfile");
const script = [
"set -euo pipefail",
`commit=${shellQuote(options.commit)}`,
`repo_url=${shellQuote(options.repoUrl)}`,
`repo_fetch_url=${shellQuote(repoFetchUrl)}`,
`dockerfile=${shellQuote(dockerfile)}`,
`source_root=${shellQuote(sourceRoot)}`,
`source_dir=${shellQuote(sourceHostPath)}`,
`repo_cache=${shellQuote(repoCache)}`,
@@ -708,7 +731,7 @@ async function prepareBackendCoreArtifactSource(config: UniDeskConfig, options:
"git -C \"$repo_cache\" fetch --no-tags origin \"$commit\" || git -C \"$repo_cache\" fetch --no-tags origin '+refs/heads/*:refs/remotes/origin/*'",
"resolved=$(git -C \"$repo_cache\" rev-parse --verify \"$commit^{commit}\")",
"test \"$resolved\" = \"$commit\" || { echo \"backend_core_artifact_resolved_commit_mismatch=$resolved expected=$commit\" >&2; exit 1; }",
"git -C \"$repo_cache\" cat-file -e \"$commit:src/components/backend-core/Dockerfile\"",
"git -C \"$repo_cache\" cat-file -e \"$commit:$dockerfile\"",
"git -C \"$repo_cache\" cat-file -e \"$commit:src/components/backend-core/src\"",
"tmp_dir=\"$source_root/.tmp-$commit-$$\"",
"rm -rf \"$tmp_dir\"",
@@ -718,7 +741,7 @@ async function prepareBackendCoreArtifactSource(config: UniDeskConfig, options:
"printf '%s\\n' \"$repo_url\" > \"$tmp_dir/.unidesk-source-repo\"",
"rm -rf \"$source_dir\"",
"mv \"$tmp_dir\" \"$source_dir\"",
"test -f \"$source_dir/src/components/backend-core/Dockerfile\"",
"test -f \"$source_dir/$dockerfile\"",
"test -d \"$source_dir/src/components/backend-core/src\"",
"echo backend_core_artifact_source_host_path=$source_dir",
].join("\n");
@@ -726,13 +749,14 @@ async function prepareBackendCoreArtifactSource(config: UniDeskConfig, options:
if (!result.ok) throw new Error(`failed to prepare backend-core source on D601: ${result.stderr || result.stdout || JSON.stringify(result.raw)}`);
return {
ok: true,
mode: "d601-host-github-ssh-export",
mode: repoNeedsGithubSshIdentity(repoFetchUrl) ? "d601-host-git-ssh-export" : "d601-host-git-https-export",
providerId: d601ProviderId,
repoUrl: options.repoUrl,
repoFetchUrl,
commit: options.commit,
sourceHostPath,
identity: {
dockerfile,
identity: sshIdentity === null ? null : {
fingerprint: sshIdentity.fingerprint,
seededFromLocal: sshIdentity.seededFromLocal,
},
@@ -741,14 +765,13 @@ async function prepareBackendCoreArtifactSource(config: UniDeskConfig, options:
}
async function prepareUserServiceArtifactSource(config: UniDeskConfig, options: CiPublishUserServiceArtifactOptions): Promise<Record<string, unknown>> {
const sshIdentity = await ensureGithubSshIdentityForProvider(config, d601ProviderId);
if (!sshIdentity.ok) throw new Error(sshIdentity.detail);
const proxyPython = gitSshHttpConnectProxySource();
const sourceRoot = `/home/ubuntu/.unidesk/ci/user-service-artifacts/${options.serviceId}`;
const sourceHostPath = options.sourceHostPath;
const repoCache = "/home/ubuntu/.unidesk/ci/git/unidesk.git";
const repoCache = `/home/ubuntu/.unidesk/ci/git/${safePathToken(options.serviceId)}.git`;
const repoFetchUrl = repoSshUrl(options.repoUrl);
const dockerfileDir = posixPath.dirname(options.dockerfile);
const sshIdentity = repoNeedsGithubSshIdentity(repoFetchUrl) ? await ensureGithubSshIdentityForProvider(config, d601ProviderId) : null;
if (sshIdentity !== null && !sshIdentity.ok) throw new Error(sshIdentity.detail);
const proxyPython = gitSshHttpConnectProxySource();
const script = [
"set -euo pipefail",
`service_id=${shellQuote(options.serviceId)}`,
@@ -756,7 +779,6 @@ async function prepareUserServiceArtifactSource(config: UniDeskConfig, options:
`repo_url=${shellQuote(options.repoUrl)}`,
`repo_fetch_url=${shellQuote(repoFetchUrl)}`,
`dockerfile=${shellQuote(options.dockerfile)}`,
`dockerfile_dir=${shellQuote(dockerfileDir)}`,
`source_root=${shellQuote(sourceRoot)}`,
`source_dir=${shellQuote(sourceHostPath)}`,
`repo_cache=${shellQuote(repoCache)}`,
@@ -781,7 +803,6 @@ async function prepareUserServiceArtifactSource(config: UniDeskConfig, options:
"resolved=$(git -C \"$repo_cache\" rev-parse --verify \"$commit^{commit}\")",
"test \"$resolved\" = \"$commit\" || { echo \"user_service_artifact_resolved_commit_mismatch=$resolved expected=$commit\" >&2; exit 1; }",
"git -C \"$repo_cache\" cat-file -e \"$commit:$dockerfile\"",
"git -C \"$repo_cache\" cat-file -e \"$commit:$dockerfile_dir/src\"",
"tmp_dir=\"$source_root/.tmp-$commit-$$\"",
"rm -rf \"$tmp_dir\"",
"mkdir -p \"$tmp_dir\"",
@@ -793,14 +814,13 @@ async function prepareUserServiceArtifactSource(config: UniDeskConfig, options:
"rm -rf \"$source_dir\"",
"mv \"$tmp_dir\" \"$source_dir\"",
"test -f \"$source_dir/$dockerfile\"",
"test -d \"$source_dir/$dockerfile_dir/src\"",
"echo user_service_artifact_source_host_path=$source_dir",
].join("\n");
const result = await runRemoteBackground(`prepare-${options.serviceId}-source`, script, 300_000);
if (!result.ok) throw new Error(`failed to prepare ${options.serviceId} source on D601: ${result.stderr || result.stdout || JSON.stringify(result.raw)}`);
return {
ok: true,
mode: "d601-host-github-ssh-export",
mode: repoNeedsGithubSshIdentity(repoFetchUrl) ? "d601-host-git-ssh-export" : "d601-host-git-https-export",
providerId: d601ProviderId,
repoUrl: options.repoUrl,
repoFetchUrl,
@@ -808,7 +828,7 @@ async function prepareUserServiceArtifactSource(config: UniDeskConfig, options:
serviceId: options.serviceId,
dockerfile: options.dockerfile,
sourceHostPath,
identity: {
identity: sshIdentity === null ? null : {
fingerprint: sshIdentity.fingerprint,
seededFromLocal: sshIdentity.seededFromLocal,
},
@@ -894,7 +914,7 @@ function pipelineRunWaitSucceeded(wait: DispatchResult | null, condition: Pipeli
function artifactSummaryDefaults(context: ArtifactSummaryContext): ArtifactSummary {
const registry = "127.0.0.1:5000";
const repository = `${registry}/unidesk/${context.serviceId}`;
const repository = `${registry}/${context.imageRepository}`;
return {
serviceId: context.serviceId,
sourceCommit: context.commit,
@@ -917,7 +937,7 @@ function artifactSummaryField(fields: Map<string, string>, suffix: string): stri
function parseArtifactSummaryFromFields(fields: Map<string, string>, context: ArtifactSummaryContext): ArtifactSummary {
const planned = artifactSummaryDefaults(context);
const registry = artifactSummaryField(fields, "registry") ?? planned.registry;
const repository = artifactSummaryField(fields, "repository") ?? `${registry}/unidesk/${context.serviceId}`;
const repository = artifactSummaryField(fields, "repository") ?? planned.repository;
const tag = artifactSummaryField(fields, "tag") ?? planned.tag;
const imageRef = artifactSummaryField(fields, "image") ?? (repository.length > 0 && tag.length > 0 ? `${repository}:${tag}` : planned.imageRef);
const digest = artifactSummaryField(fields, "digest");
@@ -1075,9 +1095,10 @@ async function run(options: CiOptions): Promise<Record<string, unknown>> {
async function publishBackendCoreArtifact(config: UniDeskConfig, options: CiPublishBackendCoreOptions): Promise<Record<string, unknown>> {
const summaryContext: ArtifactSummaryContext = {
serviceId: "backend-core",
dockerfile: "src/components/backend-core/Dockerfile",
commit: options.commit,
repoUrl: options.repoUrl,
dockerfile: options.dockerfile,
imageRepository: options.imageRepository,
};
const plannedArtifact = artifactSummaryDefaults(summaryContext);
if (options.dryRun) {
@@ -1096,7 +1117,8 @@ async function publishBackendCoreArtifact(config: UniDeskConfig, options: CiPubl
repoUrl: options.repoUrl,
repoFetchUrl: repoSshUrl(options.repoUrl),
commit: options.commit,
dockerfile: "src/components/backend-core/Dockerfile",
dockerfile: options.dockerfile,
imageRepository: options.imageRepository,
sourceHostPath: options.sourceHostPath,
},
artifact: plannedArtifact.imageRef,
@@ -1148,6 +1170,7 @@ async function publishUserServiceArtifact(config: UniDeskConfig, options: CiPubl
dockerfile: options.dockerfile,
commit: options.commit,
repoUrl: options.repoUrl,
imageRepository: options.imageRepository,
};
const plannedArtifact = artifactSummaryDefaults(summaryContext);
if (options.dryRun) {
@@ -1169,6 +1192,7 @@ async function publishUserServiceArtifact(config: UniDeskConfig, options: CiPubl
commit: options.commit,
serviceId: options.serviceId,
dockerfile: options.dockerfile,
imageRepository: options.imageRepository,
sourceHostPath: options.sourceHostPath,
},
artifact: plannedArtifact.imageRef,
@@ -1461,7 +1485,32 @@ async function logs(name: string): Promise<Record<string, unknown>> {
};
}
function catalogArtifactDescriptor(artifact: CiCatalogArtifact): Record<string, unknown> {
if (artifact.kind === "source-build") {
return {
serviceId: artifact.serviceId,
kind: artifact.kind,
status: artifact.status,
producer: artifact.producer,
source: artifact.source,
image: artifact.image,
...(artifact.notes === undefined ? {} : { notes: artifact.notes }),
...(artifact.blockedReason === undefined ? {} : { blockedReason: artifact.blockedReason }),
};
}
return {
serviceId: artifact.serviceId,
kind: artifact.kind,
status: artifact.status,
producer: artifact.producer,
upstream: artifact.upstream,
blockedReason: artifact.blockedReason,
...(artifact.notes === undefined ? {} : { notes: artifact.notes }),
};
}
export function ciHelp(): Record<string, unknown> {
const catalog = loadCiCatalog();
return {
command: "ci install|status|run|publish-backend-core|publish-user-service|run-dev-e2e|logs",
description: "Manage the D601 k3s Tekton CI gate. CI may publish commit-pinned image artifacts, but it intentionally does not deploy CD.",
@@ -1492,9 +1541,13 @@ export function ciHelp(): Record<string, unknown> {
userServiceArtifact: {
producer: "D601 CI",
command: "bun scripts/cli.ts ci publish-user-service --service <service-id> --commit <full-sha>",
initiallySupportedServices: ["baidu-netdisk", "decision-center", "frontend"],
supportedServices: supportedSourceBuildArtifactIds().filter((serviceId) => serviceId !== "backend-core"),
blockedServices: blockedCatalogArtifactIds(),
registry: "127.0.0.1:5000/unidesk/<service-id>:<commit>",
outputFields: ["serviceId", "sourceCommit", "sourceRepo", "dockerfile", "imageRef", "tag", "digest", "digestRef"],
summaryContract: catalog.summaryContract,
catalogSummary: catalogSummary(),
catalog: catalog.artifacts.map(catalogArtifactDescriptor),
boundary: "artifact producer only; no prod deploy and no production namespace mutation",
frontendNext: [
"bun scripts/cli.ts deploy apply --env dev --service frontend",
@@ -1528,37 +1581,82 @@ export async function runCiCommand(config: UniDeskConfig, args: string[]): Promi
return run({ repoUrl, revision, waitMs });
}
if (action === "publish-backend-core") {
const repoUrl = stringOption(args, "--repo") ?? stringOption(args, "--repo-url") ?? "https://github.com/pikasTech/unidesk";
if (stringOption(args, "--repo") !== null || stringOption(args, "--repo-url") !== null) {
throw new Error("ci publish-backend-core reads source repo from CI.json; edit CI.json instead of using --repo");
}
const commit = requireFullCommit(stringOption(args, "--commit") ?? stringOption(args, "--revision"));
const waitMs = numberOption(args, "--wait-ms", 0);
const dryRun = boolFlag(args, "--dry-run");
return publishBackendCoreArtifact(config, { repoUrl, commit, waitMs, sourceHostPath: backendCoreArtifactSourceHostPath(commit), dryRun });
const artifact = resolveCatalogArtifact("backend-core");
if (artifact.kind !== "source-build") throw new Error("backend-core must be modeled as a source-build artifact in CI.json");
if (artifact.status === "blocked") return blockedArtifactResult(artifact, commit, blockedReason(artifact));
return publishBackendCoreArtifact(config, {
repoUrl: artifact.source.repo,
commit,
waitMs,
sourceHostPath: backendCoreArtifactSourceHostPath(commit),
dockerfile: artifact.source.dockerfile,
imageRepository: artifact.image.repository,
dryRun,
});
}
if (action === "publish-user-service") {
const serviceId = requireServiceId(stringOption(args, "--service") ?? stringOption(args, "--service-id"));
const repoOverride = stringOption(args, "--repo") ?? stringOption(args, "--repo-url");
const target = serviceId === "frontend"
? frontendArtifactTarget(repoOverride)
: (() => {
const service = requireSupportedUserService(config, serviceId);
return {
repoUrl: repoOverride ?? service.repository.url,
dockerfile: service.repository.dockerfile,
};
})();
const commit = requireFullCommit(stringOption(args, "--commit") ?? stringOption(args, "--revision"));
const waitMs = numberOption(args, "--wait-ms", 0);
const dryRun = boolFlag(args, "--dry-run");
const dockerfile = requireRepoRelativePath(target.dockerfile, serviceId === "frontend" ? "frontend.dockerfile" : `microservices.${serviceId}.repository.dockerfile`);
if (!publishUserServiceArtifactAllowedServiceIds.has(serviceId)) {
throw new Error(`ci publish-user-service currently allows only ${Array.from(publishUserServiceArtifactAllowedServiceIds).join(", ")} until each user-service Dockerfile contract is reviewed`);
if (stringOption(args, "--repo") !== null || stringOption(args, "--repo-url") !== null) {
throw new Error("ci publish-user-service reads source repo from CI.json; edit CI.json instead of using --repo");
}
const artifact = resolveCatalogArtifact(serviceId);
if (artifact.kind === "source-build" && artifact.serviceId === "backend-core") {
throw new Error("backend-core uses ci publish-backend-core; publish-user-service is for registered user services");
}
if (artifact.kind === "upstream-image") {
return blockedArtifactResult(artifact, commit, artifact.blockedReason);
}
if (artifact.status === "blocked") {
return blockedArtifactResult(artifact, commit, blockedReason(artifact));
}
const repoUrl = artifact.source.repo;
const dockerfile = requireRepoRelativePath(artifact.source.dockerfile, `CI.json.artifacts.${serviceId}.source.dockerfile`);
const configService = config.microservices.find((item) => item.id === serviceId);
if (configService !== undefined) {
const isD601K3sService = configService.providerId === d601ProviderId
&& configService.development.providerId === d601ProviderId
&& configService.deployment.mode === "k3sctl-managed";
const isD601DirectService = configService.providerId === d601ProviderId
&& configService.development.providerId === d601ProviderId
&& configService.deployment.mode === "unidesk-direct";
const isMainServerDirectService = configService.providerId === "main-server"
&& configService.development.providerId === "main-server"
&& configService.deployment.mode === "unidesk-direct";
const isMainServerInternalSidecar = configService.providerId === "main-server"
&& configService.development.providerId === "main-server"
&& configService.deployment.mode === "internal-sidecar";
if (!isD601K3sService && !isD601DirectService && !isMainServerDirectService && !isMainServerInternalSidecar) {
return {
ok: false,
status: "blocked",
error: "blocked",
serviceId,
commit,
reason: `config.json marks ${serviceId} as ${configService.providerId}/${configService.deployment.mode}, which is outside the reviewed CI artifact producer boundary`,
catalogArtifact: artifact,
configService: {
providerId: configService.providerId,
deploymentMode: configService.deployment.mode,
},
};
}
}
return publishUserServiceArtifact(config, {
repoUrl: target.repoUrl,
repoUrl,
commit,
waitMs,
serviceId,
dockerfile,
imageRepository: artifact.image.repository,
sourceHostPath: userServiceArtifactSourceHostPath(serviceId, commit),
dryRun,
});