# HWLAB DEV Artifact Publish `scripts/dev-artifact-publish.mjs` is the DEV-only build and publish path for D601 local/internal registry artifacts. It does not deploy workloads, read secrets, enable PROD, or push to GHCR/Docker Hub/other third-party registries. ## 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 network pull or third-party fallback during publish; the build runs with `--pull=false`. - 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/refresh-artifact-catalog.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 and publish to the D601 local/internal registry: ```sh node scripts/dev-artifact-publish.mjs --publish ``` For `hwlab-cloud-web`, refresh the static bundle before publishing: ```sh node web/hwlab-cloud-web/scripts/build.mjs node scripts/dev-artifact-publish.mjs --publish --services hwlab-cloud-web --report reports/dev-gate/dev-artifacts-hwlab-cloud-web-.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 been probed locally and the served HTML matches the intended source. 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. ## Report The script writes `reports/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. Each service record contains: - `serviceId` - `image` - `imageTag` - `digest` - `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 reports/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 ``` 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. Current rebaseline: the committed DEV artifact report is `status: "published"` for source commit `73b379f`, with `baseImagePreflight.status: "ready"`, `baseImage: "node:20-bookworm-slim"`, and 13/13 required services carrying real registry digests. 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//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.