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

265 lines
11 KiB
Markdown

# 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:
```sh
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:
```sh
node scripts/preflight-dev-base-image.mjs
```
Preflight and report generation:
```sh
node scripts/dev-artifact-publish.mjs --preflight
```
Build only:
```sh
node scripts/dev-artifact-publish.mjs --build
```
Build only one or more selected services for a CI artifact task:
```sh
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:
```sh
HWLAB_CI_ARTIFACT_RUN_ID=<run-id> node scripts/dev-artifact-publish.mjs --publish
```
The legacy transaction entry remains available during migration:
```sh
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:
```sh
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:
```sh
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:
```sh
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:
```sh
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:
```sh
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:
```sh
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.