Skip to content

feat: Phase 7 — CI, release automation and container images - #31

Merged
TheMeinerLP merged 18 commits into
mainfrom
feat/phase-7-ci
Aug 13, 2026
Merged

feat: Phase 7 — CI, release automation and container images#31
TheMeinerLP merged 18 commits into
mainfrom
feat/phase-7-ci

Conversation

@TheMeinerLP

Copy link
Copy Markdown
Contributor

Builds out everything around delivery: this repository had no CI at all before, and three of its six components had no container image.

What lands

  • CIbuild-pr.yml builds and tests the Gradle modules and the Nuxt dashboard on every pull request; markdown-lint.yml lints the (substantial) documentation; close-invalid-prs.yml guards against fork-default-branch PRs.
  • Renovate via the central OneLiteFeather preset, with the :paper flavour for paper-worldpush's version scheme.
  • Release Please with three independent tracks — the project as a whole, telemetry-addon and paper-worldpush, the latter two because they hang off foreign version tracks (design spec §4). The version marker moved out of gradle.properties into build.gradle.kts.
  • Container images for operator, api and ui — the three that had none. The first two follow the conventions ingest/runner already set; the dashboard is a static SPA build served by unprivileged nginx.
  • Publishing — all six images to Harbor, plus telemetry-addon and paper-worldpush to the OneLiteFeather Maven repository.
  • A root README, which the repository never had.

Notes for review

The whole-branch review found four defects that would have failed only in CI, never locally, and they are worth knowing about:

  • runner/Dockerfile copied runner/vendor/BlueMapS3Storage.jar, which is gitignored — it only existed on developer machines from phase-1 setup. It is now fetched in a builder stage with a pinned version, the same way hosting/Dockerfile already did it.
  • Release Please resolves extra-files paths relative to the package path, so telemetry-addon/build.gradle.kts became telemetry-addon/telemetry-addon/build.gradle.kts and was silently skipped. Every release would have republished 0.1.0 over the previous one.
  • All six docker-publish calls share one concurrency group in the central workflow, so they would have cancelled each other. They are serialised with a needs: chain until the central workflow grows a concurrency-suffix input.
  • pnpm/action-setup had no version source: there is no root package.json, packageManager lives in ui/package.json.

Two values in the plan turned out to be wrong and were corrected against the real sources: the Maven repository is OneLiteFeatherRepository with ONELITEFEATHER_MAVEN_USERNAME/_PASSWORD, and hosting builds from hosting/ as its Docker context rather than the repository root.

Still open, recorded rather than fixed

No module carries an SLF4J provider, so no container writes application logs to stdout — which §13.1 (Alloy → Loki) and §11.1 (log streaming to the UI) both depend on. Recorded as a new open point in the design spec §15; it belongs to the Phase 8 observability work.

🤖 Generated with Claude Code

Phase 7 covers CI, release automation and packaging the three components
that had no container image. Phase 8 covers the Kustomize base, metrics
and the k3s end-to-end run. Phase 9 works through the five points the
design spec itself lists as open in §15.

The identity broker task carries the requirement that per-tenant roles
must look identical whether a user signs in through the tenant's own
federated IdP or through a local broker account, and that roles are never
taken from a federated assertion.
Multi-stage build compiles the SPA with `pnpm generate` (Node version taken
from ui/.nvmrc, currently 24) and serves the static output via
nginx-unprivileged (uid 101, port 8080), with try_files SPA fallback and a
cache policy that never caches index.html while treating hashed _nuxt/
assets as immutable.

Also adds a root .dockerignore: without it, `COPY ui/ ./` pulls the host's
gitignored ui/node_modules over the image's freshly installed one and the
build fails (or, worse, could ship stale local build output).
runner/Dockerfile copied runner/vendor/BlueMapS3Storage.jar out of the build
context, but runner/vendor/ is gitignored and therefore does not exist in a
fresh clone -- the publish-runner job could never have built the image in CI.

Download the addon in the existing fetch stage instead, pinned by
BLUEMAP_S3_STORAGE_VERSION, exactly the way hosting/Dockerfile already fetches
the same jar from the same release. The addon still lands at
/work/config/packs/bluemap-s3-storage.jar.

Verified by building the image with runner/vendor/ moved out of the way.
… bootstrap sha

extra-files paths are resolved relative to the package path, so
"telemetry-addon/build.gradle.kts" resolved to
telemetry-addon/telemetry-addon/build.gradle.kts. Generic updaters use
createIfMissing: false, so the miss was silent: tag, release and manifest were
bumped while the version marker in the build file stayed at 0.1.0 and the
publish job republished 0.1.0 over the previous release. Write both paths
package-relative, matching the root package's entry.

bootstrap-sha is compared against the full 40-character SHA, so the abbreviated
value never matched and the first release PR would have swept in all of phases
1-6.
docker-publish.yml@v2.4.0 declares a concurrency group of
docker-publish-${{ github.workflow }}-${{ github.ref }} with
cancel-in-progress: false. Both expressions are identical for all six of our
calls, so all six shared one group and the queued jobs cancelled each other. A
caller cannot override a callee's concurrency, so chain the six publish jobs
with needs: to keep at most one of them queued. Each job keeps a standalone
!cancelled() gate, so the edges convey ordering only: nothing publishes without
a release, and publish-hosting/publish-ui still take their content from the
checkout rather than from the Gradle artifact. The proper long-term fix is a
concurrency-suffix input on the central docker-publish.yml.

context-path: "." shipped the whole working tree, including .git and every
build directory, to five jobs that had already checked the repository out.
Narrow it to the four shadow jars. docker-publish checks out first and only then
downloads the artifact over the context, and upload-artifact roots a **-prefixed
glob at the workspace, so the <module>/build/libs/ prefix the Dockerfiles COPY
by name is preserved.

Give the two Maven publish jobs contents: read instead of inheriting the
workflow-level contents: write, matching the six Docker jobs.
pnpm/action-setup reads ${GITHUB_WORKSPACE}/package.json by default; this repo
has no root package.json and defaults.run.working-directory does not apply to
action inputs, so the ui job failed on its first step on every pull request with
"No pnpm version is specified." Point it at ui/package.json.

The central markdown-lint workflow defaults config-file to .markdownlint.json,
which does not exist here, so markdownlint-cli2 threw ENOENT on every docs PR.
Pass .markdownlint-cli2.jsonc.

Pin the Gradle PR build to ubuntu-latest: the central default is a three-OS
matrix, this project targets Linux only and several tests are path-separator and
line-ending sensitive.

Restore .github/workflows/** in the code path filter so a CI-only change still
runs the build, and add gradlew.bat.
The MD041 exemption cited SDD briefs and reports under .superpowers/sdd/, but
those files are not tracked; the tracked tree lints clean with the rule on.

The telemetry-addon publication comment claimed the thin jar would leave
consumers resolving relocated dependencies. Every dependency of that module is
compileOnly, so its shadow jar relocates nothing. State the actual reason it is
published: it is the same file the runner image ships, so no second jar can
drift from it. What is published is unchanged.
@github-actions

Copy link
Copy Markdown
Contributor

Test results

496 tests   496 ✅  40s ⏱️
 68 suites    0 💤
 68 files      0 ❌

Results for commit af528b5.

@TheMeinerLP
TheMeinerLP merged commit 12c0d81 into main Aug 13, 2026
9 checks passed
@TheMeinerLP
TheMeinerLP deleted the feat/phase-7-ci branch August 13, 2026 12:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant