Files
pikasTech-HWLAB/docs/dev-artifact-publish.md
T
2026-05-24 04:36:48 +00:00

11 KiB

HWLAB DEV Artifact Publish

scripts/dev-artifact-publish.mjs is the DEV-only artifact preflight/build backend for D601 local/internal registry artifacts. Publish side effects are normally entered by a CI artifact job identity, or by the legacy scripts/dev-cd-apply.mjs transaction path during transition. The artifact script does not deploy workloads, read secrets, enable PROD, or push to GHCR/Docker Hub/other third-party registries.

长期口径:CI 产物报告不是人工维护的镜像库状态,也不是 CD 放行的本地状态源。 它是 CI 自动生成的一次性审计记录。CD 必须从 deploy/deploy.json 读取发布控制 意图,从 deploy/artifact-catalog.dev.json 读取 digest 期望,并直接向 registry manifest 验证目标 tag/digest 是否存在。/tmp/hwlab-dev-gate/dev-artifacts.json 只能用于排障、刷新 catalog 和审计追溯。

Scope

  • Environment: dev.
  • Namespace: hwlab-dev.
  • Default registry prefix: 127.0.0.1:5000/hwlab.
  • Base image: resolved by scripts/preflight-dev-base-image.mjs from HWLAB_DEV_BASE_IMAGE, a local node:20-* Docker image, or a local HWLAB base tag such as 127.0.0.1:5000/hwlab/hwlab-dev-base:node20-bookworm-slim. There is no base-image pull or third-party fallback during publish; the build runs with --pull=false.
  • Node runtime dependencies are installed from the source package.json and package-lock.json inside the DEV image build with npm ci --omit=dev --ignore-scripts when the lockfile is present, unless the selected base image already provides /opt/hwlab-node-runtime-base/node_modules. This includes the Postgres pg driver required by the durable cloud-api runtime adapter.
  • Image tag: first seven characters of the Git commit used as build source.
  • Required image labels:
    • hwlab.pikastech.local/repo
    • hwlab.pikastech.local/commit
    • hwlab.pikastech.local/service-id
    • hwlab.pikastech.local/environment=dev

Commands

Static check:

node --check scripts/preflight-dev-base-image.mjs
node --check scripts/src/dev-base-image-preflight.mjs
node --check scripts/src/dev-artifact-services.mjs
node --check scripts/src/registry-capabilities.mjs
node --check scripts/dev-artifact-publish.mjs
node --check scripts/dev-runtime-base-image.mjs
node --check scripts/refresh-artifact-catalog.mjs
node --test scripts/refresh-artifact-catalog.test.mjs

Base image preflight:

node scripts/preflight-dev-base-image.mjs

Preflight and report generation:

node scripts/dev-artifact-publish.mjs --preflight

Build only:

node scripts/dev-artifact-publish.mjs --build

Build only one or more selected services for a CI artifact task:

node scripts/dev-artifact-publish.mjs --build --services hwlab-cloud-web

--services narrows the build/publish report to selected service IDs. It is a CI/artifact acceleration surface; the normal DEV rollout still consumes repo-owned desired state through scripts/dev-cd-apply.mjs.

Build and publish to the D601 local/internal registry inside a CI artifact job:

HWLAB_CI_ARTIFACT_RUN_ID=<run-id> node scripts/dev-artifact-publish.mjs --publish

The legacy transaction entry remains available during migration:

node scripts/dev-cd-apply.mjs --apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/report.json

Direct --publish without HWLAB_CI_ARTIFACT_RUN_ID or HWLAB_CD_TRANSACTION_ID is rejected with cd-transaction-required; this is a bypass guard, not a second lock.

Daily Cloud Web source validation does not require ignored dist/ to exist:

node web/hwlab-cloud-web/scripts/check.mjs

For hwlab-cloud-web, the build path refreshes and verifies the static bundle before image build; publish still goes through the transaction command above:

node web/hwlab-cloud-web/scripts/build.mjs
node scripts/dev-cd-apply.mjs --apply --confirm-dev --confirmed-non-production --report /tmp/hwlab-dev-gate/report.json

The runtime wrapper serves /app/web/hwlab-cloud-web/dist before the source directory. If dist is stale, a newly tagged image can still serve old HTML, CSS, and JavaScript. A publish for Cloud Web is not valid until the image has fresh dist evidence. build.mjs regenerates dist and verifies every runtime file and route alias matches source. dev-artifact-publish.mjs re-runs that build/freshness gate and records artifactPublish.services[].distFreshness for hwlab-cloud-web before the Docker build. check.mjs remains a clean-checkout source contract and must not fail only because ignored dist/ is absent.

Use --registry-prefix only for another localhost/private/internal D601 registry prefix. The script rejects third-party registry hosts and any prefix that names PROD. Use --base-image only to override HWLAB_DEV_BASE_IMAGE with an approved local image for that run.

Docker build output is visible by default so a slow layer is observable from the runner log. Use --quiet-build only when a caller already captures the full Docker log elsewhere. The artifact report records per-service dockerBuildDurationMs, cloudWebBuildDurationMs, and publishDurationMs.

