Merge pull request #2137 from pikasTech/feat/pikaoa-production-release
feat(pikaoa): 建立生产 release 控制面
This commit is contained in:
@@ -101,6 +101,15 @@ testRuntime:
|
||||
sourceKey: DATABASE_URL
|
||||
targetKey: DATABASE_URL
|
||||
runtime:
|
||||
replicas:
|
||||
api: 1
|
||||
worker: 1
|
||||
web: 1
|
||||
resources:
|
||||
api: { requests: { cpu: 100m, memory: 128Mi }, limits: { cpu: 1, memory: 512Mi } }
|
||||
worker: { requests: { cpu: 100m, memory: 128Mi }, limits: { cpu: 1, memory: 512Mi } }
|
||||
web: { requests: { cpu: 50m, memory: 64Mi }, limits: { cpu: 500m, memory: 256Mi } }
|
||||
initializer: { requests: { cpu: 100m, memory: 128Mi }, limits: { cpu: 1, memory: 512Mi } }
|
||||
secretName: pikaoa-test-runtime
|
||||
apiServiceName: pikaoa-api
|
||||
apiPort: 8080
|
||||
@@ -174,6 +183,163 @@ testRuntime:
|
||||
livenessPath: /healthz
|
||||
readinessPath: /healthz
|
||||
|
||||
releaseRuntime:
|
||||
defaultTargetId: NC01
|
||||
targets:
|
||||
NC01:
|
||||
enabled: true
|
||||
validationOnly: false
|
||||
node: NC01
|
||||
route: NC01:k3s
|
||||
namespace: pikaoa
|
||||
ttlSeconds: 86400
|
||||
waitTimeoutSeconds: 300
|
||||
fieldManager: unidesk-pikaoa-release-runtime
|
||||
taskState:
|
||||
rootPath: /var/lib/unidesk/pikaoa-release-runtime
|
||||
statusRequestTimeoutSeconds: 10
|
||||
logTailLines: 80
|
||||
cleanup:
|
||||
mode: delete-namespace
|
||||
requireOwnershipLabels: true
|
||||
source:
|
||||
mode: prebuilt-images
|
||||
repository: pikainc/pikaoa
|
||||
commitPolicy: cli-required
|
||||
branch: release
|
||||
worktreeRemote: https://github.com/pikainc/pikaoa.git
|
||||
readUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikainc-pikaoa.git
|
||||
snapshotPrefix: refs/unidesk/snapshots/gitea-actions/pikaoa-release-nc01
|
||||
images:
|
||||
api: 127.0.0.1:5000/pikaoa/api:${commit}
|
||||
worker: 127.0.0.1:5000/pikaoa/worker:${commit}
|
||||
web: 127.0.0.1:5000/pikaoa/web:${commit}
|
||||
initializer: 127.0.0.1:5000/pikaoa/api:${commit}
|
||||
pullPolicy: IfNotPresent
|
||||
ci:
|
||||
lane: pikaoa
|
||||
namespace: pikaoa-ci
|
||||
pipeline: pikaoa-nc01-pac
|
||||
pipelineRunPrefix: pikaoa-nc01
|
||||
serviceAccountName: pikaoa-nc01-tekton-runner
|
||||
workspaceSize: 8Gi
|
||||
pipelineTimeout: 20m0s
|
||||
toolsImage: 127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-bun-v1
|
||||
buildkitImage: 127.0.0.1:5000/hwlab/buildkit:rootless
|
||||
build:
|
||||
dockerfiles:
|
||||
api: deploy/api.Dockerfile
|
||||
worker: deploy/worker.Dockerfile
|
||||
web: deploy/web.Dockerfile
|
||||
images:
|
||||
api: 127.0.0.1:5000/pikaoa/api
|
||||
worker: 127.0.0.1:5000/pikaoa/worker
|
||||
web: 127.0.0.1:5000/pikaoa/web
|
||||
networkMode: host
|
||||
gitops:
|
||||
readUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikainc-pikaoa.git
|
||||
writeUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikainc-pikaoa.git
|
||||
branch: nc01-pikaoa-gitops
|
||||
manifestPath: deploy/gitops/nc01/resources.yaml
|
||||
credential:
|
||||
secretName: pac-gitea-pikaoa-nc01
|
||||
tokenKey: token
|
||||
username: unidesk-admin
|
||||
author:
|
||||
name: PikaOA NC01 CI
|
||||
email: pikaoa-nc01-ci@unidesk.local
|
||||
database:
|
||||
configRef: config/platform-db/postgres-pk01.yaml#exports.connectionStrings.pikaoa-database-url
|
||||
name: pikaoa
|
||||
username: pikaoa
|
||||
schema: pikaoa
|
||||
connection:
|
||||
sourceRef: /root/.unidesk/.state/secrets/platform-infra/pikaoa.env
|
||||
sourceKey: DATABASE_URL
|
||||
targetKey: DATABASE_URL
|
||||
runtime:
|
||||
replicas:
|
||||
api: 1
|
||||
worker: 1
|
||||
web: 1
|
||||
resources:
|
||||
api: { requests: { cpu: 250m, memory: 256Mi }, limits: { cpu: 1, memory: 1Gi } }
|
||||
worker: { requests: { cpu: 100m, memory: 256Mi }, limits: { cpu: 1, memory: 1Gi } }
|
||||
web: { requests: { cpu: 100m, memory: 128Mi }, limits: { cpu: 500m, memory: 512Mi } }
|
||||
initializer: { requests: { cpu: 250m, memory: 256Mi }, limits: { cpu: 1, memory: 1Gi } }
|
||||
secretName: pikaoa-runtime
|
||||
apiServiceName: pikaoa-api
|
||||
apiPort: 8080
|
||||
workerMetricsPort: 8081
|
||||
webPort: 8080
|
||||
webApiUpstreamEnv: PIKAOA_API_UPSTREAM
|
||||
attachment:
|
||||
storageRoot: /var/lib/pikaoa/attachments
|
||||
maxBytes: 104857600
|
||||
claimName: pikaoa-attachments
|
||||
storageRequest: 20Gi
|
||||
storageClassName: null
|
||||
fsGroup: 65532
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
workerInterval: 2s
|
||||
outbox:
|
||||
batchSize: 50
|
||||
maxAttempts: 4
|
||||
retryDelay: 2s
|
||||
sessionTTL: 8h
|
||||
shutdownTimeout: 10s
|
||||
administrator:
|
||||
username: admin
|
||||
password:
|
||||
sourceRef: ~/.unidesk/.env/pikaoa-admin-password.txt
|
||||
targetKey: PIKAOA_BOOTSTRAP_ADMIN_PASSWORD
|
||||
employee:
|
||||
username: employee
|
||||
password:
|
||||
sourceRef: ~/.unidesk/.env/pikaoa-employee-password.txt
|
||||
targetKey: PIKAOA_BOOTSTRAP_EMPLOYEE_PASSWORD
|
||||
sessionSecret:
|
||||
sourceRef: /root/.unidesk/.state/secrets/platform-infra/pikaoa.env
|
||||
sourceKey: PIKAOA_SESSION_SECRET
|
||||
targetKey: PIKAOA_SESSION_SECRET
|
||||
initializer:
|
||||
mode: fresh-database-only
|
||||
jobName: pikaoa-init
|
||||
backoffLimit: 2
|
||||
command:
|
||||
- /usr/local/bin/pikaoa
|
||||
- migrate
|
||||
- up
|
||||
- --config
|
||||
- /etc/pikaoa/pikaoa.yaml
|
||||
exposure:
|
||||
serviceName: pikaoa-web
|
||||
serviceType: NodePort
|
||||
hostIP: 152.53.229.148
|
||||
port: 32096
|
||||
observability:
|
||||
configRef: config/platform-infra/observability.yaml
|
||||
otlpEndpoint: otel-collector.platform-infra.svc.cluster.local:4317
|
||||
prometheus:
|
||||
configRef: config/platform-infra/observability.yaml#metricsBackend.discovery.labelSelector
|
||||
scrape: true
|
||||
apiPath: /metrics
|
||||
workerPath: /metrics
|
||||
exporterFailure:
|
||||
warning: true
|
||||
blocking: false
|
||||
probes:
|
||||
api:
|
||||
livenessPath: /healthz
|
||||
readinessPath: /readyz
|
||||
worker:
|
||||
livenessPath: /healthz
|
||||
readinessPath: /readyz
|
||||
web:
|
||||
livenessPath: /healthz
|
||||
readinessPath: /healthz
|
||||
|
||||
delivery:
|
||||
targets:
|
||||
NC01:
|
||||
|
||||
@@ -252,6 +252,45 @@ repositories:
|
||||
runtime_service_port: "8080"
|
||||
health_path: /healthz
|
||||
health_url: http://pikaoa-web.pikaoa-test.svc.cluster.local:8080/healthz
|
||||
- id: pikaoa-nc01
|
||||
name: pikaoa-nc01
|
||||
namespace: pikaoa-ci
|
||||
providerType: gitea
|
||||
url: https://gitea.pikapython.com/mirrors/pikainc-pikaoa
|
||||
cloneUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikainc-pikaoa.git
|
||||
owner: mirrors
|
||||
repo: pikainc-pikaoa
|
||||
secretName: pac-gitea-pikaoa-nc01
|
||||
tokenKey: token
|
||||
webhookSecretKey: webhook.secret
|
||||
concurrencyLimit: 1
|
||||
params:
|
||||
git_read_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikainc-pikaoa.git
|
||||
source_branch: release
|
||||
source_snapshot_prefix: refs/unidesk/snapshots/gitea-actions/pikaoa-release-nc01
|
||||
node: NC01
|
||||
pipeline_name: pikaoa-nc01-pac
|
||||
pipeline_run_prefix: pikaoa-nc01
|
||||
service_account: pikaoa-nc01-tekton-runner
|
||||
pipeline_timeout: 20m0s
|
||||
workspace_pvc_size: 8Gi
|
||||
image_repository: 127.0.0.1:5000/pikaoa/api
|
||||
api_image_repository: 127.0.0.1:5000/pikaoa/api
|
||||
worker_image_repository: 127.0.0.1:5000/pikaoa/worker
|
||||
web_image_repository: 127.0.0.1:5000/pikaoa/web
|
||||
registry_probe_base: http://127.0.0.1:5000
|
||||
gitops_read_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikainc-pikaoa.git
|
||||
gitops_write_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikainc-pikaoa.git
|
||||
gitops_branch: nc01-pikaoa-gitops
|
||||
gitops_username: unidesk-admin
|
||||
gitops_secret_name: pac-gitea-pikaoa-nc01
|
||||
gitops_manifest_path: deploy/gitops/nc01/resources.yaml
|
||||
runtime_namespace: pikaoa
|
||||
runtime_deployment: pikaoa-api
|
||||
runtime_service: pikaoa-web
|
||||
runtime_service_port: "8080"
|
||||
health_path: /healthz
|
||||
health_url: http://pikaoa-web.pikaoa.svc.cluster.local:8080/healthz
|
||||
consumers:
|
||||
- extends: templates.consumers.agentrunV02
|
||||
variables:
|
||||
@@ -509,7 +548,7 @@ consumers:
|
||||
dnsPolicy: ClusterFirstWithHostNet
|
||||
fsGroup: 1000
|
||||
- id: pikaoa-nc01
|
||||
repositoryRef: pikaoa-test-nc01
|
||||
repositoryRef: pikaoa-nc01
|
||||
node: NC01
|
||||
lane: pikaoa
|
||||
namespace: pikaoa-ci
|
||||
@@ -541,10 +580,27 @@ consumers:
|
||||
repositoryCredential:
|
||||
secretName: argocd-repo-pikaoa-nc01
|
||||
username: unidesk-admin
|
||||
deliveryProvenance:
|
||||
required: true
|
||||
markerValue: admission-pac-v2:pikaoa-nc01
|
||||
executionServiceAccountName: pikaoa-nc01-tekton-runner
|
||||
gitOps:
|
||||
repoUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikainc-pikaoa.git
|
||||
targetRevision: nc01-pikaoa-gitops
|
||||
runnerServiceAccount:
|
||||
name: pikaoa-nc01-tekton-runner
|
||||
automountServiceAccountToken: false
|
||||
roleBindingName: pikaoa-nc01-tekton-runner
|
||||
sourceArtifact:
|
||||
mode: embedded-pipeline-spec
|
||||
renderer: pikaoa-release-runtime
|
||||
configRef: config/pikaoa.yaml#releaseRuntime.targets.NC01
|
||||
pipelineRunPath: .tekton/pikaoa-nc01-pac.yaml
|
||||
maxKeepRuns: 8
|
||||
taskRunTemplate:
|
||||
hostNetwork: true
|
||||
dnsPolicy: ClusterFirstWithHostNet
|
||||
fsGroup: 1000
|
||||
templates:
|
||||
repositories:
|
||||
agentrunV02:
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# R1.6.1.3 任务报告
|
||||
|
||||
## 任务回链
|
||||
|
||||
- MDTODO:`R1.6.1.3`。
|
||||
- GitHub:`pikasTech/unidesk#2129`、`pikasTech/unidesk#2110`。
|
||||
- Target:`NC01`。
|
||||
- 交付边界:本任务只完成生产 release 控制面、只读计划与定向测试,不执行生产 mutation。
|
||||
|
||||
## 已完成
|
||||
|
||||
- `config/pikaoa.yaml#releaseRuntime.targets.NC01` 成为生产运行面唯一 owning selector:
|
||||
- namespace、单副本、resources、附件 PVC、Service、OTel、Prometheus、release source、fresh initializer policy 均由 YAML 声明;
|
||||
- Secret 继续引用既有受控 distribution 与 PK01 PostgreSQL export,不打印值;
|
||||
- 公网 hostname、FRP port、Caddy/TLS 事实保留在同一生产 delivery 声明。
|
||||
- `config/platform-infra/pipelines-as-code.yaml` 新增独立 `pikaoa-nc01` Repository/consumer:
|
||||
- 只跟随 `release` 与 `refs/unidesk/snapshots/gitea-actions/pikaoa-release-nc01`;
|
||||
- 与 `pikaoa-test-nc01/master` 的 Secret、PipelineRun、GitOps branch、Argo application 完全隔离;
|
||||
- production sourceArtifact 使用 `pikaoa-release-runtime` renderer 生成 `.tekton/pikaoa-nc01-pac.yaml`。
|
||||
- test/release 共用 renderer 已修复真实 PaC 缺陷:
|
||||
- TaskSpec workspace 声明 `mountPath: /workspace`,step 不再手工挂载 workspace,token Secret mounts 保留;
|
||||
- BuildKit metadata 解析允许 JSON 冒号两侧空白,只接受 `containerimage.digest` 的完整 `sha256:<64hex>`;
|
||||
- metadata 不可读时使用本地 registry manifest `Docker-Content-Digest` 受控兜底。
|
||||
- Gitea mirror webhook status 只解析选中 repository 的 credential 与 topology:
|
||||
- `--repo pikaoa-nc01` 不再读取未选 `pikaoa-test-nc01` token;
|
||||
- 现场状态显示 release commit `07a9a9db3cad` 已 committed。
|
||||
|
||||
## 验证证据
|
||||
|
||||
- `bun test scripts/src/pikaoa-test-delivery-renderer.test.ts scripts/src/platform-infra-gitea-config.test.ts scripts/src/platform-infra-pipelines-as-code-bootstrap.test.ts`:`24 pass / 0 fail`。
|
||||
- `platform-infra pipelines-as-code plan --target NC01 --consumer pikaoa-nc01`:识别独立 production Repository、consumer、Tekton 与 Argo 路径。
|
||||
- `platform-infra gitea mirror webhook status --target NC01 --repo pikaoa-nc01`:`READY=true`,release head、Gitea 与 snapshot 均为 `07a9a9db3cad`。
|
||||
- source-artifact `plan/check`:desired hash 已生成;产品 `.tekton/pikaoa-nc01-pac.yaml` 尚未写入,符合本任务禁止直接编辑固定产品主 worktree 的边界。
|
||||
|
||||
## 主代理后续命令
|
||||
|
||||
在 `/root/pikaoa` clean worktree 审核并写入产品 release artifact:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts platform-infra pipelines-as-code source-artifact plan --target NC01 --consumer pikaoa-nc01 --source-worktree /root/pikaoa
|
||||
bun scripts/cli.ts platform-infra pipelines-as-code source-artifact write --target NC01 --consumer pikaoa-nc01 --source-worktree /root/pikaoa --confirm
|
||||
bun scripts/cli.ts platform-infra pipelines-as-code source-artifact check --target NC01 --consumer pikaoa-nc01 --source-worktree /root/pikaoa
|
||||
```
|
||||
|
||||
产品 artifact 提交、正常 release PR merge 后,由主代理执行只读 bootstrap 计划并按正常入口触发交付:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts platform-infra gitea mirror webhook status --target NC01 --repo pikaoa-nc01
|
||||
bun scripts/cli.ts platform-infra pipelines-as-code plan --target NC01 --consumer pikaoa-nc01
|
||||
bun scripts/cli.ts platform-infra pipelines-as-code status --target NC01 --consumer pikaoa-nc01
|
||||
```
|
||||
|
||||
交付完成后再按原入口验收 `pikaoa` namespace、initializer、API/Worker/Web、附件 PVC、OTel/Prometheus 与 `https://oa.pikapython.com`;不得人工创建 PipelineRun、Argo refresh 或生产 namespace mutation。
|
||||
@@ -145,6 +145,12 @@
|
||||
##### R1.6.1.1 [completed]
|
||||
|
||||
将 PK01 PostgreSQL 逻辑备份正规化为 YAML-first 多数据库 logicalDumps,保留 Sub2API 并新增 PikaOA 独立 dump、timer、retention 与受控状态/可恢复性摘要(pikasTech/unidesk#2115),完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.6.1.1_Task_Report.md)。
|
||||
##### R1.6.1.2
|
||||
|
||||
为 NC01 pikaoa namespace 的生产附件 PVC 建立 YAML-first Restic 离机备份、受控状态与 restore smoke,复用 PK01 /var/backups/unidesk 且失败只告警不阻塞业务,执行记录见 [pikasTech/unidesk#2128](https://github.com/pikasTech/unidesk/issues/2128),完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.6.1.2_Task_Report.md)。
|
||||
##### R1.6.1.3 [completed]
|
||||
|
||||
建立仅跟随产品 release 的 PikaOA 生产 PaC/Tekton/GitOps/Argo lane,以 fresh initializer 在 NC01 pikaoa namespace 启动 API/Worker/Web/附件 PVC,接入 PK01 host PostgreSQL、OTel/Prometheus 和 oa.pikapython.com TLS 公网入口,执行记录见 [pikasTech/unidesk#2129](https://github.com/pikasTech/unidesk/issues/2129),完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.6.1.3_Task_Report.md)。
|
||||
### R1.7
|
||||
|
||||
通过 CLI、OTel、Prometheus 和 https://oa.pikapython.com 桌面/移动原入口验证完整主路径,依赖 R1.6,完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.7_Task_Report.md)。
|
||||
|
||||
@@ -9,13 +9,13 @@ function pacDocument(): Record<string, unknown> {
|
||||
return Bun.YAML.parse(readFileSync(resolve(import.meta.dir, "../../config/platform-infra/pipelines-as-code.yaml"), "utf8")) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function rendererBinding(): Parameters<typeof renderPikaoaTestDesiredPipeline>[0] {
|
||||
function rendererBinding(id = "pikaoa-test-nc01"): Parameters<typeof renderPikaoaTestDesiredPipeline>[0] {
|
||||
const document = pacDocument();
|
||||
const repositories = document.repositories as Array<Record<string, unknown>>;
|
||||
const consumers = document.consumers as Array<Record<string, unknown>>;
|
||||
const repository = repositories.find((item) => item.id === "pikaoa-test-nc01");
|
||||
const consumer = consumers.find((item) => item.id === "pikaoa-test-nc01");
|
||||
if (repository === undefined || consumer === undefined) throw new Error("PikaOA test PaC fixture is required");
|
||||
const consumer = consumers.find((item) => item.id === id);
|
||||
const repository = repositories.find((item) => item.id === consumer?.repositoryRef);
|
||||
if (repository === undefined || consumer === undefined) throw new Error(`PikaOA PaC fixture is required: ${id}`);
|
||||
return { consumer, repository } as Parameters<typeof renderPikaoaTestDesiredPipeline>[0];
|
||||
}
|
||||
|
||||
@@ -34,12 +34,31 @@ describe("PikaOA test PaC renderer", () => {
|
||||
const repository = repositories.find((item) => item.id === "pikaoa-test-nc01");
|
||||
const testConsumer = consumers.find((item) => item.id === "pikaoa-test-nc01");
|
||||
const productionConsumer = consumers.find((item) => item.id === "pikaoa-nc01");
|
||||
const productionRepository = repositories.find((item) => item.id === "pikaoa-nc01");
|
||||
expect((repository?.params as Record<string, unknown>).source_branch).toBe("master");
|
||||
expect((testConsumer?.sourceArtifact as Record<string, unknown>).pipelineRunPath).toBe(".tekton/pikaoa-test-nc01-pac.yaml");
|
||||
expect((testConsumer?.sourceArtifact as Record<string, unknown>).renderer).toBe("pikaoa-test-runtime");
|
||||
expect((productionConsumer?.params as Record<string, unknown>).source_branch).toBe("release");
|
||||
expect((productionConsumer?.params as Record<string, unknown>).source_snapshot_prefix).toBe("refs/unidesk/snapshots/gitea-actions/pikaoa-release-nc01");
|
||||
expect(productionConsumer?.sourceArtifact).toBeUndefined();
|
||||
expect(productionConsumer?.repositoryRef).toBe("pikaoa-nc01");
|
||||
expect((productionRepository?.params as Record<string, unknown>).source_branch).toBe("release");
|
||||
expect((productionConsumer?.sourceArtifact as Record<string, unknown>).renderer).toBe("pikaoa-release-runtime");
|
||||
expect((productionConsumer?.sourceArtifact as Record<string, unknown>).configRef).toBe("config/pikaoa.yaml#releaseRuntime.targets.NC01");
|
||||
});
|
||||
|
||||
test("renders release runtime from its isolated owning selector", () => {
|
||||
const source = sourceFixture();
|
||||
try {
|
||||
const rendered = renderPikaoaTestDesiredPipeline(rendererBinding("pikaoa-nc01"), source);
|
||||
expect(rendered.configRef).toBe("config/pikaoa.yaml#releaseRuntime.targets.NC01");
|
||||
const serialized = JSON.stringify(rendered.pipeline);
|
||||
expect(serialized).toContain("pikaoa-nc01-pac");
|
||||
expect(serialized).toContain("refs/unidesk/snapshots/gitea-actions/pikaoa-release-nc01");
|
||||
expect(serialized).toContain("pac-gitea-pikaoa-nc01");
|
||||
expect(serialized).not.toContain("pikaoa-test-nc01-pac");
|
||||
} finally {
|
||||
rmSync(source, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("renders parallel digest builds and a PreSync fresh database initializer", () => {
|
||||
@@ -65,15 +84,30 @@ describe("PikaOA test PaC renderer", () => {
|
||||
expect(JSON.stringify(task)).not.toContain("source-token");
|
||||
const buildScript = (((task?.taskSpec as Record<string, unknown>).steps as Array<Record<string, unknown>>)[0]?.script) as string;
|
||||
expect(buildScript).toContain('"containerimage.digest"[[:space:]]*:[[:space:]]*"\\(sha256:[0-9a-f]\\{64\\}\\)"');
|
||||
expect(buildScript).toContain(".*/\\1/p");
|
||||
expect(buildScript).not.toContain("\u0001");
|
||||
expect(buildScript).toContain("Docker-Content-Digest:");
|
||||
expect(buildScript).toContain("build_status=$?");
|
||||
expect(buildScript).toContain('"code":"buildctl-nonzero-artifact-verified"');
|
||||
expect(buildScript).toContain('"artifactVerified":true');
|
||||
}
|
||||
const digest = "sha256:" + "a".repeat(64);
|
||||
const metadata = JSON.stringify({ "containerimage.digest": digest }, null, 2);
|
||||
const parsedDigest = /"containerimage\.digest"\s*:\s*"(sha256:[0-9a-f]{64})"/u.exec(metadata)?.[1];
|
||||
expect(parsedDigest).toBe(digest);
|
||||
const sourceTask = tasks.find((item) => item.name === "source");
|
||||
expect(JSON.stringify(sourceTask)).toContain("source-token");
|
||||
expect(JSON.stringify(sourceTask)).toContain("GIT_ASKPASS");
|
||||
expect(JSON.stringify(sourceTask)).toContain("$(params.gitops-secret-name)");
|
||||
for (const task of tasks) {
|
||||
const taskSpec = task.taskSpec as Record<string, unknown> | undefined;
|
||||
if (taskSpec === undefined) continue;
|
||||
expect(taskSpec.workspaces).toEqual([{ name: "workspace", mountPath: "/workspace" }]);
|
||||
const steps = taskSpec.steps as Array<Record<string, unknown>>;
|
||||
for (const step of steps) {
|
||||
expect((step.volumeMounts as Array<Record<string, unknown>> | undefined)?.some((mount) => mount.name === "workspace") ?? false).toBe(false);
|
||||
}
|
||||
}
|
||||
const publish = tasks.find((item) => item.name === "gitops-publish");
|
||||
expect(publish?.runAfter).toEqual(["build-api", "build-worker", "build-web"]);
|
||||
const script = ((((publish?.taskSpec as Record<string, unknown>).steps as Array<Record<string, unknown>>)[0]?.script) as string);
|
||||
|
||||
@@ -3,6 +3,8 @@ import { resolve } from "node:path";
|
||||
|
||||
import {
|
||||
pikaoaTestRuntimeConfigRef,
|
||||
pikaoaReleaseRuntimeConfigRef,
|
||||
resolvePikaoaReleaseRuntimeTargetByConfigRef,
|
||||
resolvePikaoaTestRuntimeTargetByConfigRef,
|
||||
type TestTargetSpec,
|
||||
} from "./pikaoa-test-target";
|
||||
@@ -37,10 +39,13 @@ export interface PikaoaTestRenderedPipeline {
|
||||
}
|
||||
|
||||
export function renderPikaoaTestDesiredPipeline(binding: PikaoaTestRendererBinding, sourceWorktree: string): PikaoaTestRenderedPipeline {
|
||||
const target = resolvePikaoaTestRuntimeTargetByConfigRef(binding.consumer.sourceArtifact.configRef);
|
||||
const configRef = pikaoaTestRuntimeConfigRef(target.id);
|
||||
if (binding.consumer.sourceArtifact.renderer !== "pikaoa-test-runtime" || binding.consumer.sourceArtifact.mode !== "embedded-pipeline-spec") {
|
||||
throw new Error("pikaoa-test-runtime requires embedded-pipeline-spec");
|
||||
const release = binding.consumer.sourceArtifact.renderer === "pikaoa-release-runtime";
|
||||
const target = release
|
||||
? resolvePikaoaReleaseRuntimeTargetByConfigRef(binding.consumer.sourceArtifact.configRef)
|
||||
: resolvePikaoaTestRuntimeTargetByConfigRef(binding.consumer.sourceArtifact.configRef);
|
||||
const configRef = release ? pikaoaReleaseRuntimeConfigRef(target.id) : pikaoaTestRuntimeConfigRef(target.id);
|
||||
if ((!release && binding.consumer.sourceArtifact.renderer !== "pikaoa-test-runtime") || binding.consumer.sourceArtifact.mode !== "embedded-pipeline-spec") {
|
||||
throw new Error(`${binding.consumer.sourceArtifact.renderer} requires embedded-pipeline-spec`);
|
||||
}
|
||||
if (binding.consumer.sourceArtifact.configRef !== configRef) {
|
||||
throw new Error(`sourceArtifact.configRef must equal resolved owning selector ${configRef}`);
|
||||
@@ -59,6 +64,10 @@ export function pikaoaTestSourceWorktreeRemote(targetId: string): string {
|
||||
return resolvePikaoaTestRuntimeTargetByConfigRef(pikaoaTestRuntimeConfigRef(targetId)).source.worktreeRemote;
|
||||
}
|
||||
|
||||
export function pikaoaReleaseSourceWorktreeRemote(targetId: string): string {
|
||||
return resolvePikaoaReleaseRuntimeTargetByConfigRef(pikaoaReleaseRuntimeConfigRef(targetId)).source.worktreeRemote;
|
||||
}
|
||||
|
||||
function pipeline(target: TestTargetSpec): Record<string, unknown> {
|
||||
const runtimeManifest = renderGitOpsManifest(target);
|
||||
return {
|
||||
@@ -280,7 +289,7 @@ git -C /workspace/gitops add -- ${shellSingleQuote(target.gitops.manifestPath)}
|
||||
if git -C /workspace/gitops diff --cached --quiet; then
|
||||
changed=false
|
||||
else
|
||||
git -C /workspace/gitops commit -m "chore(gitops): deploy pikaoa test $SOURCE_COMMIT"
|
||||
git -C /workspace/gitops commit -m "chore(gitops): deploy pikaoa $SOURCE_COMMIT"
|
||||
git -C /workspace/gitops push "$(params.gitops-write-url)" HEAD:${shellSingleQuote(target.gitops.branch)}
|
||||
changed=true
|
||||
fi
|
||||
@@ -329,7 +338,7 @@ function renderGitOpsManifest(target: TestTargetSpec): string {
|
||||
|
||||
function assertBinding(target: TestTargetSpec, binding: PikaoaTestRendererBinding): void {
|
||||
const expected: Readonly<Record<string, string>> = {
|
||||
id: "pikaoa-test-nc01",
|
||||
id: target.source.branch === "release" ? "pikaoa-nc01" : "pikaoa-test-nc01",
|
||||
node: target.node,
|
||||
lane: target.ci.lane,
|
||||
namespace: target.ci.namespace,
|
||||
@@ -351,7 +360,7 @@ function assertBinding(target: TestTargetSpec, binding: PikaoaTestRendererBindin
|
||||
serviceAccount: binding.repository.params.service_account,
|
||||
};
|
||||
for (const [key, value] of Object.entries(expected)) {
|
||||
if (actual[key] !== value) throw new Error(`pikaoa-test-runtime binding ${key} must equal owning YAML value ${value}`);
|
||||
if (actual[key] !== value) throw new Error(`PikaOA runtime binding ${key} must equal owning YAML value ${value}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,6 @@ export interface PikaoaTestManifestOptions {
|
||||
initializerHook?: "argo-presync";
|
||||
}
|
||||
|
||||
const MANAGED_BY = "unidesk-pikaoa-test-target";
|
||||
const TEST_RUNTIME_LABEL = "pikaoa.unidesk.io/test-runtime";
|
||||
const TARGET_LABEL = "pikaoa.unidesk.io/target";
|
||||
const INSTANCE_LABEL = "pikaoa.unidesk.io/instance";
|
||||
|
||||
@@ -97,6 +95,7 @@ export function renderPikaoaTestRuntimeManifest(
|
||||
containers: [{
|
||||
name: "initializer", image: context.initializerImage, imagePullPolicy: target.source.pullPolicy,
|
||||
command: target.initializer.command,
|
||||
resources: target.runtime.resources.initializer,
|
||||
env: [{ name: "PIKAOA_CONFIG", value: "/etc/pikaoa/pikaoa.yaml" }],
|
||||
volumeMounts: [{ name: "runtime", mountPath: "/etc/pikaoa", readOnly: true }],
|
||||
}],
|
||||
@@ -136,11 +135,12 @@ export function pikaoaManifestForStep(manifest: Record<string, unknown>[], step:
|
||||
}
|
||||
|
||||
export function ownershipLabels(target: TestTargetSpec, context: PikaoaTestRenderContext): Record<string, string> {
|
||||
const release = target.source.branch === "release";
|
||||
return {
|
||||
"app.kubernetes.io/name": "pikaoa",
|
||||
"app.kubernetes.io/part-of": "pikaoa-test-runtime",
|
||||
"app.kubernetes.io/managed-by": MANAGED_BY,
|
||||
[TEST_RUNTIME_LABEL]: "true",
|
||||
"app.kubernetes.io/part-of": release ? "pikaoa-release-runtime" : "pikaoa-test-runtime",
|
||||
"app.kubernetes.io/managed-by": target.fieldManager,
|
||||
[release ? "pikaoa.unidesk.io/release-runtime" : "pikaoa.unidesk.io/test-runtime"]: "true",
|
||||
[TARGET_LABEL]: target.id,
|
||||
[INSTANCE_LABEL]: context.instanceId,
|
||||
};
|
||||
@@ -191,13 +191,13 @@ function deployment(
|
||||
return {
|
||||
apiVersion: "apps/v1", kind: "Deployment", metadata: { name: `pikaoa-${component}`, namespace: context.namespace, labels: { ...labels, ...selector } },
|
||||
spec: {
|
||||
replicas: 1, selector: { matchLabels: selector },
|
||||
replicas: target.runtime.replicas[component], selector: { matchLabels: selector },
|
||||
template: {
|
||||
metadata: { labels: { ...labels, ...selector, ...podLabels }, annotations },
|
||||
spec: {
|
||||
...(component === "api" ? { securityContext: { fsGroup: target.runtime.attachment.fsGroup } } : {}),
|
||||
containers: [{
|
||||
name: component, image, imagePullPolicy: target.source.pullPolicy, env,
|
||||
name: component, image, imagePullPolicy: target.source.pullPolicy, env, resources: target.runtime.resources[component],
|
||||
ports: [{ name: component === "worker" ? "metrics" : "http", containerPort: port }],
|
||||
volumeMounts: [
|
||||
...(component === "web" ? [] : [{ name: "runtime", mountPath: "/etc/pikaoa", readOnly: true }]),
|
||||
@@ -241,7 +241,7 @@ function runtimeSecretData(target: TestTargetSpec, context: PikaoaTestRenderCont
|
||||
` storageRoot: ${JSON.stringify(target.runtime.attachment.storageRoot)}`,
|
||||
` maxBytes: ${target.runtime.attachment.maxBytes}`,
|
||||
"observability:",
|
||||
` serviceName: pikaoa-test-${context.instanceId}`,
|
||||
` serviceName: ${target.source.branch === "release" ? "pikaoa" : `pikaoa-test-${context.instanceId}`}`,
|
||||
` otlpEndpoint: ${JSON.stringify(target.observability.otlpEndpoint)}`,
|
||||
`shutdownTimeout: ${target.runtime.shutdownTimeout}`,
|
||||
"",
|
||||
|
||||
@@ -118,6 +118,8 @@ export interface TestTargetSpec {
|
||||
connection: SecretSourceSpec;
|
||||
};
|
||||
runtime: {
|
||||
replicas: { api: number; worker: number; web: number };
|
||||
resources: Record<"api" | "worker" | "web" | "initializer", Record<string, unknown>>;
|
||||
secretName: string;
|
||||
apiServiceName: string;
|
||||
apiPort: number;
|
||||
@@ -318,8 +320,8 @@ function readSelection(options: TestTargetOptions): Selection {
|
||||
};
|
||||
}
|
||||
|
||||
function parseTarget(id: string, root: Record<string, unknown>, configLabel: string): TestTargetSpec {
|
||||
const path = `${configLabel}.testRuntime.targets.${id}`;
|
||||
function parseTarget(id: string, root: Record<string, unknown>, configLabel: string, runtimeSection = "testRuntime"): TestTargetSpec {
|
||||
const path = `${configLabel}.${runtimeSection}.targets.${id}`;
|
||||
simpleId(id, "target id");
|
||||
const source = record(root.source, `${path}.source`);
|
||||
const images = record(source.images, `${path}.source.images`);
|
||||
@@ -332,6 +334,8 @@ function parseTarget(id: string, root: Record<string, unknown>, configLabel: str
|
||||
const gitopsAuthor = record(gitops.author, `${path}.gitops.author`);
|
||||
const database = record(root.database, `${path}.database`);
|
||||
const runtime = record(root.runtime, `${path}.runtime`);
|
||||
const replicas = record(runtime.replicas, `${path}.runtime.replicas`);
|
||||
const resources = record(runtime.resources, `${path}.runtime.resources`);
|
||||
const attachment = record(runtime.attachment, `${path}.runtime.attachment`);
|
||||
const outbox = record(runtime.outbox, `${path}.runtime.outbox`);
|
||||
const administrator = record(runtime.administrator, `${path}.runtime.administrator`);
|
||||
@@ -426,6 +430,17 @@ function parseTarget(id: string, root: Record<string, unknown>, configLabel: str
|
||||
connection: secretSource(database.connection, "database.connection", `${path}.database.connection`, true),
|
||||
},
|
||||
runtime: {
|
||||
replicas: {
|
||||
api: positiveInteger(replicas.api, `${path}.runtime.replicas.api`, 20),
|
||||
worker: positiveInteger(replicas.worker, `${path}.runtime.replicas.worker`, 20),
|
||||
web: positiveInteger(replicas.web, `${path}.runtime.replicas.web`, 20),
|
||||
},
|
||||
resources: {
|
||||
api: record(resources.api, `${path}.runtime.resources.api`),
|
||||
worker: record(resources.worker, `${path}.runtime.resources.worker`),
|
||||
web: record(resources.web, `${path}.runtime.resources.web`),
|
||||
initializer: record(resources.initializer, `${path}.runtime.resources.initializer`),
|
||||
},
|
||||
secretName: kubernetesName(nonEmpty(runtime.secretName, `${path}.runtime.secretName`), `${path}.runtime.secretName`, 63),
|
||||
apiServiceName: kubernetesName(nonEmpty(runtime.apiServiceName, `${path}.runtime.apiServiceName`), `${path}.runtime.apiServiceName`, 63),
|
||||
apiPort: positiveInteger(runtime.apiPort, `${path}.runtime.apiPort`, 65_535),
|
||||
@@ -505,7 +520,21 @@ export function resolvePikaoaTestRuntimeTargetByConfigRef(configRef: string): Te
|
||||
if (resolved.file !== "config/pikaoa.yaml" || match === null) {
|
||||
throw new Error("PikaOA test runtime configRef must select config/pikaoa.yaml#testRuntime.targets.<id>");
|
||||
}
|
||||
return parseTarget(match[1]!, record(resolved.value, configRef), resolved.file);
|
||||
return parseTarget(match[1]!, record(resolved.value, configRef), resolved.file, "testRuntime");
|
||||
}
|
||||
|
||||
export function pikaoaReleaseRuntimeConfigRef(targetId: string): string {
|
||||
simpleId(targetId, "PikaOA release runtime target id");
|
||||
return `config/pikaoa.yaml#releaseRuntime.targets.${targetId}`;
|
||||
}
|
||||
|
||||
export function resolvePikaoaReleaseRuntimeTargetByConfigRef(configRef: string): TestTargetSpec {
|
||||
const resolved = resolveConfigRef(configRef, "PikaOA release runtime configRef");
|
||||
const match = /^releaseRuntime\.targets\.([A-Za-z0-9._-]+)$/u.exec(resolved.fragment);
|
||||
if (resolved.file !== "config/pikaoa.yaml" || match === null) {
|
||||
throw new Error("PikaOA release runtime configRef must select config/pikaoa.yaml#releaseRuntime.targets.<id>");
|
||||
}
|
||||
return parseTarget(match[1]!, record(resolved.value, configRef), resolved.file, "releaseRuntime");
|
||||
}
|
||||
|
||||
function planPayload(options: TestTargetOptions, selection: Selection): Record<string, unknown> {
|
||||
|
||||
@@ -103,6 +103,25 @@ describe("platform-infra Gitea repository GitHub credentials", () => {
|
||||
expect(JSON.stringify(result)).not.toContain(config.sourceAuthority.credentials.github.sourceRef);
|
||||
});
|
||||
|
||||
test("production PikaOA status scope never reads the unselected master credential", () => {
|
||||
const config = readGiteaConfig();
|
||||
const production = config.sourceAuthority.repositories.find((repo) => repo.key === "pikaoa-nc01");
|
||||
expect(production).toBeDefined();
|
||||
const requested: string[] = [];
|
||||
const result = buildGiteaMirrorBootstrapCredentialPreflight(config, [production!], (credential) => {
|
||||
requested.push(credential.gitFetchCredential.secretRef.key);
|
||||
return { [credential.sourceKey]: "fixture-release-token" };
|
||||
});
|
||||
|
||||
expect(result.blockers).toEqual([]);
|
||||
expect(requested).toEqual(["github-token-pikainc-pikaoa-release"]);
|
||||
expect(result.credentials).toHaveLength(1);
|
||||
expect(result.credentials[0]).toMatchObject({
|
||||
id: "github-upstream:pikaoa-nc01",
|
||||
valuesPrinted: false,
|
||||
});
|
||||
});
|
||||
|
||||
test("does not inject the NC01 selfmedia token into JD01 bridge containers", () => {
|
||||
const config = readGiteaConfig();
|
||||
const selfmediaTokenEnv = githubTokenEnvNameForSecretKey("github-token-pikainc-selfmedia");
|
||||
|
||||
@@ -1074,7 +1074,9 @@ rows = []
|
||||
for repo in repos:
|
||||
repository = repo["upstream"]["repository"]
|
||||
topology_spec = next((item for item in topology_repositories if isinstance(item, dict) and item.get("repository") == repository), {})
|
||||
token = os.environ[topology_spec.get("githubTokenEnv") or os.environ["UNIDESK_GITEA_GITHUB_DEFAULT_TOKEN_ENV"]]
|
||||
credential_spec = repo.get("githubCredential") or {}
|
||||
token_env = credential_spec.get("tokenEnv") or topology_spec.get("githubTokenEnv") or os.environ["UNIDESK_GITEA_GITHUB_DEFAULT_TOKEN_ENV"]
|
||||
token = os.environ[token_env]
|
||||
branch = repo["upstream"]["branch"]
|
||||
result = request(f"/repos/{repository}/hooks")
|
||||
hooks_result = parse_github_hooks_response(result.get("ok"), result.get("status"), result.get("body"))
|
||||
|
||||
@@ -1182,6 +1182,9 @@ function envVars(gitea: GiteaConfig, target: GiteaTarget): string {
|
||||
function remoteScript(action: "apply" | "status" | "validate" | "mirror-preflight" | "mirror-bootstrap" | "mirror-sync" | "mirror-status" | "mirror-webhook-apply" | "mirror-webhook-status", gitea: GiteaConfig, target: GiteaTarget, manifest: string, options: ApplyOptions | MirrorOptions, params: MirrorRemoteParams = {}): { script: string; payloadSummary: Record<string, unknown> } {
|
||||
const frpcExposure = targetFrpcExposure(gitea, target);
|
||||
const sync = targetWebhookSync(gitea, target);
|
||||
const selectedRepositories = params.repos ?? repositoriesForTarget(gitea, target);
|
||||
const selectedCredentials = githubCredentialsForConsumers(gitea, target, selectedRepositories, false);
|
||||
const defaultCredential = selectedCredentials[0] ?? gitea.sourceAuthority.credentials.github;
|
||||
const env: Record<string, string> = {
|
||||
UNIDESK_GITEA_ACTION: action,
|
||||
UNIDESK_GITEA_TARGET_ID: target.id,
|
||||
@@ -1201,9 +1204,9 @@ function remoteScript(action: "apply" | "status" | "validate" | "mirror-prefligh
|
||||
UNIDESK_GITEA_ROOT_URL: gitea.app.server.rootUrl,
|
||||
UNIDESK_GITEA_GITHUB_PROXY_ENABLED: gitea.sourceAuthority.githubProxy.enabled ? "1" : "0",
|
||||
UNIDESK_GITEA_GITHUB_PROXY_URL: gitea.sourceAuthority.githubProxy.url,
|
||||
UNIDESK_GITEA_GITHUB_SECRET_KEY: gitea.sourceAuthority.credentials.github.gitFetchCredential.secretRef.key,
|
||||
UNIDESK_GITEA_GITHUB_DEFAULT_TOKEN_ENV: githubTokenEnvName(gitea.sourceAuthority.credentials.github.gitFetchCredential.secretRef.key),
|
||||
UNIDESK_GITEA_GITHUB_SECRET_ENV_MAP: githubCredentialsForConsumers(gitea, target, repositoriesForTarget(gitea, target), true)
|
||||
UNIDESK_GITEA_GITHUB_SECRET_KEY: defaultCredential.gitFetchCredential.secretRef.key,
|
||||
UNIDESK_GITEA_GITHUB_DEFAULT_TOKEN_ENV: githubTokenEnvName(defaultCredential.gitFetchCredential.secretRef.key),
|
||||
UNIDESK_GITEA_GITHUB_SECRET_ENV_MAP: selectedCredentials
|
||||
.map((credential) => `${credential.gitFetchCredential.secretRef.key}=${githubTokenEnvName(credential.gitFetchCredential.secretRef.key)}`)
|
||||
.join(","),
|
||||
UNIDESK_GITEA_NO_PROXY: gitea.sourceAuthority.githubProxy.noProxy.join(","),
|
||||
@@ -1702,10 +1705,7 @@ function webhookSyncSummary(gitea: GiteaConfig, target: GiteaTarget): Record<str
|
||||
|
||||
function credentialSummaries(gitea: GiteaConfig, repositories = gitea.sourceAuthority.repositories): Array<Record<string, unknown>> {
|
||||
const adminPath = credentialPath(gitea, gitea.sourceAuthority.credentials.admin.sourceRef);
|
||||
const github = gitea.sourceAuthority.credentials.github;
|
||||
const githubPath = credentialPath(gitea, github.sourceRef);
|
||||
const adminLinePair = existsSync(adminPath) ? parseLinePairCredential(adminPath, gitea.sourceAuthority.credentials.admin) : { username: null, password: null };
|
||||
const githubEnv = existsSync(githubPath) ? parseGithubCredentialFile(githubPath, github) : {};
|
||||
const summaries: Array<Record<string, unknown>> = [
|
||||
{
|
||||
id: "gitea-admin",
|
||||
@@ -1716,36 +1716,8 @@ function credentialSummaries(gitea: GiteaConfig, repositories = gitea.sourceAuth
|
||||
permissionResult: { status: "not-applicable", error: null },
|
||||
valuesPrinted: false,
|
||||
},
|
||||
{
|
||||
id: "github-upstream",
|
||||
sourceRef: github.sourceRef,
|
||||
present: existsSync(githubPath),
|
||||
requiredKeysPresent: Boolean(githubEnv[github.sourceKey]),
|
||||
fingerprint: fingerprintKeys(githubEnv, [github.sourceKey]),
|
||||
permissionResult: existsSync(githubPath) && Boolean(githubEnv[github.sourceKey])
|
||||
? { status: "not-probed", error: null }
|
||||
: { status: "blocked-missing-credential", error: "credential material is absent" },
|
||||
valuesPrinted: false,
|
||||
},
|
||||
];
|
||||
for (const repo of repositories) {
|
||||
const githubOverride = repo.credentialOverride?.github;
|
||||
if (githubOverride === undefined) continue;
|
||||
const sourcePath = credentialPath(gitea, githubOverride.sourceRef);
|
||||
const values = existsSync(sourcePath) ? parseGithubCredentialFile(sourcePath, githubOverride) : {};
|
||||
summaries.push({
|
||||
id: `github-upstream:${repo.key}`,
|
||||
repository: repo.upstream.repository,
|
||||
sourceRef: githubOverride.sourceRef,
|
||||
present: existsSync(sourcePath),
|
||||
requiredKeysPresent: Boolean(values[githubOverride.sourceKey]),
|
||||
fingerprint: fingerprintKeys(values, [githubOverride.sourceKey]),
|
||||
permissionResult: existsSync(sourcePath) && Boolean(values[githubOverride.sourceKey])
|
||||
? { status: "not-probed", permissions: githubOverride.permissions, error: null }
|
||||
: { status: "blocked-missing-credential", permissions: githubOverride.permissions, error: "credential material is absent" },
|
||||
valuesPrinted: false,
|
||||
});
|
||||
}
|
||||
summaries.push(...buildGiteaMirrorBootstrapCredentialPreflight(gitea, repositories).credentials);
|
||||
return summaries;
|
||||
}
|
||||
|
||||
@@ -1803,7 +1775,7 @@ function githubCredentialsForConsumers(
|
||||
repositories: GiteaMirrorRepository[],
|
||||
includeHookOwnerCredentials: boolean,
|
||||
): GiteaGithubCredential[] {
|
||||
const credentials = [gitea.sourceAuthority.credentials.github, ...repositories.flatMap((repo) => repo.credentialOverride?.github ?? [])];
|
||||
const credentials = repositories.map((repo) => githubCredentialForRepo(gitea, repo));
|
||||
if (includeHookOwnerCredentials
|
||||
&& gitea.sourceAuthority.webhookSync.hookReconcile.ownerTargetId.toLowerCase() === target.id.toLowerCase()) {
|
||||
credentials.push(...gitea.sourceAuthority.repositories.flatMap((repo) => {
|
||||
|
||||
@@ -133,7 +133,7 @@ test("unselected parse and repository errors remain warnings while selected obje
|
||||
evidence: expect.stringMatching(/^type=string,length=\d+,sha256=[0-9a-f]{64}$/u),
|
||||
});
|
||||
expect(validPacConsumers(isolated).some((consumer) => consumer.repositoryRef === "pikaoa-test-nc01")).toBe(false);
|
||||
expect(() => parsePacConfigDocument(invalidRepositoryDocument, { consumerId: "pikaoa-nc01" })).toThrow("cloneUrl");
|
||||
expect(() => parsePacConfigDocument(invalidRepositoryDocument, { consumerId: "pikaoa-nc01" })).not.toThrow();
|
||||
|
||||
const duplicateSelectedRepository = pacConfigDocument();
|
||||
const repositories = duplicateSelectedRepository.repositories as Array<Record<string, unknown>>;
|
||||
|
||||
@@ -19,10 +19,10 @@ import { stableJsonSha256, stableJsonValue } from "./stable-json";
|
||||
import { pipelineProvenanceAnnotations, pipelineProvenanceFromManifest, withPipelineProvenanceAnnotations } from "./pipeline-provenance";
|
||||
import { renderSub2RankDesiredPipeline, sub2RankOwningSourceRemote } from "./platform-infra-sub2rank-pipeline";
|
||||
import { renderSelfMediaDesiredPipeline, selfMediaSourceWorktreeRemote } from "./selfmedia-delivery-renderer";
|
||||
import { renderPikaoaTestDesiredPipeline, pikaoaTestSourceWorktreeRemote } from "./pikaoa-test-delivery-renderer";
|
||||
import { renderPikaoaTestDesiredPipeline, pikaoaReleaseSourceWorktreeRemote, pikaoaTestSourceWorktreeRemote } from "./pikaoa-test-delivery-renderer";
|
||||
|
||||
export type PacSourceArtifactMode = "embedded-pipeline-spec" | "remote-pipeline-annotation";
|
||||
export type PacSourceArtifactRenderer = "agentrun-control-plane" | "hwlab-runtime-lane" | "sub2rank-platform-service" | "selfmedia-runtime" | "pikaoa-test-runtime";
|
||||
export type PacSourceArtifactRenderer = "agentrun-control-plane" | "hwlab-runtime-lane" | "sub2rank-platform-service" | "selfmedia-runtime" | "pikaoa-test-runtime" | "pikaoa-release-runtime";
|
||||
export type PacSourceArtifactAction = "plan" | "check" | "write" | "status" | "verify-runtime";
|
||||
export type PacSourceArtifactRuntimeAlignment = "not-requested" | "aligned" | "drifted" | "missing" | "unavailable";
|
||||
|
||||
@@ -593,7 +593,7 @@ function renderPikaoaTestPipeline(binding: PacSourceArtifactBinding, sourceWorkt
|
||||
provenance: {
|
||||
configRef: rendered.configRef,
|
||||
effectiveConfigSha256: rendered.effectiveConfigSha256,
|
||||
renderer: "pikaoa-test-runtime",
|
||||
renderer: binding.consumer.sourceArtifact.renderer,
|
||||
mode: "embedded-pipeline-spec",
|
||||
},
|
||||
};
|
||||
@@ -852,7 +852,7 @@ function pipelineRunParams(binding: PacSourceArtifactBinding, desiredSpec: Recor
|
||||
if (name === "source-stage-ref") return { name, value: "{{ source_snapshot_prefix }}/{{ revision }}" };
|
||||
if (name === "git-url") return { name, value: "{{ repo_url }}" };
|
||||
const repositoryParam = name.replaceAll("-", "_");
|
||||
if ((binding.consumer.sourceArtifact.renderer === "selfmedia-runtime" || binding.consumer.sourceArtifact.renderer === "pikaoa-test-runtime") && Object.prototype.hasOwnProperty.call(param, "default")) {
|
||||
if ((binding.consumer.sourceArtifact.renderer === "selfmedia-runtime" || binding.consumer.sourceArtifact.renderer === "pikaoa-test-runtime" || binding.consumer.sourceArtifact.renderer === "pikaoa-release-runtime") && Object.prototype.hasOwnProperty.call(param, "default")) {
|
||||
return { name, value: param.default };
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(binding.repository.params, repositoryParam)) {
|
||||
@@ -867,7 +867,7 @@ export function pipelineRunWorkspaces(binding: PacSourceArtifactBinding, desired
|
||||
if (desiredSpec.workspaces === undefined) return [];
|
||||
return arrayRecords(desiredSpec.workspaces, "Pipeline.spec.workspaces").map((workspace) => {
|
||||
const name = requiredString(workspace.name, "Pipeline.spec.workspaces[].name");
|
||||
if (name === "source" || (name === "workspace" && binding.consumer.sourceArtifact.renderer === "pikaoa-test-runtime")) {
|
||||
if (name === "source" || (name === "workspace" && (binding.consumer.sourceArtifact.renderer === "pikaoa-test-runtime" || binding.consumer.sourceArtifact.renderer === "pikaoa-release-runtime"))) {
|
||||
return {
|
||||
name,
|
||||
volumeClaimTemplate: {
|
||||
@@ -946,6 +946,7 @@ function owningSourceRemote(binding: PacSourceArtifactBinding): string {
|
||||
if (binding.consumer.sourceArtifact.renderer === "sub2rank-platform-service") return sub2RankOwningSourceRemote();
|
||||
if (binding.consumer.sourceArtifact.renderer === "selfmedia-runtime") return selfMediaSourceWorktreeRemote(binding.consumer.node);
|
||||
if (binding.consumer.sourceArtifact.renderer === "pikaoa-test-runtime") return pikaoaTestSourceWorktreeRemote(binding.consumer.node);
|
||||
if (binding.consumer.sourceArtifact.renderer === "pikaoa-release-runtime") return pikaoaReleaseSourceWorktreeRemote(binding.consumer.node);
|
||||
if (!isHwlabRuntimeLane(binding.consumer.lane)) throw new PacSourceArtifactError("owning-source-lane-unresolved");
|
||||
return hwlabRuntimeLaneSpecForNode(binding.consumer.lane, binding.consumer.node).gitUrl;
|
||||
}
|
||||
@@ -1065,7 +1066,8 @@ export function pacSourceArtifactProvenanceFromManifest(manifest: Record<string,
|
||||
&& provenance.renderer !== "hwlab-runtime-lane"
|
||||
&& provenance.renderer !== "sub2rank-platform-service"
|
||||
&& provenance.renderer !== "selfmedia-runtime"
|
||||
&& provenance.renderer !== "pikaoa-test-runtime") return null;
|
||||
&& provenance.renderer !== "pikaoa-test-runtime"
|
||||
&& provenance.renderer !== "pikaoa-release-runtime") return null;
|
||||
if (provenance.mode !== "embedded-pipeline-spec" && provenance.mode !== "remote-pipeline-annotation") return null;
|
||||
return provenance as PacSourceArtifactProvenance;
|
||||
}
|
||||
|
||||
@@ -801,7 +801,7 @@ function parseConsumerDeliveryProvenance(value: Record<string, unknown>, path: s
|
||||
|
||||
function parseSourceArtifact(value: Record<string, unknown>, path: string): PacSourceArtifactSpec {
|
||||
const mode = y.enumField(value, "mode", path, ["embedded-pipeline-spec", "remote-pipeline-annotation"] as const);
|
||||
const renderer = y.enumField(value, "renderer", path, ["agentrun-control-plane", "hwlab-runtime-lane", "sub2rank-platform-service", "selfmedia-runtime", "pikaoa-test-runtime"] as const);
|
||||
const renderer = y.enumField(value, "renderer", path, ["agentrun-control-plane", "hwlab-runtime-lane", "sub2rank-platform-service", "selfmedia-runtime", "pikaoa-test-runtime", "pikaoa-release-runtime"] as const);
|
||||
const pipelineRunPath = sourceArtifactPath(y.stringField(value, "pipelineRunPath", path), `${path}.pipelineRunPath`);
|
||||
const pipelinePath = value.pipelinePath === undefined ? null : sourceArtifactPath(y.stringField(value, "pipelinePath", path), `${path}.pipelinePath`);
|
||||
const taskRunTemplate = y.objectField(value, "taskRunTemplate", path);
|
||||
@@ -812,6 +812,7 @@ function parseSourceArtifact(value: Record<string, unknown>, path: string): PacS
|
||||
if (renderer === "sub2rank-platform-service" && mode !== "embedded-pipeline-spec") throw new Error(`${path}.renderer sub2rank-platform-service requires embedded-pipeline-spec`);
|
||||
if (renderer === "selfmedia-runtime" && mode !== "embedded-pipeline-spec") throw new Error(`${path}.renderer selfmedia-runtime requires embedded-pipeline-spec`);
|
||||
if (renderer === "pikaoa-test-runtime" && mode !== "embedded-pipeline-spec") throw new Error(`${path}.renderer pikaoa-test-runtime requires embedded-pipeline-spec`);
|
||||
if (renderer === "pikaoa-release-runtime" && mode !== "embedded-pipeline-spec") throw new Error(`${path}.renderer pikaoa-release-runtime requires embedded-pipeline-spec`);
|
||||
return {
|
||||
mode,
|
||||
renderer,
|
||||
|
||||
Reference in New Issue
Block a user