04 · CI / CD
How every repo ships
All CI lives once, in venus-os-ci-toolkit. Project repos call reusable workflows — no per-repo copies of lint/test logic to drift apart.
Reusable workflows
| Workflow | What it runs |
|---|---|
| python-ci.yml | ruff lint · mypy · pytest · coverage gate → Codecov |
| go-ci.yml | golangci-lint · govulncheck · tests · coverage gate → Codecov |
| scorecard.yml | OSSF Scorecard → SARIF uploaded to code scanning (avoids the api.scorecard.dev timeout) |
| auto-approve.yml | Auto-approves Dependabot/Renovate PRs via hmarr/auto-approve-action — dismisses stale approvals before re-approving |
Consuming a toolkit workflow — always pinned to a full commit SHA:
jobs:
test:
uses: victron-venus/venus-os-ci-toolkit/.github/workflows/python-ci.yml@875762362bb31b4899bf02fdf9e835188dc5eb80
Non-negotiable rules
- Pin everything. Every
uses:in every workflow resolves to a full commit SHA — never a tag or branch. Tags move; SHAs don't. This is the supply-chain boundary. - Harden runners. Workflows include
step-security/harden-runnerto audit egress network calls. - Never push to main. Feature branch from
main, open a PR, let CI and CodeQL pass, merge via review. Eligible, non-draft PRs receive auto-approval independently of labels. Theautomergelabel enables automatic merging after required checks and review requirements are satisfied. - Conventional Commits.
feat:,fix:,docs:,chore:— release tooling parses them. - Auto-approve hygiene. The reusable auto-approve workflow dismisses its own stale approval before re-approving, and the retry window is 30 minutes (not 15s).
Releases
Releases use softprops/action-gh-release with dynamic prerelease detection — any tag
matching -alpha.N, -beta.N, -rc.N (or containing a dash)
publishes as a prerelease automatically:
if [[ "$TAG" =~ -(alpha|beta|rc)\.[0-9]+ || "$TAG" =~ - ]]; then
echo "is_prerelease=true" >> $GITHUB_OUTPUT
fi
Version sync rule
Three sources of version must always agree — when they diverge, dashboards report one version and the installed package another:
- The
versionfile in the repo root (read at runtime by dashboards). - The
versionfield inpyproject.toml. - The git tag, prefixed with
v(e.g.v1.18.10).
Bump all three in one commit, then tag that commit.
Why so strict? These repos control physical hardware. A moved tag or an unreviewed
workflow edit is not a cosmetic risk — treat CI config with the same care as control code.