feat(auth-broker): register dry-run service surface
This commit is contained in:
+45
@@ -765,6 +765,51 @@
|
||||
"mode": "internal-sidecar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "auth-broker",
|
||||
"name": "Auth Broker",
|
||||
"providerId": "main-server",
|
||||
"description": "Auth Broker 是主 server 私有鉴权代理 skeleton,为 Code Queue PR preflight 和 GitHub issue/PR dry-run 提供 broker-held credential ref 语义;当前只登记 source/contract/profile,不启用 live secret 或公网入口。",
|
||||
"repository": {
|
||||
"url": "https://github.com/pikasTech/unidesk",
|
||||
"commitId": "local",
|
||||
"dockerfile": "src/components/microservices/auth-broker/Dockerfile",
|
||||
"composeFile": "docker-compose.yml",
|
||||
"composeService": "auth-broker",
|
||||
"containerName": "auth-broker-backend"
|
||||
},
|
||||
"backend": {
|
||||
"nodeBaseUrl": "http://auth-broker:4291",
|
||||
"nodeBindHost": "auth-broker",
|
||||
"nodePort": 4291,
|
||||
"proxyMode": "unidesk-direct",
|
||||
"frontendOnly": true,
|
||||
"public": false,
|
||||
"allowedMethods": [
|
||||
"GET",
|
||||
"HEAD",
|
||||
"POST"
|
||||
],
|
||||
"allowedPathPrefixes": [
|
||||
"/health",
|
||||
"/v1/github/"
|
||||
],
|
||||
"healthPath": "/health",
|
||||
"timeoutMs": 10000
|
||||
},
|
||||
"development": {
|
||||
"providerId": "main-server",
|
||||
"sshPassthrough": false,
|
||||
"worktreePath": "/root/unidesk/src/components/microservices/auth-broker"
|
||||
},
|
||||
"frontend": {
|
||||
"route": "/apps/auth-broker",
|
||||
"integrated": false
|
||||
},
|
||||
"deployment": {
|
||||
"mode": "internal-sidecar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "mdtodo",
|
||||
"name": "MDTODO",
|
||||
|
||||
+10
@@ -68,6 +68,11 @@
|
||||
"repo": "https://github.com/pikasTech/unidesk",
|
||||
"commitId": "fee1b1b710151d827749cc4b0662b1560cbe1fd6"
|
||||
},
|
||||
{
|
||||
"id": "auth-broker",
|
||||
"repo": "https://github.com/pikasTech/unidesk",
|
||||
"commitId": "a6144ae71069d1467ccf452f53674b386978fc1d"
|
||||
},
|
||||
{
|
||||
"id": "mdtodo",
|
||||
"repo": "https://github.com/pikasTech/unidesk",
|
||||
@@ -224,6 +229,11 @@
|
||||
"repo": "https://github.com/pikasTech/unidesk",
|
||||
"commitId": "22b02e7ce98a32647f8c3962dbf90aafabd53ff0"
|
||||
},
|
||||
{
|
||||
"id": "auth-broker",
|
||||
"repo": "https://github.com/pikasTech/unidesk",
|
||||
"commitId": "a6144ae71069d1467ccf452f53674b386978fc1d"
|
||||
},
|
||||
{
|
||||
"id": "code-queue",
|
||||
"repo": "https://github.com/pikasTech/unidesk",
|
||||
|
||||
@@ -165,6 +165,39 @@ services:
|
||||
timeout: 3s
|
||||
retries: 20
|
||||
|
||||
auth-broker:
|
||||
image: auth-broker
|
||||
profiles:
|
||||
- auth-broker
|
||||
build:
|
||||
context: .
|
||||
dockerfile: src/components/microservices/auth-broker/Dockerfile
|
||||
container_name: auth-broker-backend
|
||||
restart: "no"
|
||||
expose:
|
||||
- "4291"
|
||||
environment:
|
||||
HOST: "0.0.0.0"
|
||||
PORT: "4291"
|
||||
AUTH_BROKER_GITHUB_CONFIGURED: "${UNIDESK_AUTH_BROKER_GITHUB_CONFIGURED:-false}"
|
||||
AUTH_BROKER_GITHUB_CREDENTIAL_REF: "${UNIDESK_AUTH_BROKER_GITHUB_CREDENTIAL_REF:-github:unidesk-dev}"
|
||||
AUTH_BROKER_ALLOWED_REPOS: "${UNIDESK_AUTH_BROKER_ALLOWED_REPOS:-pikasTech/unidesk}"
|
||||
AUTH_BROKER_AUDIT_LOG: "/var/log/unidesk/${UNIDESK_LOG_DAY}/${UNIDESK_LOG_PREFIX}_auth-broker.jsonl"
|
||||
UNIDESK_DEPLOY_REF: "${UNIDESK_AUTH_BROKER_DEPLOY_REF:-deploy.json#environments.prod.services.auth-broker}"
|
||||
UNIDESK_DEPLOY_SERVICE_ID: "${UNIDESK_AUTH_BROKER_DEPLOY_SERVICE_ID:-auth-broker}"
|
||||
UNIDESK_DEPLOY_REPO: "${UNIDESK_AUTH_BROKER_DEPLOY_REPO:-}"
|
||||
UNIDESK_DEPLOY_COMMIT: "${UNIDESK_AUTH_BROKER_DEPLOY_COMMIT:-}"
|
||||
UNIDESK_DEPLOY_REQUESTED_COMMIT: "${UNIDESK_AUTH_BROKER_DEPLOY_REQUESTED_COMMIT:-}"
|
||||
LOG_FILE: "/var/log/unidesk/${UNIDESK_LOG_DAY}/${UNIDESK_LOG_PREFIX}_auth-broker.jsonl"
|
||||
UNIDESK_LOG_RETENTION_BYTES: "${UNIDESK_LOG_RETENTION_BYTES:-1GiB}"
|
||||
volumes:
|
||||
- ${UNIDESK_LOG_DIR}:/var/log/unidesk
|
||||
healthcheck:
|
||||
test: ["CMD", "auth-broker", "--healthcheck"]
|
||||
interval: 30s
|
||||
timeout: 3s
|
||||
retries: 1
|
||||
|
||||
todo-note:
|
||||
image: todo-note
|
||||
build:
|
||||
|
||||
@@ -36,10 +36,25 @@ The first skeleton lives at:
|
||||
- `src/components/microservices/auth-broker/Cargo.toml`
|
||||
- `src/components/microservices/auth-broker/src/main.rs`
|
||||
- `src/components/microservices/auth-broker/Dockerfile`
|
||||
- `config.json` microservice id `auth-broker`
|
||||
- `deploy.json` prod/dev desired-state entries for `auth-broker`
|
||||
- `docker-compose.yml` service `auth-broker` behind Compose profile `auth-broker`
|
||||
- `scripts/src/auth-broker.ts`
|
||||
|
||||
The skeleton intentionally does not read `GH_TOKEN` or `GITHUB_TOKEN`. It uses only redacted readiness configuration such as `AUTH_BROKER_GITHUB_CONFIGURED`, `AUTH_BROKER_GITHUB_CREDENTIAL_REF`, `AUTH_BROKER_ALLOWED_REPOS` and optional `AUTH_BROKER_AUDIT_LOG`. Real secret mounting is outside this contract.
|
||||
|
||||
## P1 Source Registration
|
||||
|
||||
P1 keeps Auth Broker in source/contract/dry-run only:
|
||||
|
||||
- `config.json` registers stable microservice id `auth-broker` on `main-server`, private backend `http://auth-broker:4291`, health path `/health`, and allowed proxy prefixes `/health` plus `/v1/github/`.
|
||||
- `docker-compose.yml` defines service `auth-broker` with `profiles: ["auth-broker"]`, `restart: "no"`, no public `ports`, and redacted env names only. Default `server start` does not select this profile, so this source registration must not change current production runtime.
|
||||
- `deploy.json` includes prod and dev desired-state entries so `deploy plan --env prod|dev --service auth-broker` has a stable identity. Live apply is supervisor-gated until credential mounting and private exposure are separately reviewed.
|
||||
- `bun scripts/cli.ts auth-broker contract|health --dry-run|credential-request --dry-run|pr-preflight --dry-run` reports `serviceRegistration.config`, `serviceRegistration.compose`, `serviceRegistration.deploy`, and `serviceRegistration.runtimeCredentialRef` using presence/ref fields only.
|
||||
- Runtime credential readiness is expressed by `UNIDESK_AUTH_BROKER_GITHUB_CONFIGURED` / `AUTH_BROKER_GITHUB_CONFIGURED` and `UNIDESK_AUTH_BROKER_GITHUB_CREDENTIAL_REF` / `AUTH_BROKER_GITHUB_CREDENTIAL_REF` presence. The CLI prints only the source key and a sanitized `github:<ref>` style preview, never a token or raw credential value.
|
||||
|
||||
P1 still does not start Auth Broker, mount real secrets, deploy to prod/dev, restart backend-core/provider-gateway/Code Queue, or proxy registry/deploy credentials.
|
||||
|
||||
### `GET /health`
|
||||
|
||||
只返回服务状态和 redacted capability,不返回 secret 值。
|
||||
|
||||
@@ -15,6 +15,9 @@ const doc = readFileSync(docPath, "utf8");
|
||||
const rustMainPath = "src/components/microservices/auth-broker/src/main.rs";
|
||||
const rustCargoPath = "src/components/microservices/auth-broker/Cargo.toml";
|
||||
const cliAdapterPath = "scripts/src/auth-broker.ts";
|
||||
const configPath = "config.json";
|
||||
const deployPath = "deploy.json";
|
||||
const composePath = "docker-compose.yml";
|
||||
|
||||
function assertCondition(condition: unknown, message: string, detail: unknown = {}): void {
|
||||
if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`);
|
||||
@@ -30,10 +33,18 @@ function runCli(args: string[], env: Record<string, string | undefined> = {}): P
|
||||
delete childEnv.GITHUB_TOKEN;
|
||||
delete childEnv.UNIDESK_AUTH_BROKER_URL;
|
||||
delete childEnv.AUTH_BROKER_URL;
|
||||
delete childEnv.UNIDESK_AUTH_BROKER_GITHUB_CONFIGURED;
|
||||
delete childEnv.AUTH_BROKER_GITHUB_CONFIGURED;
|
||||
delete childEnv.UNIDESK_AUTH_BROKER_GITHUB_CREDENTIAL_REF;
|
||||
delete childEnv.AUTH_BROKER_GITHUB_CREDENTIAL_REF;
|
||||
if (env.GH_TOKEN !== undefined) childEnv.GH_TOKEN = env.GH_TOKEN;
|
||||
if (env.GITHUB_TOKEN !== undefined) childEnv.GITHUB_TOKEN = env.GITHUB_TOKEN;
|
||||
if (env.UNIDESK_AUTH_BROKER_URL !== undefined) childEnv.UNIDESK_AUTH_BROKER_URL = env.UNIDESK_AUTH_BROKER_URL;
|
||||
if (env.AUTH_BROKER_URL !== undefined) childEnv.AUTH_BROKER_URL = env.AUTH_BROKER_URL;
|
||||
if (env.UNIDESK_AUTH_BROKER_GITHUB_CONFIGURED !== undefined) childEnv.UNIDESK_AUTH_BROKER_GITHUB_CONFIGURED = env.UNIDESK_AUTH_BROKER_GITHUB_CONFIGURED;
|
||||
if (env.AUTH_BROKER_GITHUB_CONFIGURED !== undefined) childEnv.AUTH_BROKER_GITHUB_CONFIGURED = env.AUTH_BROKER_GITHUB_CONFIGURED;
|
||||
if (env.UNIDESK_AUTH_BROKER_GITHUB_CREDENTIAL_REF !== undefined) childEnv.UNIDESK_AUTH_BROKER_GITHUB_CREDENTIAL_REF = env.UNIDESK_AUTH_BROKER_GITHUB_CREDENTIAL_REF;
|
||||
if (env.AUTH_BROKER_GITHUB_CREDENTIAL_REF !== undefined) childEnv.AUTH_BROKER_GITHUB_CREDENTIAL_REF = env.AUTH_BROKER_GITHUB_CREDENTIAL_REF;
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn("bun", ["scripts/cli.ts", ...args], {
|
||||
cwd: process.cwd(),
|
||||
@@ -62,6 +73,47 @@ function dataOf(response: Record<string, unknown>): Record<string, unknown> {
|
||||
return response.data as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function asRecord(value: unknown, message: string): Record<string, unknown> {
|
||||
assertCondition(typeof value === "object" && value !== null && !Array.isArray(value), message, value);
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function assertServiceRegistration(value: unknown): void {
|
||||
const registration = asRecord(value, "serviceRegistration must be an object");
|
||||
const config = asRecord(registration.config, "serviceRegistration.config must be an object");
|
||||
assertCondition(config.ok === true, "auth-broker should be registered in config.json", config);
|
||||
assertCondition(config.providerId === "main-server", "auth-broker config provider should be main-server", config);
|
||||
assertCondition(config.composeService === "auth-broker", "auth-broker compose service should be stable", config);
|
||||
assertCondition(config.containerName === "auth-broker-backend", "auth-broker container name should be stable", config);
|
||||
assertCondition(config.public === false, "auth-broker must not be public", config);
|
||||
const compose = asRecord(registration.compose, "serviceRegistration.compose must be an object");
|
||||
assertCondition(compose.servicePresent === true, "auth-broker compose service should exist", compose);
|
||||
assertCondition(compose.profileGated === true, "auth-broker compose service should require auth-broker profile", compose);
|
||||
assertCondition(compose.publicPortPublished === false, "auth-broker compose service must not publish a public port", compose);
|
||||
assertCondition(compose.mutatesDefaultRuntime === false, "auth-broker compose registration must not mutate default runtime", compose);
|
||||
const deploy = asRecord(registration.deploy, "serviceRegistration.deploy must be an object");
|
||||
assertCondition(deploy.ok === true, "auth-broker should be registered in deploy.json prod and dev", deploy);
|
||||
const prod = asRecord(deploy.prod, "serviceRegistration.deploy.prod must be an object");
|
||||
const dev = asRecord(deploy.dev, "serviceRegistration.deploy.dev must be an object");
|
||||
assertCondition(prod.present === true && dev.present === true, "deploy.json should include auth-broker in prod and dev", deploy);
|
||||
const runtimeCredentialRef = asRecord(registration.runtimeCredentialRef, "runtimeCredentialRef must be an object");
|
||||
assertCondition(runtimeCredentialRef.valuesRead === false && runtimeCredentialRef.valuesPrinted === false, "runtime credential ref must be presence-only", runtimeCredentialRef);
|
||||
}
|
||||
|
||||
function extractComposeServiceBlock(composeText: string, serviceName: string): string {
|
||||
const lines = composeText.split("\n");
|
||||
const startLine = lines.findIndex((line) => line === ` ${serviceName}:`);
|
||||
if (startLine < 0) return "";
|
||||
let endLine = lines.length;
|
||||
for (let index = startLine + 1; index < lines.length; index += 1) {
|
||||
if (/^ [A-Za-z0-9][A-Za-z0-9_-]*:$/u.test(lines[index] ?? "")) {
|
||||
endLine = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return lines.slice(startLine, endLine).join("\n");
|
||||
}
|
||||
|
||||
const requiredOperations = [
|
||||
"github.auth.status",
|
||||
"github.issue.list",
|
||||
@@ -155,11 +207,15 @@ async function main(): Promise<void> {
|
||||
for (const heading of ["## Existing Paths", "## API", "## Permission Boundary", "## Audit Fields", "## Failure Semantics", "## D601 Dev Acceptance"]) {
|
||||
assertDocContains(heading);
|
||||
}
|
||||
assertDocContains("## P1 Source Registration");
|
||||
for (const path of [
|
||||
"scripts/src/gh.ts",
|
||||
"scripts/src/auth-broker.ts",
|
||||
"src/components/microservices/auth-broker/Cargo.toml",
|
||||
"src/components/microservices/auth-broker/src/main.rs",
|
||||
"config.json",
|
||||
"deploy.json",
|
||||
"docker-compose.yml",
|
||||
"scripts/code-queue-pr-preflight-example.ts",
|
||||
"src/components/microservices/code-queue/src/runtime-preflight.ts",
|
||||
"scripts/src/code-queue.ts",
|
||||
@@ -183,18 +239,30 @@ async function main(): Promise<void> {
|
||||
assertCondition(samplePreflightResponse.prCapabilityContract.preflightMergesPr === false, "P0 preflight must not merge PRs", samplePreflightResponse.prCapabilityContract);
|
||||
walk(samplePreflightResponse);
|
||||
|
||||
for (const path of [rustCargoPath, rustMainPath, cliAdapterPath]) {
|
||||
for (const path of [rustCargoPath, rustMainPath, cliAdapterPath, configPath, deployPath, composePath]) {
|
||||
assertCondition(existsSync(path), `required auth broker implementation file is missing: ${path}`);
|
||||
}
|
||||
const rustMain = readFileSync(rustMainPath, "utf8");
|
||||
const cliAdapter = readFileSync(cliAdapterPath, "utf8");
|
||||
const config = JSON.parse(readFileSync(configPath, "utf8")) as { microservices?: Array<Record<string, unknown>> };
|
||||
const deploy = JSON.parse(readFileSync(deployPath, "utf8")) as { environments?: Record<string, { services?: Array<Record<string, unknown>> }> };
|
||||
const composeText = readFileSync(composePath, "utf8");
|
||||
const authBrokerComposeBlock = extractComposeServiceBlock(composeText, "auth-broker");
|
||||
assertCondition(rustMain.includes("GET") && rustMain.includes("/health"), "Rust skeleton should expose GET /health", rustMainPath);
|
||||
assertCondition(rustMain.includes("/v1/github/gh"), "Rust skeleton should expose credential-request endpoint", rustMainPath);
|
||||
assertCondition(rustMain.includes("/v1/github/pr-preflight"), "Rust skeleton should expose pr-preflight endpoint", rustMainPath);
|
||||
assertCondition(rustMain.includes("--healthcheck"), "Rust skeleton should expose a process healthcheck mode", rustMainPath);
|
||||
assertCondition(rustMain.includes("credential_value_printed: false"), "audit event must force credentialValuePrinted=false", rustMainPath);
|
||||
assertCondition(!rustMain.includes("GH_TOKEN") && !rustMain.includes("GITHUB_TOKEN"), "Rust skeleton must not read runner token env keys", rustMainPath);
|
||||
assertCondition(cliAdapter.includes("valuesRead: false") && cliAdapter.includes("valuesPrinted: false"), "CLI adapter must declare secret values unread/unprinted", cliAdapterPath);
|
||||
assertCondition(cliAdapter.includes("broker-needed") && cliAdapter.includes("auth-missing"), "CLI adapter must expose broker-needed/auth-missing shape", cliAdapterPath);
|
||||
const configService = config.microservices?.find((item) => item.id === "auth-broker");
|
||||
assertCondition(configService !== undefined, "config.json should register auth-broker microservice", configPath);
|
||||
assertCondition(asRecord(configService?.backend, "auth-broker backend should be object").public === false, "auth-broker backend must be private", configService);
|
||||
assertCondition(deploy.environments?.prod?.services?.some((item) => item.id === "auth-broker") === true, "deploy.json prod should include auth-broker", deployPath);
|
||||
assertCondition(deploy.environments?.dev?.services?.some((item) => item.id === "auth-broker") === true, "deploy.json dev should include auth-broker", deployPath);
|
||||
assertCondition(authBrokerComposeBlock.includes(" auth-broker:") && authBrokerComposeBlock.includes("profiles:") && authBrokerComposeBlock.includes("- auth-broker"), "docker-compose should include auth-broker behind an explicit profile", authBrokerComposeBlock);
|
||||
assertCondition(!/^\s{4}ports:/mu.test(authBrokerComposeBlock), "auth-broker compose service must not publish ports", authBrokerComposeBlock);
|
||||
|
||||
const noToken = await runCli(["auth-broker", "pr-preflight", "--repo", "pikasTech/unidesk", "--base", "master", "--head", "feature/auth-broker", "--issue", "59", "--dry-run"]);
|
||||
assertCondition(noToken.status === 1, "missing broker endpoint should exit 1", { status: noToken.status, stdout: noToken.stdout, stderr: noToken.stderr });
|
||||
@@ -207,6 +275,7 @@ async function main(): Promise<void> {
|
||||
assertCondition(noTokenAuthBroker.source === "broker/auth-broker-needed", "missing broker endpoint should expose broker-needed auth source", noTokenAuthBroker);
|
||||
assertCondition(noTokenAuthBroker.capability === "missing-token", "missing broker endpoint should expose missing-token capability", noTokenAuthBroker);
|
||||
assertCondition(noTokenAuthBroker.nextAction === "configure-auth-broker", "missing broker endpoint should expose next action", noTokenAuthBroker);
|
||||
assertServiceRegistration(noTokenData.serviceRegistration);
|
||||
assertCondition(!noToken.stdout.includes("contract-secret-marker"), "missing-token response must not leak secret marker strings", noToken.stdout);
|
||||
|
||||
const brokerReady = await runCli([
|
||||
@@ -232,6 +301,7 @@ async function main(): Promise<void> {
|
||||
const readyAuthBroker = readyData.authBroker as Record<string, unknown>;
|
||||
const prCapability = readyData.prCapabilityContract as Record<string, unknown>;
|
||||
const brokerProxy = prCapability.brokerProxy as Record<string, unknown>;
|
||||
assertServiceRegistration(readyData.serviceRegistration);
|
||||
assertCondition(readyAuthBroker.source === "auth-broker", "ready auth broker source should be auth-broker", readyAuthBroker);
|
||||
assertCondition(readyAuthBroker.capability === "broker-issued-token", "ready auth broker capability should be broker-issued-token", readyAuthBroker);
|
||||
assertCondition(readyAuthBroker.nextAction === "use-auth-broker", "ready auth broker next action should use broker", readyAuthBroker);
|
||||
@@ -247,6 +317,23 @@ async function main(): Promise<void> {
|
||||
assertCondition(Array.isArray(brokerProxy.operations) && brokerProxy.operations.includes("github.pr.create"), "P0 broker proxy should include PR create dry-run operation", brokerProxy);
|
||||
walk(readyData);
|
||||
|
||||
const credentialRefPresence = await runCli(
|
||||
["auth-broker", "health", "--dry-run", "--endpoint", "http://127.0.0.1:4291"],
|
||||
{
|
||||
UNIDESK_AUTH_BROKER_GITHUB_CONFIGURED: "true",
|
||||
UNIDESK_AUTH_BROKER_GITHUB_CREDENTIAL_REF: "github:contract-secret-marker",
|
||||
},
|
||||
);
|
||||
assertCondition(credentialRefPresence.status === 0, "configured credential-ref presence dry-run should exit 0", credentialRefPresence);
|
||||
const credentialRefData = dataOf(credentialRefPresence.json ?? {});
|
||||
const registration = asRecord(credentialRefData.serviceRegistration, "credential-ref health should include service registration");
|
||||
const runtimeCredentialRef = asRecord(registration.runtimeCredentialRef, "health registration should include runtimeCredentialRef");
|
||||
const credentialRef = asRecord(runtimeCredentialRef.credentialRef, "runtimeCredentialRef.credentialRef should be object");
|
||||
assertCondition(runtimeCredentialRef.ok === true, "credential ref presence should be ready when configured flag and ref key are present", runtimeCredentialRef);
|
||||
assertCondition(credentialRef.valuePreview === "github:<ref>", "credential ref preview should be sanitized", credentialRef);
|
||||
assertCondition(!credentialRefPresence.stdout.includes("contract-secret-marker"), "credential ref dry-run must not print the raw credential ref value", credentialRefPresence.stdout);
|
||||
walk(credentialRefData);
|
||||
|
||||
process.stdout.write(`${JSON.stringify({
|
||||
ok: true,
|
||||
docPath,
|
||||
@@ -254,6 +341,9 @@ async function main(): Promise<void> {
|
||||
rustMainPath,
|
||||
rustCargoPath,
|
||||
cliAdapterPath,
|
||||
configPath,
|
||||
deployPath,
|
||||
composePath,
|
||||
},
|
||||
operations: requiredOperations,
|
||||
failureKinds: failureContracts.map((item) => item.failureKind),
|
||||
|
||||
@@ -111,6 +111,7 @@ const defaultOptions: ArtifactRegistryOptions = {
|
||||
deployJsonService: null,
|
||||
};
|
||||
const supportedArtifactConsumerServices = [
|
||||
"auth-broker",
|
||||
"backend-core",
|
||||
"baidu-netdisk",
|
||||
"claudeqq",
|
||||
@@ -259,6 +260,43 @@ const baiduNetdiskAuthHealthGate: AuthHealthGate = {
|
||||
};
|
||||
|
||||
const artifactConsumerSpecs: Record<string, ArtifactConsumerSpec> = {
|
||||
"auth-broker": {
|
||||
serviceId: "auth-broker",
|
||||
environment: "prod",
|
||||
kind: "compose",
|
||||
registryRepository: "unidesk/auth-broker",
|
||||
dockerfile: "src/components/microservices/auth-broker/Dockerfile",
|
||||
prodLiveApply: "supervisor-only",
|
||||
prodLiveBlockReason: "auth-broker is registered for source/contract/profile dry-run only; live production apply requires a separate credential mounting and exposure review.",
|
||||
devLiveApply: "supervisor-only",
|
||||
devLiveBlockReason: "auth-broker DEV live apply requires explicit operator authorization after reviewing credential mounting, private exposure, and dry-run evidence.",
|
||||
targets: {
|
||||
dev: {
|
||||
targetImage: "auth-broker",
|
||||
targetCommitImage: (commit: string) => `auth-broker:${commit}`,
|
||||
deployRef: "deploy.json#environments.dev.services.auth-broker",
|
||||
compose: {
|
||||
serviceName: "auth-broker",
|
||||
containerName: "auth-broker-backend",
|
||||
deployEnvPrefix: "UNIDESK_AUTH_BROKER_DEPLOY",
|
||||
healthProbeCommand: "auth-broker --healthcheck",
|
||||
requireHealthCommit: false,
|
||||
},
|
||||
},
|
||||
prod: {
|
||||
targetImage: "auth-broker",
|
||||
targetCommitImage: (commit: string) => `auth-broker:${commit}`,
|
||||
deployRef: "deploy.json#environments.prod.services.auth-broker",
|
||||
compose: {
|
||||
serviceName: "auth-broker",
|
||||
containerName: "auth-broker-backend",
|
||||
deployEnvPrefix: "UNIDESK_AUTH_BROKER_DEPLOY",
|
||||
healthProbeCommand: "auth-broker --healthcheck",
|
||||
requireHealthCommit: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"backend-core": {
|
||||
serviceId: "backend-core",
|
||||
environment: "prod",
|
||||
@@ -1435,6 +1473,27 @@ function codeQueueMgrSelfBootstrapGuard(environment: ArtifactDeployEnvironment,
|
||||
};
|
||||
}
|
||||
|
||||
function authBrokerCredentialMountGuard(environment: ArtifactDeployEnvironment, requiresSupervisorApproval: boolean): Record<string, unknown> {
|
||||
return {
|
||||
check: "auth-broker-credential-mount-guard",
|
||||
serviceId: "auth-broker",
|
||||
requiresSupervisorApproval,
|
||||
actorBoundary: "dry-run and contract evidence are allowed, but live broker startup needs explicit review of credential reference mounting and private exposure",
|
||||
targetScope: "main-server Compose profile auth-broker / container auth-broker-backend only",
|
||||
composeProfileRequired: "auth-broker",
|
||||
publicPortAllowed: false,
|
||||
registryCredentialsProxied: false,
|
||||
deployCredentialsProxied: false,
|
||||
environment,
|
||||
forbiddenActions: [
|
||||
"write real GitHub token into config.json, deploy.json, or docker-compose.yml",
|
||||
"publish auth-broker on a public port",
|
||||
"restart backend-core, provider-gateway, or Code Queue as part of broker dry-run registration",
|
||||
"grant registry, deploy, database, k3s, provider token, or host SSH permissions",
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function artifactConsumerSelfBootstrapGuard(
|
||||
spec: ArtifactConsumerSpec,
|
||||
environment: ArtifactDeployEnvironment,
|
||||
@@ -1442,6 +1501,7 @@ function artifactConsumerSelfBootstrapGuard(
|
||||
): Record<string, unknown> | undefined {
|
||||
if (spec.serviceId === "code-queue") return codeQueueSelfBootstrapGuard(environment);
|
||||
if (spec.serviceId === "code-queue-mgr") return codeQueueMgrSelfBootstrapGuard(environment, requiresSupervisorApproval);
|
||||
if (spec.serviceId === "auth-broker") return authBrokerCredentialMountGuard(environment, requiresSupervisorApproval);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
+244
-2
@@ -1,7 +1,13 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { type UniDeskConfig, readConfig, rootPath } from "./config";
|
||||
|
||||
const DEFAULT_REPO = "pikasTech/unidesk";
|
||||
const DEFAULT_BASE = "master";
|
||||
const SECRET_ENV_KEYS = ["GH_TOKEN", "GITHUB_TOKEN"] as const;
|
||||
const BROKER_URL_ENV_KEYS = ["UNIDESK_AUTH_BROKER_URL", "AUTH_BROKER_URL"] as const;
|
||||
const BROKER_CREDENTIAL_REF_ENV_KEYS = ["UNIDESK_AUTH_BROKER_GITHUB_CREDENTIAL_REF", "AUTH_BROKER_GITHUB_CREDENTIAL_REF"] as const;
|
||||
const BROKER_CONFIGURED_ENV_KEYS = ["UNIDESK_AUTH_BROKER_GITHUB_CONFIGURED", "AUTH_BROKER_GITHUB_CONFIGURED"] as const;
|
||||
const AUTH_BROKER_SERVICE_ID = "auth-broker";
|
||||
const DEFAULT_CAPABILITIES = [
|
||||
"github.auth.status",
|
||||
"github.issue.list",
|
||||
@@ -15,6 +21,7 @@ const DEFAULT_CAPABILITIES = [
|
||||
|
||||
type BrokerCommand = "contract" | "credential-request" | "pr-preflight" | "health";
|
||||
type RunnerDisposition = "ready" | "infra-blocked" | "business-failed";
|
||||
type ConfigSource = "config.json" | "unavailable";
|
||||
|
||||
interface BrokerAdapterOptions {
|
||||
command: BrokerCommand;
|
||||
@@ -27,10 +34,76 @@ interface BrokerAdapterOptions {
|
||||
issueNumber: number | null;
|
||||
}
|
||||
|
||||
interface AuthBrokerServiceRegistration {
|
||||
ok: boolean;
|
||||
serviceId: "auth-broker";
|
||||
source: ConfigSource;
|
||||
configured: boolean;
|
||||
providerId: string | null;
|
||||
deploymentMode: string | null;
|
||||
proxyMode: string | null;
|
||||
backendBaseUrl: string | null;
|
||||
healthPath: string | null;
|
||||
allowedPathPrefixes: string[];
|
||||
composeService: string | null;
|
||||
containerName: string | null;
|
||||
dockerfile: string | null;
|
||||
public: boolean | null;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
interface ComposeProfileRegistration {
|
||||
ok: boolean;
|
||||
source: "docker-compose.yml";
|
||||
serviceId: "auth-broker";
|
||||
servicePresent: boolean;
|
||||
profileGated: boolean;
|
||||
profiles: string[];
|
||||
restart: string | null;
|
||||
publicPortPublished: boolean;
|
||||
exposesPort: boolean;
|
||||
mutatesDefaultRuntime: false;
|
||||
}
|
||||
|
||||
interface DeployManifestRegistration {
|
||||
ok: boolean;
|
||||
source: "deploy.json";
|
||||
serviceId: "auth-broker";
|
||||
prod: { present: boolean; commitId: string | null };
|
||||
dev: { present: boolean; commitId: string | null };
|
||||
dryRunOnly: true;
|
||||
}
|
||||
|
||||
interface RuntimeCredentialRefPresence {
|
||||
ok: boolean;
|
||||
source: string | null;
|
||||
configuredFlag: {
|
||||
present: boolean;
|
||||
key: string | null;
|
||||
truthy: boolean;
|
||||
};
|
||||
credentialRef: {
|
||||
present: boolean;
|
||||
key: string | null;
|
||||
valuePrinted: false;
|
||||
valuePreview: string | null;
|
||||
};
|
||||
presenceOnly: true;
|
||||
valuesRead: false;
|
||||
valuesPrinted: false;
|
||||
}
|
||||
|
||||
function hasEnvKey(name: string): boolean {
|
||||
return Object.prototype.hasOwnProperty.call(process.env, name);
|
||||
}
|
||||
|
||||
function firstPresentEnvKey(keys: readonly string[]): string | null {
|
||||
for (const key of keys) {
|
||||
if (hasEnvKey(key)) return key;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function stringOption(args: string[], name: string): string | undefined {
|
||||
const index = args.indexOf(name);
|
||||
if (index === -1) return undefined;
|
||||
@@ -52,6 +125,14 @@ function sanitizeEndpoint(value: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
function sanitizeCredentialRef(value: string): string {
|
||||
const trimmed = value.trim();
|
||||
if (trimmed.length === 0) return "<empty>";
|
||||
const separator = trimmed.indexOf(":");
|
||||
if (separator <= 0) return "<credential-ref>";
|
||||
return `${trimmed.slice(0, separator)}:<ref>`;
|
||||
}
|
||||
|
||||
function numberOption(args: string[], name: string): number | null {
|
||||
const raw = stringOption(args, name);
|
||||
if (raw === undefined) return null;
|
||||
@@ -67,6 +148,160 @@ function firstConfiguredBrokerUrl(): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
function envTruthy(key: string | null): boolean {
|
||||
if (key === null) return false;
|
||||
const value = process.env[key]?.trim().toLowerCase();
|
||||
return value === "1" || value === "true" || value === "yes" || value === "on";
|
||||
}
|
||||
|
||||
function runtimeCredentialRefPresence(): RuntimeCredentialRefPresence {
|
||||
const configuredKey = firstPresentEnvKey(BROKER_CONFIGURED_ENV_KEYS);
|
||||
const refKey = firstPresentEnvKey(BROKER_CREDENTIAL_REF_ENV_KEYS);
|
||||
return {
|
||||
ok: configuredKey !== null && envTruthy(configuredKey) && refKey !== null,
|
||||
source: refKey === null ? null : "broker-held-github-credential-ref",
|
||||
configuredFlag: {
|
||||
present: configuredKey !== null,
|
||||
key: configuredKey,
|
||||
truthy: envTruthy(configuredKey),
|
||||
},
|
||||
credentialRef: {
|
||||
present: refKey !== null,
|
||||
key: refKey,
|
||||
valuePrinted: false,
|
||||
valuePreview: refKey === null ? null : sanitizeCredentialRef(process.env[refKey] ?? ""),
|
||||
},
|
||||
presenceOnly: true,
|
||||
valuesRead: false,
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
function readConfigRegistration(): AuthBrokerServiceRegistration {
|
||||
let config: UniDeskConfig;
|
||||
try {
|
||||
config = readConfig();
|
||||
} catch (error) {
|
||||
return {
|
||||
ok: false,
|
||||
serviceId: AUTH_BROKER_SERVICE_ID,
|
||||
source: "unavailable",
|
||||
configured: false,
|
||||
providerId: null,
|
||||
deploymentMode: null,
|
||||
proxyMode: null,
|
||||
backendBaseUrl: null,
|
||||
healthPath: null,
|
||||
allowedPathPrefixes: [],
|
||||
composeService: null,
|
||||
containerName: null,
|
||||
dockerfile: null,
|
||||
public: null,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
const service = config.microservices.find((item) => item.id === AUTH_BROKER_SERVICE_ID);
|
||||
return {
|
||||
ok: service !== undefined,
|
||||
serviceId: AUTH_BROKER_SERVICE_ID,
|
||||
source: "config.json",
|
||||
configured: service !== undefined,
|
||||
providerId: service?.providerId ?? null,
|
||||
deploymentMode: service?.deployment.mode ?? null,
|
||||
proxyMode: service?.backend.proxyMode ?? null,
|
||||
backendBaseUrl: service?.backend.nodeBaseUrl ?? null,
|
||||
healthPath: service?.backend.healthPath ?? null,
|
||||
allowedPathPrefixes: service?.backend.allowedPathPrefixes ?? [],
|
||||
composeService: service?.repository.composeService ?? null,
|
||||
containerName: service?.repository.containerName ?? null,
|
||||
dockerfile: service?.repository.dockerfile ?? null,
|
||||
public: service?.backend.public ?? null,
|
||||
error: service === undefined ? "auth-broker is not registered in config.json microservices" : null,
|
||||
};
|
||||
}
|
||||
|
||||
function extractComposeServiceBlock(composeText: string, serviceName: string): string {
|
||||
const lines = composeText.split("\n");
|
||||
const startLine = lines.findIndex((line) => line === ` ${serviceName}:`);
|
||||
if (startLine < 0) return "";
|
||||
let endLine = lines.length;
|
||||
for (let index = startLine + 1; index < lines.length; index += 1) {
|
||||
if (/^ [A-Za-z0-9][A-Za-z0-9_-]*:$/u.test(lines[index] ?? "")) {
|
||||
endLine = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return lines.slice(startLine, endLine).join("\n");
|
||||
}
|
||||
|
||||
function composeProfileRegistration(): ComposeProfileRegistration {
|
||||
const composeText = readFileSync(rootPath("docker-compose.yml"), "utf8");
|
||||
const block = extractComposeServiceBlock(composeText, AUTH_BROKER_SERVICE_ID);
|
||||
const blockLines = block.split("\n");
|
||||
const profiles: string[] = [];
|
||||
const profileStart = blockLines.findIndex((line) => /^\s{4}profiles:\s*$/u.test(line));
|
||||
if (profileStart >= 0) {
|
||||
for (let index = profileStart + 1; index < blockLines.length; index += 1) {
|
||||
const line = blockLines[index] ?? "";
|
||||
if (/^\s{4}[A-Za-z0-9_-]+:/u.test(line)) break;
|
||||
const profile = line.match(/^\s{6}-\s+"?([A-Za-z0-9_.-]+)"?\s*$/u)?.[1];
|
||||
if (profile !== undefined) profiles.push(profile);
|
||||
}
|
||||
}
|
||||
const restart = block.match(/^\s{4}restart:\s+"?([^"\n]+)"?\s*$/mu)?.[1] ?? null;
|
||||
const portsSection = /^\s{4}ports:\s*$/mu.test(block);
|
||||
const portMapping = /^\s{6}-\s+["']?[^"'\n]*4291:/mu.test(block);
|
||||
return {
|
||||
ok: block.length > 0 && profiles.includes(AUTH_BROKER_SERVICE_ID) && !portsSection && !portMapping,
|
||||
source: "docker-compose.yml",
|
||||
serviceId: AUTH_BROKER_SERVICE_ID,
|
||||
servicePresent: block.length > 0,
|
||||
profileGated: profiles.includes(AUTH_BROKER_SERVICE_ID),
|
||||
profiles,
|
||||
restart,
|
||||
publicPortPublished: portsSection || portMapping,
|
||||
exposesPort: block.includes('"4291"'),
|
||||
mutatesDefaultRuntime: false,
|
||||
};
|
||||
}
|
||||
|
||||
function serviceCommitFromDeployJson(environment: "prod" | "dev"): string | null {
|
||||
const parsed = JSON.parse(readFileSync(rootPath("deploy.json"), "utf8")) as {
|
||||
environments?: Record<string, { services?: Array<{ id?: unknown; commitId?: unknown }> }>;
|
||||
};
|
||||
const service = parsed.environments?.[environment]?.services?.find((item) => item.id === AUTH_BROKER_SERVICE_ID);
|
||||
return typeof service?.commitId === "string" ? service.commitId : null;
|
||||
}
|
||||
|
||||
function deployManifestRegistration(): DeployManifestRegistration {
|
||||
const prodCommit = serviceCommitFromDeployJson("prod");
|
||||
const devCommit = serviceCommitFromDeployJson("dev");
|
||||
return {
|
||||
ok: prodCommit !== null && devCommit !== null,
|
||||
source: "deploy.json",
|
||||
serviceId: AUTH_BROKER_SERVICE_ID,
|
||||
prod: { present: prodCommit !== null, commitId: prodCommit },
|
||||
dev: { present: devCommit !== null, commitId: devCommit },
|
||||
dryRunOnly: true,
|
||||
};
|
||||
}
|
||||
|
||||
function serviceRegistrationContract(): Record<string, unknown> {
|
||||
return {
|
||||
config: readConfigRegistration(),
|
||||
compose: composeProfileRegistration(),
|
||||
deploy: deployManifestRegistration(),
|
||||
runtimeCredentialRef: runtimeCredentialRefPresence(),
|
||||
defaultRuntimeMutation: {
|
||||
ok: true,
|
||||
mutatesCurrentProd: false,
|
||||
composeProfileRequired: AUTH_BROKER_SERVICE_ID,
|
||||
publicPortPublished: false,
|
||||
liveDeployPerformed: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function parseOptions(args: string[]): BrokerAdapterOptions {
|
||||
const rawCommand = args[0] ?? "contract";
|
||||
if (!["contract", "credential-request", "pr-preflight", "health"].includes(rawCommand)) {
|
||||
@@ -101,11 +336,14 @@ function runnerEnvTokenCoverage(): Record<string, unknown> {
|
||||
}
|
||||
|
||||
function brokerCoverage(endpoint: string | null): Record<string, unknown> {
|
||||
const credential = runtimeCredentialRefPresence();
|
||||
return {
|
||||
ok: endpoint !== null,
|
||||
source: endpoint === null ? null : "auth-broker",
|
||||
endpoint: endpoint ?? null,
|
||||
credentialRef: endpoint === null ? null : "github:unidesk-dev",
|
||||
credentialRef: endpoint === null ? null : credential.credentialRef.valuePreview ?? "github:<ref>",
|
||||
credentialRefPresent: credential.credentialRef.present,
|
||||
credentialRefSourceKey: credential.credentialRef.key,
|
||||
scope: endpoint === null ? null : "broker-held-github-credential",
|
||||
runnerEnvTokenRequired: false,
|
||||
valuesRead: false,
|
||||
@@ -124,6 +362,7 @@ function brokerNeededResult(options: BrokerAdapterOptions): Record<string, unkno
|
||||
message: "No auth broker endpoint is configured for this dry-run contract; runner env token coverage is reported only for migration diagnostics.",
|
||||
tokenCoverage: runnerEnvTokenCoverage(),
|
||||
brokerCoverage: brokerCoverage(options.endpoint),
|
||||
serviceRegistration: serviceRegistrationContract(),
|
||||
authBroker: {
|
||||
ok: false,
|
||||
source: "broker/auth-broker-needed",
|
||||
@@ -158,7 +397,7 @@ function auditEventShape(options: BrokerAdapterOptions): Record<string, unknown>
|
||||
? { base: options.base, head: options.head, issueNumber: options.issueNumber }
|
||||
: null,
|
||||
dryRun: true,
|
||||
credentialRef: "github:unidesk-dev",
|
||||
credentialRef: "github:<ref>",
|
||||
credentialKind: "github-rest-token-ref",
|
||||
credentialValuePrinted: false,
|
||||
upstream: { method: "planned", path: "planned GitHub REST path without query secrets" },
|
||||
@@ -218,6 +457,7 @@ function readyContract(options: BrokerAdapterOptions): Record<string, unknown> {
|
||||
brokerCoverage: brokerCoverage(options.endpoint),
|
||||
credentialRequest: plannedCredentialRequest(options),
|
||||
auditEventShape: auditEventShape(options),
|
||||
serviceRegistration: serviceRegistrationContract(),
|
||||
prCapabilityContract: {
|
||||
targetBranch: options.base,
|
||||
headBranch: options.head,
|
||||
@@ -263,6 +503,7 @@ function contractResult(options: BrokerAdapterOptions): Record<string, unknown>
|
||||
registryCredentials: false,
|
||||
deployPermissions: false,
|
||||
},
|
||||
serviceRegistration: serviceRegistrationContract(),
|
||||
runnerNoTokenResult: brokerNeededResult({ ...options, endpoint: null }),
|
||||
readyShape: readyContract({ ...options, endpoint: "<UNIDESK_AUTH_BROKER_URL>" }),
|
||||
};
|
||||
@@ -305,6 +546,7 @@ export function runAuthBrokerCommand(args: string[]): Record<string, unknown> {
|
||||
phase: "p0",
|
||||
brokerCoverage: broker,
|
||||
tokenCoverage: envCoverage,
|
||||
serviceRegistration: serviceRegistrationContract(),
|
||||
healthRequest: {
|
||||
method: "GET",
|
||||
path: "/health",
|
||||
|
||||
@@ -149,16 +149,18 @@ const nativeK3sCtrAddress = "/run/k3s/containerd/containerd.sock";
|
||||
const unideskRepoUrl = "https://github.com/pikasTech/unidesk";
|
||||
const d601MaintenanceDeployAllowedServiceIds = new Set<string>(["k3sctl-adapter"]);
|
||||
const devApplySupportedServiceIds = new Set<string>();
|
||||
const devArtifactConsumerServiceIds = new Set<string>(["backend-core", "baidu-netdisk", "claudeqq", "code-queue", "code-queue-mgr", "decision-center", "findjob", "frontend", "mdtodo", "met-nonlinear", "oa-event-flow", "pipeline", "project-manager", "todo-note"]);
|
||||
const devArtifactConsumerServiceIds = new Set<string>(["auth-broker", "backend-core", "baidu-netdisk", "claudeqq", "code-queue", "code-queue-mgr", "decision-center", "findjob", "frontend", "mdtodo", "met-nonlinear", "oa-event-flow", "pipeline", "project-manager", "todo-note"]);
|
||||
const devArtifactConsumerProdDesiredFallbackServiceIds = new Set<string>(["code-queue-mgr", "oa-event-flow", "project-manager", "todo-note"]);
|
||||
const prodArtifactConsumerServiceIds = new Set<string>(["backend-core", "baidu-netdisk", "claudeqq", "code-queue-mgr", "decision-center", "findjob", "frontend", "k3sctl-adapter", "mdtodo", "met-nonlinear", "oa-event-flow", "pipeline", "project-manager", "todo-note"]);
|
||||
const prodArtifactConsumerServiceIds = new Set<string>(["auth-broker", "backend-core", "baidu-netdisk", "claudeqq", "code-queue-mgr", "decision-center", "findjob", "frontend", "k3sctl-adapter", "mdtodo", "met-nonlinear", "oa-event-flow", "pipeline", "project-manager", "todo-note"]);
|
||||
const prodForbiddenTargetSideBuildServiceIds = new Set<string>(["backend-core", "baidu-netdisk", "claudeqq", "decision-center", "findjob", "frontend", "k3sctl-adapter", "mdtodo", "met-nonlinear", "pipeline"]);
|
||||
const prodArtifactLiveApplyBlockedServiceIds = new Map<string, string>([
|
||||
["auth-broker", "auth-broker is registered for source/contract/profile dry-run only; live production apply requires a separate credential mounting and exposure review."],
|
||||
["code-queue-mgr", "code-queue-mgr is the main-server Code Queue control-plane sidecar; live production apply requires explicit supervisor confirmation."],
|
||||
["met-nonlinear", "met-nonlinear is blocked for live artifact deploy because config.json points at docker/unidesk/Dockerfile.ml while the compose service is met-nonlinear-ts."],
|
||||
["k3sctl-adapter", "k3sctl-adapter is an infrastructure control bridge; this executor exposes artifact consumer plan/dry-run only. Real production deployment requires supervisor confirmation outside this task."],
|
||||
]);
|
||||
const devArtifactLiveApplyBlockedServiceIds = new Map<string, string>([
|
||||
["auth-broker", "auth-broker is registered for source/contract/profile dry-run only; live DEV apply requires a separate credential mounting and exposure review."],
|
||||
["code-queue", "Code Queue DEV live apply is self-bootstrap sensitive: a running Code Queue task may produce dry-run evidence only. A human operator or supervisor must explicitly authorize DEV apply outside Code Queue after reviewing the CI artifact digest and dry-run target list."],
|
||||
]);
|
||||
const artifactConsumerDryRunBlockedServiceIds = new Map<string, string>([
|
||||
@@ -3538,7 +3540,7 @@ export async function runDeployCommand(config: UniDeskConfig | null, args: strin
|
||||
}
|
||||
const unsupported = unsupportedDevApplyServices(manifest, options.serviceId);
|
||||
if (unsupported.length > 0) {
|
||||
throw new Error(`deploy apply --env dev currently supports backend-core/frontend/baidu-netdisk/decision-center/mdtodo/claudeqq/code-queue/project-manager/oa-event-flow/code-queue-mgr/todo-note/findjob/pipeline/met-nonlinear artifact consumers; unsupported selected services: ${unsupported.join(", ")}. Use ci run-dev-e2e for smoke verification.`);
|
||||
throw new Error(`deploy apply --env dev currently supports auth-broker/backend-core/frontend/baidu-netdisk/decision-center/mdtodo/claudeqq/code-queue/project-manager/oa-event-flow/code-queue-mgr/todo-note/findjob/pipeline/met-nonlinear artifact consumers; unsupported selected services: ${unsupported.join(", ")}. Use ci run-dev-e2e for smoke verification.`);
|
||||
}
|
||||
const devArtifactServices = selectedDevArtifactServicesWithProdFallback(manifest, options.serviceId);
|
||||
const devTargetServices = selectedDevTargetServices(manifest, options.serviceId);
|
||||
|
||||
@@ -181,6 +181,14 @@ export function writeComposeEnv(config: UniDeskConfig, freshLogPrefix: boolean):
|
||||
UNIDESK_CODE_QUEUE_MGR_DEPLOY_REPO: runtimeSecret("UNIDESK_CODE_QUEUE_MGR_DEPLOY_REPO"),
|
||||
UNIDESK_CODE_QUEUE_MGR_DEPLOY_COMMIT: runtimeSecret("UNIDESK_CODE_QUEUE_MGR_DEPLOY_COMMIT"),
|
||||
UNIDESK_CODE_QUEUE_MGR_DEPLOY_REQUESTED_COMMIT: runtimeSecret("UNIDESK_CODE_QUEUE_MGR_DEPLOY_REQUESTED_COMMIT"),
|
||||
UNIDESK_AUTH_BROKER_DEPLOY_REF: runtimeSecret("UNIDESK_AUTH_BROKER_DEPLOY_REF"),
|
||||
UNIDESK_AUTH_BROKER_DEPLOY_SERVICE_ID: runtimeSecret("UNIDESK_AUTH_BROKER_DEPLOY_SERVICE_ID") || "auth-broker",
|
||||
UNIDESK_AUTH_BROKER_DEPLOY_REPO: runtimeSecret("UNIDESK_AUTH_BROKER_DEPLOY_REPO"),
|
||||
UNIDESK_AUTH_BROKER_DEPLOY_COMMIT: runtimeSecret("UNIDESK_AUTH_BROKER_DEPLOY_COMMIT"),
|
||||
UNIDESK_AUTH_BROKER_DEPLOY_REQUESTED_COMMIT: runtimeSecret("UNIDESK_AUTH_BROKER_DEPLOY_REQUESTED_COMMIT"),
|
||||
UNIDESK_AUTH_BROKER_GITHUB_CONFIGURED: runtimeSecret("UNIDESK_AUTH_BROKER_GITHUB_CONFIGURED") || "false",
|
||||
UNIDESK_AUTH_BROKER_GITHUB_CREDENTIAL_REF: runtimeSecret("UNIDESK_AUTH_BROKER_GITHUB_CREDENTIAL_REF") || "github:unidesk-dev",
|
||||
UNIDESK_AUTH_BROKER_ALLOWED_REPOS: runtimeSecret("UNIDESK_AUTH_BROKER_ALLOWED_REPOS") || "pikasTech/unidesk",
|
||||
UNIDESK_TODO_NOTE_DEPLOY_REF: runtimeSecret("UNIDESK_TODO_NOTE_DEPLOY_REF"),
|
||||
UNIDESK_TODO_NOTE_DEPLOY_SERVICE_ID: runtimeSecret("UNIDESK_TODO_NOTE_DEPLOY_SERVICE_ID") || "todo-note",
|
||||
UNIDESK_TODO_NOTE_DEPLOY_REPO: runtimeSecret("UNIDESK_TODO_NOTE_DEPLOY_REPO"),
|
||||
|
||||
Reference in New Issue
Block a user