# B1 / @next Durable Publish Pipeline — Design ## Objective Make `next` a durable integration line that publishes the artifacts required by downstream federation boot tests without manual builds. Every merge to `next` publishes: 1. **npm prerelease packages** to the Gitea npm registry with dist-tag `next`. 2. **Gateway container image** tagged only as `gateway:sha-`. The existing stable release behavior remains isolated to `main` / tags. ## Registry verification Target registry: `https://git.mosaicstack.dev/api/packages/mosaicstack/npm/`. Pre-implementation checks: - `npm view @mosaicstack/mosaic dist-tags --registry https://git.mosaicstack.dev/api/packages/mosaicstack/npm/ --json` returned a dist-tags object (`latest: 0.0.48`). - `npm view @mosaicstack/mosaic@latest version --registry https://git.mosaicstack.dev/api/packages/mosaicstack/npm/` resolved `0.0.48`. - `@next` currently returns 404 because no `next` dist-tag exists yet; this is expected before the first next prerelease publish. Pipeline design includes a post-publish verification that `npm view @mosaicstack/mosaic@next version` resolves to the exact CI-computed prerelease version. If Gitea fails to honor the `next` dist-tag, the pipeline fails closed. ## Version scheme The prerelease version is computed at publish time only; no `package.json` version changes are committed. For each non-private `@mosaicstack/*` package: ```text -next. ``` Where: - `CI_PIPELINE_NUMBER` is Woodpecker's monotonic pipeline number. - `target-stable` is the package's current committed stable version with the patch component incremented. - Example: `@mosaicstack/mosaic` `0.0.48` publishes as `0.0.49-next.1626`. - Example: `@mosaicstack/gateway` `0.0.6` publishes as `0.0.7-next.1626`. Rationale: - npm semver sorts `0.0.49-next.1627` above `0.0.49-next.1626`. - The prerelease does not overtake the future stable `0.0.49`. - The monotonic pipeline number avoids conflicts across repeated `next` merges. ## Branch and tag guardrails | Pipeline path | Branch/event | Publishes | Forbidden | | --------------------- | ------------------------------ | ------------------------------------------------------- | ---------------------- | | stable npm publish | `main` push/manual or tag | package versions already committed in package manifests | `@next` dist-tag | | next npm publish | `next` push/manual only | CI-computed prereleases with `--tag next` | `latest` dist-tag | | gateway image | `main` push/manual or tag | `sha-` + `latest` on main + tag on tag events | next prerelease npm | | gateway image | `next` push/manual only | `sha-` only | `latest` | | appservice/web images | `main` push/manual or tag only | existing stable image behavior | next image publication | The pipeline has explicit branch checks inside the publish commands as a second fail-closed layer beyond Woodpecker `when` clauses. ## Implementation plan 1. Widen `.woodpecker/publish.yml` top-level `when` to include `next` so the publish pipeline runs on next merges. 2. Keep existing `publish-npm` on `main` / tags only. 3. Add `publish-next-npm` for `next` push/manual only: - configure Gitea npm auth from existing `gitea_token` secret as `NPM_TOKEN`; - preflight registry dist-tag metadata; - compute prerelease versions in CI by temporarily editing package manifests in the workspace; - run `pnpm publish ... --tag next` against non-private `@mosaicstack/*` packages; - verify `@mosaicstack/mosaic@next` resolves to the computed version. 4. Split image `when` anchors: - `image_build_when` includes `next` and is used by `build-gateway`; - `main_image_build_when` keeps appservice/web on main/tags only. 5. Keep gateway next image destinations to `sha-` only; no `latest` on next. ## Risk controls - Auth/registry failures are fatal. - No manual image build/push path is introduced. - No production `latest` tags are touched from `next`. - No `@latest` npm dist-tags are touched from `next`. - All changes live in CI config and docs; no runtime source behavior changes.