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

WorkflowWhat it runs
python-ci.ymlruff lint · mypy · pytest · coverage gate → Codecov
go-ci.ymlgolangci-lint · govulncheck · tests · coverage gate → Codecov
scorecard.ymlOSSF Scorecard → SARIF uploaded to code scanning (avoids the api.scorecard.dev timeout)
auto-approve.ymlAuto-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-runner to 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. The automerge label 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:

  1. The version file in the repo root (read at runtime by dashboards).
  2. The version field in pyproject.toml.
  3. 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.

Next: contributing a change →