diff --git a/config/hwlab-node-lanes.yaml b/config/hwlab-node-lanes.yaml index 2c68519b..c51ad449 100644 --- a/config/hwlab-node-lanes.yaml +++ b/config/hwlab-node-lanes.yaml @@ -299,7 +299,7 @@ lanes: install: stateDir: /tmp/hwlab-release-source-workspace-bootstrap sourceAuthority: - giteaMirrorRepoKey: hwlab-nc01-v03 + giteaMirrorRepoKey: hwlab-nc01-production cicdRepo: /root/hwlab-release-cicd.git cicdRepoLock: /tmp/hwlab-release-cicd-repo.lock app: hwlab-node-production diff --git a/config/platform-infra/gitea.yaml b/config/platform-infra/gitea.yaml index d6dd21be..9f0eaa78 100644 --- a/config/platform-infra/gitea.yaml +++ b/config/platform-infra/gitea.yaml @@ -375,6 +375,28 @@ sourceAuthority: readUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git configRef: config/hwlab-node-lanes.yaml#lanes.v03.targets.NC01.git.readUrl disposition: replaced-by-gitea + - key: hwlab-nc01-production + targetId: NC01 + upstream: + repository: pikasTech/HWLAB + cloneUrl: https://github.com/pikasTech/HWLAB.git + branch: release + gitea: + owner: mirrors + name: pikasTech-HWLAB + mirrorMode: controlled-push + publicRead: true + readUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git + gitops: + branch: release-gitops + flushDisposition: retained-for-gitops-flush + snapshot: + naming: gitea-actions-immutable-source + prefix: refs/unidesk/snapshots/gitea-actions/hwlab-node-runtime/release + legacyGitMirror: + readUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git + configRef: config/hwlab-node-lanes.yaml#lanes.production.targets.NC01.git.readUrl + disposition: replaced-by-gitea - key: selfmedia-nc01 targetId: NC01 credentialOverride: diff --git a/config/platform-infra/pipelines-as-code.yaml b/config/platform-infra/pipelines-as-code.yaml index e2572e57..37d36daf 100644 --- a/config/platform-infra/pipelines-as-code.yaml +++ b/config/platform-infra/pipelines-as-code.yaml @@ -214,6 +214,10 @@ repositories: variables: NODE: NC01 LANE: v03 + - extends: templates.repositories.hwlabProduction + variables: + NODE: NC01 + LANE: production - id: selfmedia-nc01 name: selfmedia-nc01 namespace: selfmedia-ci @@ -434,20 +438,11 @@ consumers: NODE: NC01 LANE: production id: hwlab-nc01-production - repositoryRef: hwlab-nc01-v03 + repositoryRef: hwlab-nc01-production lane: production pipeline: hwlab-nc01-production-ci-image-publish pipelineRunPrefix: hwlab-nc01-production-ci-poll argoApplication: hwlab-node-production - params: - source_branch: release - gitops_branch: release-gitops - source_snapshot_prefix: refs/unidesk/snapshots/gitea-actions/hwlab-node-runtime/release - pipeline_name: hwlab-nc01-production-ci-image-publish - pipeline_run_prefix: hwlab-nc01-production-ci-poll - service_account: hwlab-nc01-production-tekton-runner - lane: production - runtime_namespace: hwlab-production sourceArtifact: mode: remote-pipeline-annotation renderer: hwlab-runtime-lane @@ -758,6 +753,34 @@ templates: node: "${NODE}" lane: v03 runtime_namespace: hwlab-v03 + hwlabProduction: + id: "hwlab-${nodeLower}-production" + name: "hwlab-${nodeLower}-production" + namespace: hwlab-ci + providerType: gitea + url: https://gitea.pikapython.com/mirrors/pikasTech-HWLAB + cloneUrl: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git + owner: mirrors + repo: pikasTech-HWLAB + secretName: "pac-gitea-hwlab-${nodeLower}-production" + tokenKey: token + webhookSecretKey: webhook.secret + concurrencyLimit: 1 + params: + git_read_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git + git_write_url: http://git-mirror-write.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git + source_branch: release + gitops_branch: release-gitops + source_snapshot_prefix: refs/unidesk/snapshots/gitea-actions/hwlab-node-runtime/release + pipeline_name: "hwlab-${nodeLower}-production-ci-image-publish" + pipeline_run_prefix: "hwlab-${nodeLower}-production-ci-poll" + service_account: "hwlab-${nodeLower}-production-tekton-runner" + workspace_pvc_size: 8Gi + git_ssh_secret: hwlab-git-ssh + pipeline_timeout: 1h0m0s + node: "${NODE}" + lane: production + runtime_namespace: hwlab-production consumers: agentrunV02: id: "agentrun-${nodeLower}-v02" diff --git a/docs/MDTODO/details/pr-merge-driven-automatic-delivery/R7.2_Task_Report.md b/docs/MDTODO/details/pr-merge-driven-automatic-delivery/R7.2_Task_Report.md index 18aeab92..05b7d0a4 100644 --- a/docs/MDTODO/details/pr-merge-driven-automatic-delivery/R7.2_Task_Report.md +++ b/docs/MDTODO/details/pr-merge-driven-automatic-delivery/R7.2_Task_Report.md @@ -19,23 +19,48 @@ - `https://hwlab.pikapython.com` YAML publicExposure; - PostgreSQL database、role、Secret consumer 和 migration ledger; - Kafka client、direct publish、refresh replay、projector 和 live SSE consumer group。 -- production 与 development 共享同一个 HWLAB Gitea PaC Repository CR;两个 lane 使用独立 consumer,并由 consumer params 覆盖 source branch、snapshot、Pipeline 和 runtime identity。 -- 共享 Repository 避免同一镜像仓库重复声明 PaC Repository;独立 consumer 仍保持 production 自动交付路径与 development 完全隔离。 -- `lanes.production.targets.NC01.sourceAuthority.giteaMirrorRepoKey` 已统一为共享 authority `hwlab-nc01-v03`,不再引用不存在的 production repo key。 -- delivery authority parser 以 consumer params 解析 branch 与 snapshot: - - 显式 consumer override 优先; - - 未显式覆盖时继承 Repository 默认; - - 多分支镜像优先匹配同 branch authority; - - 唯一共享镜像允许 production consumer 复用同一 Repository。 +- production 与 development 复用同一个 Gitea 物理仓库 + `mirrors/pikasTech-HWLAB`,但分别声明独立 YAML source identity: + - development:`hwlab-nc01-v03` / `v0.3` / `v0.3-gitops`; + - production:`hwlab-nc01-production` / `release` / `release-gitops`。 +- production PaC consumer 精确引用 `hwlab-nc01-production` repository identity; + branch、snapshot 和 GitOps revision 由该 repository identity 声明, + 不再由 consumer params 覆盖 development identity。 +- `lanes.production.targets.NC01.sourceAuthority.giteaMirrorRepoKey` + 精确引用 `hwlab-nc01-production`。 +- delivery authority parser 已删除同 target/owner/repo 的跨 branch fallback: + - 必须同时精确匹配 target、Gitea owner/name 和 source branch; + - 缺少或冲突时 fail-closed; + - 禁止把 `v0.3` identity 猜作 `release` authority。 - 通用 HWLAB lane parser 和 plan 已投影 migration ledger,并从 owning YAML 动态生成合法 lane 提示。 - `config/platform-infra/kafka.yaml` 已声明 production Cloud API Kafka ACL,未改变 topic、实时和回放架构。 +## 方案依据 + +- OpenGitOps 原则要求 desired state 声明式、版本化且不可变,并由软件代理自动拉取和持续 reconcile;因此 branch identity 与 immutable snapshot 必须由 owning YAML 明确声明,不能依赖运行时猜测。 +- Tekton Pipelines 将 Git revision 作为显式输入: + - source branch 只负责选择受控镜像入口; + - 实际构建继续使用受控 snapshot/commit revision; + - mutable branch 名不直接充当构建 provenance。 +- Argo CD 的 `targetRevision` 明确选择 Git branch、tag 或 commit;production 使用独立 `release-gitops`,避免与 `v0.3-gitops` 共用可变 desired state。 +- Kubernetes 建议以不可变 image digest 或等价内容寻址 revision + 固定工作负载版本: + - Pipeline 继续产出 commit-pinned artifact; + - GitOps/Argo 只消费已固定 revision; + - 禁止从运行面或脏 workspace 构建。 +- 官方参考: + - OpenGitOps principles:; + - Tekton Git resolver revision:; + - Argo CD tracking strategies:; + - Kubernetes image digest:。 + ## Target 原入口证据 以下命令均在 `NC01:/root/unidesk/.worktree/2010-hwlab-release-production-lane` 执行: - `git diff --check`:通过。 - `bun --check`: + - `scripts/src/cicd-delivery-authority.ts`:通过; - `scripts/src/hwlab-fake-model-provider.ts`:通过; - `scripts/src/hwlab-node-lanes.ts`:通过; - `scripts/src/hwlab-node/entry.ts`:通过; @@ -50,19 +75,22 @@ - namespace 为 `hwlab-v03`; - development 配置继续可解析。 - `bun scripts/cli.ts platform-infra pipelines-as-code plan --target NC01 --consumer hwlab-nc01-production`: - - Repository 为共享的 `hwlab-nc01-v03`; + - Repository 为独立 identity `hwlab-nc01-production`; - consumer 为独立的 `hwlab-nc01-production`; - Tekton Pipeline 为 `hwlab-nc01-production-ci-image-publish`; - Argo Application 为 `hwlab-node-production`; - `single-path`、`yaml-source-of-truth`、`public-repository-url`、`gitea-internal-source` 和 `runtime-zero-docker` 均为 `true`。 - `bun scripts/cli.ts platform-infra pipelines-as-code plan --target NC01 --consumer hwlab-nc01-v03`:通过,development consumer 保持独立。 - delivery authority 与 renderer 定向断言通过: - - NC01 只声明一个 HWLAB Gitea repository,key 为 `hwlab-nc01-v03`; - - production 与 v03 的 `repositoryRef` 均为 `hwlab-nc01-v03`; - - production consumer 保持 `hwlab-nc01-production`; - - production branch 为 `release`,snapshot prefix 为 release 独立前缀; - - authority 解析结果为 `pac-pr-merge`; - - feature config renderer 使用 production consumer。 + - NC01 声明两个 HWLAB source identity,分别为 + `hwlab-nc01-v03` 与 `hwlab-nc01-production`; + - 两个 identity 指向同一 Gitea 物理仓库; + - production `repositoryRef` 为 `hwlab-nc01-production`,branch 为 `release`; + - development `repositoryRef` 为 `hwlab-nc01-v03`,branch 为 `v0.3`; + - 两个 lane 的 snapshot prefix 与 GitOps branch 独立; + - production consumer 携带 `v0.3` source branch 查询时返回 + `authority-identity-mismatch`,证明跨 branch fail-closed; + - parser 源码不再包含 `branchAuthorityRepositories` fallback。 - 纯 Kafka 定向断言通过: - `directPublish`、`liveKafkaSse`、`kafkaRefreshReplay` 为 `true`; - `transactionalProjector`、`projectionOutboxRelay`、`projectionRealtime` 为 `false`; @@ -72,7 +100,18 @@ - GitOps branch、runtime namespace、Argo Application、public origin; - PostgreSQL database、role、Secret、migration ledger; - Kafka direct publish、refresh replay 和 live SSE group; - - shared Repository、independent consumer 和各 lane branch 参数。 + - shared physical Gitea repository、independent source/repository/consumer identity。 + +## 验证边界 + +- `platform-infra gitea validate --target NC01` 未能进入本任务新增 identity 的运行面校验: + - 命令先被既有 `agentrun-nc01-release` 的 + `legacyGitMirror.configRef` 缺失阻断; + - 该缺失存在于 `origin/master`,不属于本任务改动; + - 本轮未扩大范围修复该独立问题。 +- 因用户明确禁止人工 mirror、PipelineRun、Argo 和创建 `release` branch, + 本轮证据证明的是 YAML 声明、controlled-push 选择、PaC binding 与 fail-closed 隔离; + 合并后的真实自动链仍由后续授权阶段通过正常 GitHub release 提交触发。 ## Primary Workspace 边界 diff --git a/docs/reference/hwlab.md b/docs/reference/hwlab.md index ba78202f..c41ab1fe 100644 --- a/docs/reference/hwlab.md +++ b/docs/reference/hwlab.md @@ -21,6 +21,16 @@ - patch 不得改变交付 authority 或冒充终态; - 结论必须写回 owning YAML/源码并由正常 PR 自动交付; - 临时 patch 随后撤销或由声明式交付覆盖。 +- HWLAB production release delivery authority: + - development 与 production 复用同一个 + `mirrors/pikasTech-HWLAB` Gitea 物理仓库; + - owning YAML 必须分别声明 `hwlab-nc01-v03` 与 + `hwlab-nc01-production` source identity; + - production identity 固定选择 `release`、独立 immutable snapshot prefix + 与 `release-gitops`; + - production PaC consumer 必须精确引用 production repository identity; + - delivery authority parser 必须精确匹配 target、owner/name 与 branch, + 缺少 production identity 时 fail-closed,禁止回退到 `v0.3`。 - 进入任何 HWLAB 工作前,按 `NC01/v03` 解析 route/workspace/sourceBranch/kubeRoute/runtime namespace 做预检、快进和验证:工作面是 `NC01:/root/hwlab-v03`、source branch 是 `v0.3`、k3s route 是 `NC01:k3s`、runtime namespace 是 `hwlab-v03`、公网入口由 YAML 的 NC01 target 声明。 - NC01 的 node-local registry 是 k3s workload/PVC,不是 host Docker registry。`hwlab nodes control-plane infra status --node NC01 --lane v03` 应显示 registry workload ready、PVC bound 和 endpoint ready;必须通过受控 `runtime-image preload` 和 `infra tools-image build/status` 补齐 BuildKit、runtime/base 和 tools image,再把 HWLAB/AgentRun status 与 web-probe smoke 作为可用性证据。 - HWLAB 项目内长期规则入口仍以目标 repo 的 `AGENTS.md` 为准。进入已解析的目标 workspace 后,必须重新读取该 workspace 的规则文件;不能只凭主 server 的压缩上下文继续操作。 diff --git a/scripts/src/cicd-delivery-authority.ts b/scripts/src/cicd-delivery-authority.ts index ce3d5793..79790a70 100644 --- a/scripts/src/cicd-delivery-authority.ts +++ b/scripts/src/cicd-delivery-authority.ts @@ -149,20 +149,12 @@ export function readCicdDeliveryAuthorityCatalog(): CicdDeliveryAuthorityCatalog pacRepositories.filter((candidate) => same(candidate.id, repositoryRef)), `${path}.repositoryRef=${repositoryRef}`, ); - const params = item.params === undefined ? null : record(item.params, `${path}.params`); - const sourceBranch = params === null - ? pacRepository.sourceBranch - : requiredString(params.source_branch ?? pacRepository.sourceBranch, `${path}.params.source_branch`); - const sourceSnapshotPrefix = params === null - ? pacRepository.sourceSnapshotPrefix - : requiredString(params.source_snapshot_prefix ?? pacRepository.sourceSnapshotPrefix, `${path}.params.source_snapshot_prefix`); - const authorityRepositories = giteaRepositories.filter((candidate) => same(candidate.targetId, node) - && same(candidate.owner, pacRepository.owner) - && same(candidate.repo, pacRepository.repo)); - const branchAuthorityRepositories = authorityRepositories.filter((candidate) => same(candidate.sourceBranch, sourceBranch)); const authorityRepository = exactlyOne( - branchAuthorityRepositories.length > 0 ? branchAuthorityRepositories : authorityRepositories, - `${path} source authority ${node}/${pacRepository.owner}/${pacRepository.repo}@${sourceBranch}`, + giteaRepositories.filter((candidate) => same(candidate.targetId, node) + && same(candidate.owner, pacRepository.owner) + && same(candidate.repo, pacRepository.repo) + && same(candidate.sourceBranch, pacRepository.sourceBranch)), + `${path} source authority ${node}/${pacRepository.owner}/${pacRepository.repo}@${pacRepository.sourceBranch}`, ); return { consumerId, @@ -172,8 +164,8 @@ export function readCicdDeliveryAuthorityCatalog(): CicdDeliveryAuthorityCatalog pipelineRunPrefix, repositoryRef, sourceRepository: authorityRepository.sourceRepository, - sourceBranch, - sourceSnapshotPrefix, + sourceBranch: authorityRepository.sourceBranch, + sourceSnapshotPrefix: pacRepository.sourceSnapshotPrefix, giteaOwner: pacRepository.owner, giteaRepository: pacRepository.repo, giteaRepositoryUrl: pacRepository.url,