265 lines
11 KiB
Markdown
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.
|