Repository navigation
Open
5 / 95 of 9 issues completedOpen
5 / 95 of 9 issues completed
Copy link
Description
Target repo: halos-org/shared-workflows. Migration touches 32 caller repos in halos-org and hatlabs.
Overview
Redesign the caller interface of the shared reusable workflows while every caller is edited once:
- Packaging is composed by the caller, not switched off by flags. A deb repo calls a deb build; an npm repo does not.
hatlabs/shared-workflowsmerges intohalos-org/shared-workflowsand is retired.- Callers pin a moving major tag
@v1instead of@main. - Each caller owns the name of its required check through a local aggregating job.
- npm publishing becomes a shared workflow.
Problem Frame
See origin note for the inventory. In short:
build-release.ymlbuilds a deb unless told not to (build-deb: truedefault); 6 repos opt out.package-nameis required and described as a Debian name even without a deb.- Lintian flags point in opposite directions (
skip-lintianin pr-checks,run-lintianin build-release). The pr-checks lintian job ignoresruns-on. - Two copies of the workflows have drifted in both directions; the only intended difference is the
apt-repositorydefault. Release notes hardcodeapt.halos.fi(hatlabs issue 2). - Every caller uses
@main, so any breaking change hits all 32 repos at merge time. - Required check names are
checks / lintian,checks / version-bump-check / version-bump-check, and so on. A job rename in this repo blocks every PR in every caller. Required-check sets are also inconsistent across repos. - Five npm
release.ymlcopies have diverged (issue 29).
Requirements Trace
- R1. No workflow builds or checks a deb unless the caller calls a deb-specific workflow.
- R2. One source repo. halos-org carries no hatlabs-only defaults or logic; hatlabs callers pass generic inputs.
- R3. Callers reference
@v1. Breaking changes ship asv2, not onmain. - R4. Each caller's branch protection requires one caller-owned check name; renaming a job in this repo does not change it.
- R5. npm publishing runs from a shared workflow; the five callers delete their copies.
- R6. No caller is broken at any point during the migration. Each non-canary caller migrates in one PR; canaries take two (rc pin, then
@v1). - R7. Release artifacts (tags, pre-releases, drafts,
.debassets, APT dispatches, npm publishes) are equivalent before and after migration.
Scope Boundaries
- Out: internal-only fixes that need no caller edits (issue 30, issue 37 and PR 31, issue 23, translation-status issues 40, 43, 45, 46), except where a rewritten file absorbs them (issue 48, see decisions).
- Out: redesign of
check-image-updates.ymlandtranslation-status.yml. Their callers only change the ref to@v1. - Out: sharing repo-local actions (
run-tests,build-deb,check-versions). They stay per repo. - Out: branch protection for the two private repos without protection (signalk-questdb-history-provider-archive, signalk-n2k-playback). Plan availability not checked.
- Out: SHA-pinning third-party actions.
Context & Research
Relevant Code and Patterns
.github/workflows/build-release.yml— revision calculation (readsVERSION, next+Nfrom tags), tag derivation, pre-release and draft creation, deb steps gated byinputs.build-deb. No job or workflow outputs exist today..github/workflows/pr-checks.yml— tests, version-check (optional action), lintian (self-skips withoutbuild-debaction; hardcodesubuntu-latest), nested./.github/workflows/version-bump-check.yml..github/workflows/publish-stable.yml— tag parse, fails with zero assets, APT dispatchchannel: stable..github/workflows/check-image-updates.yml— checks out its own scripts withgithub.job_workflow_sha, which does not exist and evaluates empty, so scripts always come frommain(issue 58).examples/docs-repo/.github/workflows/translation-status.ymlandREADME.md"Making it a gate" — the caller-ownedstatusjob pattern from PR 47.- Five npm
release.ymlcopies (skip, skip-freeboard-panel, signalk-duckdb-history-provider, signalk-questdb-history-provider, signalk-halpi). Common core:release: published, non-prerelease only,id-token: writetrusted publishing, npm 11 via corepack, skip if version already on npm. Divergence: VERSION vs package.json guard (2 of 5),persist-credentials: false(1), concurrency (4), package name hardcoded (4) vs read from package.json (1), explicit build step (1). - Repo state: zero tags, zero releases, no CI of its own, no AGENTS.md, README input tables stale,
examples/cockpit-aptreferences inputs that do not exist.mainhas protection with no required checks.probe.ymlandguardprobe.ymlstill listed as active workflows from deleted test branches. - Callers'
main.ymlalready setsconcurrency(checked cockpit-apt, skip, HALPI2-blinkenlights).
External References
- Reusable workflow refs, nesting, limits, outputs: https://docs.github.com/en/actions/reference/workflows-and-actions/reusing-workflow-configurations
- Moving major tag process: https://github.com/actions/toolkit/blob/main/docs/action-versioning.md
- Skipped jobs report success for required checks: https://docs.github.com/en/actions/how-tos/writing-workflows/choosing-when-your-workflow-runs/using-conditions-to-control-job-execution
- Check name format
<job> / <reusable job>: https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/troubleshooting-rules - npm trusted publishing validates the calling workflow's filename;
id-token: writeneeded in caller and called workflow: https://docs.npmjs.com/trusted-publishers/ - Cross-org callers must pass secrets explicitly (
secrets: inheritis same-org only).
Key Technical Decisions
- New filenames, legacy files frozen. New workflows get new names.
build-release.yml,pr-checks.yml,publish-stable.ymlstay untouched onmainuntil the last caller leaves@main, then are deleted. This satisfies R6 with one PR per caller. Rejected: reuse names and first move every caller to a frozen@v0tag — doubles caller PRs. - Legacy files are not part of the v1 interface.
v1.0.0contains them only becausemainstill serves@maincallers. AGENTS.md and the README migration guide say so, and their removal inv1.1.0is the one documented exception to "breaking changes need a new major". Rejected: cutting v1 from arelease/v1branch without the legacy files — a second long-lived branch to keep in sync for the whole migration. - The first major is
v1. No tags exist. Full releasesv1.0.0,v1.1.0, … plus a movingv1tag, per the actions/toolkit process. Release candidates (v1.0.0-rc.N) serve canary callers beforev1exists. - Release pipeline as composed jobs. The ordering is fixed by data: the deb build needs the version, the release needs the deb, the APT dispatch needs the release asset. Each step is one reusable workflow that exposes outputs:
release-version.yml— reads VERSION, computes+N, derives tags, reports whether a release is due. Generic.build-deb.yml— changelog, repo-localbuild-debaction, lintian, rename, upload artifact. Deb only.stage-release.yml— pre-release and draft, attaching any artifact it is given. Generic.apt-publish.yml— APT dispatch for a channel (unstablefrom main,stablefrom release). Deb only. Replaces the dispatch in build-release andpublish-stable.yml.publish-npm.yml— npm only.
Rejected: one release workflow with adebflag — that is the default-on flag inverted, which R1 exists to remove.
- PR checks split the same way.
checks.ymlruns tests, version-check, version-bump-check. Deb repos add a call tobuild-deb.ymlin check mode (build and lintian, no version inputs, no upload).skip-lintiandisappears;lintianis an input ofbuild-deb.yml(default on) for the two repos that disable it. Theruns-oninput applies to every job. skip-testsdisappears. Tests on main are a job the caller includes or omits. Branding repos simply omit it.apt-repositorybecomes required onapt-publish.yml. No default in either org's favour. Release-note install text derives the APT host from the repository name (both repos are named after their domain) and the distro and component from the existing inputs, fixing hatlabs issue 2 without hatlabs logic. Whether the keyring filename and key URL path are the same on both hosts is checked on the hosts in Unit 3; if not, they become inputs.- Caller-owned check. Every caller's
pr.ymlends with astatusjob thatneedsevery other job in the file, shared or local (halos-pi-genimage-version-check, flash-live-systembuildandshellcheckstay required).if: always(); it fails unless each result issuccess. On PRs no job is expected to skip, soskippedfails too; a caller that makes a job conditional lists that job as allowed to skip. Branch protection requiresstatusonly. Same shape as PR 47. - Tags target the tested commit (issue 48).
stage-release.ymlis new code, so it passes the run's SHA as the tag target. Absorbed here because the file is rewritten anyway. - Releases only from the default branch. Because tags now target the run's SHA, a
workflow_dispatchofmain.ymlon a feature branch would tag and dispatch that branch.release-version.ymlfails with a clear error when the ref is not the default branch. Legacy behaviour tagged main's head from any branch, which was also wrong. - Publish workflows skip pre-releases.
apt-publish.ymlstable mode andpublish-npm.ymlboth skip whengithub.event.release.prereleaseis true. Today pre-releases are created with the job token and do not triggerrelease.yml; the guard keeps that true if a token change ever makes them trigger. - npm trusted publishing keeps working. npm validates the calling workflow's filename, so callers keep
.github/workflows/release.yml.publish-npm.ymlreads the package name frompackage.json, enforces VERSION equals package.json version, enforces the release tag's upstream part equals VERSION at the checked-out commit, setspersist-credentials: false, and skips when the version already exists. The tag guard catches a legacy draft (created without a tag target) published aftermainmoved to a new VERSION. The caller grantsid-token: write. - hatlabs retirement. Port the only hatlabs-ahead change (
.coderabbit.ya?mlexclusion in version-bump-check). hatlabs callers switch tohalos-org/shared-workflows@v1. Archivehatlabs/shared-workflowsafter its last caller moves; archiving is a repo setting and needs approval at that moment.
Open Questions
Resolved During Planning
- Does trusted publishing allow the publish step in another org's reusable workflow? Yes; npm checks the caller's filename.
- Do
version-bump-check.yml,check-image-updates.yml,translation-status.ymlchange interface? No. Their callers only change the ref. - Do caller migration PRs need a VERSION bump? No.
.github/is excluded by version-bump-check.
Deferred to Implementation
- Nested
./reference under a tag pin. GitHub does not document whetheruses: ./.github/workflows/version-bump-check.ymlinside a called workflow resolves at the caller's pinned tag. Verify with an rc tag on a canary. If it does not, referencehalos-org/shared-workflows/.github/workflows/version-bump-check.yml@v1explicitly and accept updating it per major. - How
build-deb.ymlcheck mode supplies a version to the changelog step without release inputs. - Whether
release-version.ymlandstage-release.ymlneed a guard against a tag created between the two jobs; callerconcurrencycovers same-repo runs. - Exact output names of
release-version.yml.
High-Level Technical Design
This illustrates the intended approach and is directional guidance for review, not implementation specification. The implementing agent should treat it as context, not code to reproduce.
Caller composition per repo kind:
| Caller file | deb repo | npm repo | tag-only repo (docs-tools) | deb + npm (signalk-halpi) |
|---|---|---|---|---|
pr.yml |
checks, build-deb (check mode), status | checks, status | checks, status | checks, build-deb (check mode), status |
main.yml |
tests?, release-version → build-deb → stage-release → apt-publish unstable | tests?, release-version → stage-release | tests?, release-version → stage-release | tests?, release-version → build-deb → stage-release → apt-publish unstable |
release.yml |
apt-publish stable | publish-npm | none | apt-publish stable, publish-npm |
flowchart LR
T[tests local job, optional] --> V[release-version]
V -->|release due| B[build-deb]
B -->|artifact| C[stage-release]
V -->|npm/tag-only| C
C --> A[apt-publish unstable]
Implementation Units
- v1 Unit 1: Repo foundation — CI, release process, AGENTS.md #50
- v1 Unit 2: PR-side workflows —
checks.yml,build-deb.ymlcheck mode #51 - v1 Unit 3: Release pipeline —
release-version.yml,build-deb.ymlrelease mode,stage-release.yml,apt-publish.yml#52 - v1 Unit 4:
publish-npm.yml#53 - v1 Unit 5: Documentation and v1.0.0 #54
- v1 Unit 6: Canary migration #55
- v1 Unit 7: Migrate remaining callers #56
- v1 Unit 8: Retire legacy #57
System-Wide Impact
- Interaction graph: apt.halos.fi and apt.hatlabs.fi receive
package-updateddispatches; payload must stay identical. npm registry trusted-publisher config binds to caller filenamerelease.yml. - Error propagation: a failing shared job fails the caller's
status; skipped jobs pass. - State lifecycle risks: open PRs in a repo lack
statusafter its protection changes; they need a rebase. Draft cleanup deletes all drafts in the caller repo, unchanged. - API surface parity:
version-bump-check.yml,check-image-updates.yml,translation-status.ymlkeep their interface. - Unchanged invariants: tag format
v<upstream>+<N>and_presuffix; N = max over existingv<upstream>+*git tags (including_pre, full fetch) + 1; Debian version<upstream>-<N>;.debrename suffix; repo-local action contract; APT dispatch payload.
Rollback
- One caller, before Unit 8: revert the migration PR and restore the old required check names in the same step. The protection change needs approval together with the revert.
- Legacy files stay frozen under every tag, so
build-release.yml@v1.0.0behaves exactly like today's@main. - After Unit 8: rollback means pinning the legacy files at
@v1.0.0; hatlabs repos then passapt-repository: hatlabs/apt.hatlabs.fiexplicitly.
Risks & Dependencies
| Risk | Mitigation |
|---|---|
Nested ./ call does not follow the pinned tag |
Verified on rc canary; fallback is an explicit @v1 reference |
| APT dispatch payload differs and the APT repo ignores or misfiles a package | Unit 3 compares canary payload and resulting APT entry with the previous release |
| Branch protection change blocks in-flight PRs in a repo | Change protection only when merging that repo's migration PR; list open PRs first |
A caller's legacy @main run breaks during migration |
Legacy files are frozen, not edited, until Unit 8 |
Moving v1 pushes a regression to all callers at once |
Changes land as v1.x.0 releases; canaries can pin vX.Y.Z before v1 moves |
| npm trusted publishing rejects the shared workflow | Unit 4 canary publishes before other npm repos migrate; NPM_TOKEN fallback not added unless needed |
Documentation / Operational Notes
- Workspace
AGENTS.md(halos) describesshared-workflowsusage and the per-org copies; update it after Unit 8. docs/solutionsor memory entry: caller-ownedstatuscheck pattern and the tag-pin rule.
Sources & References
Metadata
Metadata
Assignees
Labels
No labels
Activity
checks.yml,build-deb.ymlcheck mode #51release-version.yml,build-deb.ymlrelease mode,stage-release.yml,apt-publish.yml#52publish-npm.yml#53mairas commentedon Sep 17, 2026
Plan correction:
check-image-updates.ymldoes not follow the pinned ref.github.job_workflow_shais not a context property, so its script checkout falls back tomain. Tracked in issue 58, added as a sub-issue; it must land before halos-core-containers and halos-marine-containers move to a tag.fix(ci): make the lintian check actually inspect the package
ci: make the required lintian check inspect the package
mairas commentedon Sep 19, 2026
A caller-repo item for whichever unit does the sweep (Unit 7 migration or Unit 8 cleanup).
halos-org/halos-core-containerscarries a workaround for #32 that exists only becausepr-checks.ymlandbuild-release.ymlare frozen:tools/expose-debs.sh— copiesbuild/*.debto the checkout root so the frozen root-level lintian glob can see them, and fails when the build produced nothing..github/actions/build-deb/action.ymlthat calls it.rm -f ./*.debat the top of.github/scripts/rename-packages.sh, which exists only to clear those copies before the release asset glob.tests/test-expose-debs.shandtests/test-rename-packages.sh.Added in halos-core-containers#224. All of it goes when the frozen workflows are retired, assuming v1 either globs where the build writes or leaves lintian to the caller. The script carries a
REMOVE when #49 landsmarker, but a comment inside a caller repo is a note to nobody unless this issue points back — hence this comment.Worth noting for the v1 design:
checks.ymlhas no lintian job today, so if lintian becomes caller-owned, the layout mismatch (build-all.shwrites tobuild/, the shared step globs the root) stops being a shared-workflow concern and the workaround becomes the normal arrangement rather than something to delete. Either way the decision belongs in v1, not in the caller.Two other repos are affected by the same frozen-workflow contradiction and are not fixed:
checks / lintianis a required context ondocs-tools,halos-pi-gen,signalk-questdb-history-providerandskip, all of which passskip-lintian: true— so no PR on any of them can merge today.halos-marine-containerspassesskip-lintian: falsebut its check is vacuous for the glob reason in #32.ci: make the required lintian check inspect the package