An optional dependency base image can be prepared with:

node scripts/dev-runtime-base-image.mjs --dry-run
node scripts/dev-runtime-base-image.mjs

The default tag is 127.0.0.1:5000/hwlab/hwlab-node-runtime-base:deps-<package-lock-hash>. Selecting that image through HWLAB_DEV_BASE_IMAGE lets service builds reuse a preinstalled node_modules layer instead of repeating npm ci for every service.

Report

The script writes /tmp/hwlab-dev-gate/dev-artifacts.json. The file is kept in the existing DEV gate report envelope for compatibility with scripts/validate-dev-gate-report.mjs, while the artifactPublish object is the HWLAB#35-specific publish record. This report is evidence, not desired state. A later CD rollout must verify catalog images directly against the registry instead of trusting this file.

Each service record contains:

  • serviceId
  • image
  • imageTag
  • digest
  • distFreshness for hwlab-cloud-web build/publish runs
  • publishEnabled
  • artifactRequired
  • artifactScope
  • notPublishedReason
  • runtimeKind
  • implementationState
  • sourceState
  • entrypoint

The report also carries artifactPublish.serviceInventory and artifactPublish.publishPlan. serviceInventory is the resolved frozen service list with enabled/disabled state. publishPlan is the machine-readable v2 plan: source commit, image tag, per-service registry target, image reference, digest placeholder or real digest, registry capability evidence, and the reason a service was not published.

The report also includes artifactPublish.registryCapabilities, split into three dimensions:

  • process-http-access: runner-process HTTP access to the registry /v2/ endpoint. This is diagnostic only. A failed http://127.0.0.1:5000/v2/ fetch from the runner process is degraded, not proof that Docker publish failed.
  • docker-daemon-push-access: Docker daemon view of the local/internal registry target. This is the publish-path capability and is the only registry capability that blocks --publish before a push attempt.
  • k3s-pull-access: read-only k3s view of hwlab-dev image pull state. This is the deploy-path capability and does not prove or disprove Docker daemon push access.

digest is only set to a registry digest after docker push succeeds and the push output contains a sha256:<64 hex> digest. If the push succeeds but no digest is observable, the service remains published_unverified_digest, digest stays not_published, and the report carries a blocker. The script records blockers instead of claiming a publish when build, push, digest observation, base image, registry, contract, or safety checks fail.

After a fully successful publish, update the catalog from the report:

node scripts/refresh-artifact-catalog.mjs --target-ref origin/main --publish-report /tmp/hwlab-dev-gate/dev-artifacts.json

If publish remains blocked, refresh only commit/tag identity and keep digests blocked:

node scripts/refresh-artifact-catalog.mjs --target-ref origin/main --blocked

Before any later DEV apply review, run the read-only artifact/runtime guard so artifact evidence is not mistaken for deployed runtime evidence:

node scripts/artifact-runtime-readiness-guard.mjs --target-ref origin/main --check --no-report --expect-blocked

The guard must remain blocked until the target commit, publish report, artifact catalog, desired-state files, Cloud API /health/live, and Cloud Web :16666/health/live all report the same identity. It does not publish, deploy, restart services, mutate Kubernetes, touch PROD, or read secrets.

The report also includes artifactPublish.baseImagePreflight with the image source, local tag, local image ID, publishUsable gate, blockers, and next steps. It also carries recommendation, provision, and blockedReport objects so the blocked report includes the exact blocker, expected image tag, recommended HWLAB_DEV_BASE_IMAGE value, and preload commands. When that preflight is blocked, artifact publish is blocked and no build or push is attempted.

If the D601 builder has no approved Node 20 base image, or the selected DEV registry is not reachable from the Docker daemon publish path, the report must remain status: "blocked". Do not turn a base-image or registry blocker into a fake digest or published state. Do not treat runner-process loopback HTTP failure as a publish failure by itself.

Committed DEV artifact reports are evidence snapshots and may lag the deploy desired-state files. Use scripts/deploy-desired-state-plan.mjs to see whether the report source matches the desired-state commit, then refresh the catalog from a publish report only when that report proves the same target source commit. If a later read-only runner cannot fetch catalog manifests from 127.0.0.1:5000, treat that as a #66 registry reachability dimension, not as a missing base-image or missing-publish blocker.

Known Implementation States

  • repo-entrypoint: the repository has cmd/<serviceId>/main.mjs.
  • static-web-wrapper: the cloud web static files are packaged behind a small health/static-file wrapper.
  • repo-bundle: the image packages repository-owned skill artifacts.
  • library-only: the repository has library code but no executable package bin.
  • missing-runtime-entrypoint: the image is only a health placeholder and is not a real service implementation.

Known Source States

  • source-present: the service remains in the MVP artifact catalog and has a repository-owned source path or entrypoint conclusion.
  • intentionally-disabled: the service is explicitly out of the MVP artifact publish set until it is given source. It must not remain a vague blocker.

Runtime implementation blockers do not fake a service implementation. They are recorded as the next minimum work needed after artifact publication.