Skip to content

Latest commit

 

History

History
127 lines (98 loc) · 5.32 KB

File metadata and controls

127 lines (98 loc) · 5.32 KB

Releasing linux-dsoxlab-training

Language: English · Français

This repository ships lab content, not a Python package. Releases publish a tar.gz bundle of the lab catalog as a GitHub Release asset — no PyPI, no wheels, no external artifact registry.

What a release contains

The release.yml workflow builds linux-dsoxlab-training-<version>.tar.gz with:

  • labs/, meta.yml, conftest.py, solution/ (vault-encrypted)
  • pyproject.toml and uv.lock: a >= constraint lets every installation resolve whatever it likes, so the archive ships the lock that says what it actually expects
  • the governance docs (README, LICENSE, CONTRIBUTING, CODE_OF_CONDUCT, SECURITY, CHANGELOG), in both languages

It excludes local piloting (.claude/, todo/, ROADMAP-*.md, CLAUDE.md) and generated files (.venv/, caches). ssh/ is not shipped at all: the whole directory is gitignored (it holds the lab keypair generated by make bootstrap), so it never exists in a CI checkout.

Four assets are published next to the archive:

Asset Purpose
<pkg>.tar.gz.sha256 integrity checksum
provenance.intoto.jsonl SLSA provenance (what Scorecard Signed-Releases reads)
<pkg>.tar.gz.cosign.bundle Cosign keyless signature bundle
(registry-side) native GitHub build attestation

Why three jobs, and not one

The workflow is split into build, attest, publish, and that split is the only thing separating SLSA Build Level 2 from Level 3.

GitHub's documentation puts it in two sentences: "Artifact attestations by itself provides SLSA v1.0 Build Level 2", and "Reusable workflows can provide isolation between the build process and the calling workflow, to meet SLSA v1.0 Build Level 3". As long as the job that builds the archive is also the one that signs its provenance, nothing technically stops the build process from producing provenance that lies. Level 3 requires the signing to happen out of its reach.

Hence .github/workflows/attester.yml, called as a reusable workflow:

  • it is the only workflow in the repository granted attestations: write;
  • it receives a name and a digest, never the archive nor the repository: it performs no checkout;
  • the publish job can write the release but cannot attest, lacking that permission;
  • the archive is re-checked against its digest before publication, so that an artifact altered between two jobs is not published with provenance that does not describe it.

The call reads uses: ./.github/workflows/attester.yml, and not the newer $/ "self-repository" form, even though zizmor recommends the latter. The reason is worth recording: security tooling cannot read $/ yet, and an analyser that does not understand a construct cannot judge it safe. Measured on 2026-09-15, on that exact line: actionlint rejects it as an invalid format, zizmor 1.26.1 refuses to load the file and audits nothing at all, and Plumber takes it for an unpinned third-party action from an unauthorised source, two HIGH findings and a score of 70/100 instead of 100/100. The called workflow is the one that signs: it is the last line in the repository on which to give up the analysers' scrutiny.

Cutting a release

  1. Update CHANGELOG.md and CHANGELOG.fr.md (move items under a new version).

  2. Make sure dsoxlab validate-structure is green locally.

  3. Tag and push the tag — this triggers release.yml:

    git tag vX.Y.Z
    git push origin vX.Y.Z
  4. The workflow builds the tar.gz, attests its provenance, signs it keyless, and creates the GitHub Release with auto-generated notes.

Verifying a release

Integrity and contents:

sha256sum -c linux-dsoxlab-training-<version>.tar.gz.sha256
tar tzf linux-dsoxlab-training-<version>.tar.gz | head

Build provenance — proves this archive was built by this repo's workflow, not rebuilt by someone else:

gh attestation verify linux-dsoxlab-training-<version>.tar.gz \
  --repo stephrobert/linux-dsoxlab-training

The check that establishes Build Level 3 names the signing workflow. It fails if the provenance was produced anywhere other than the isolated attester workflow, and it is the one to run on the first release to confirm the chain holds:

gh attestation verify linux-dsoxlab-training-<version>.tar.gz \
  --repo stephrobert/linux-dsoxlab-training \
  --signer-workflow stephrobert/linux-dsoxlab-training/.github/workflows/attester.yml

Cosign keyless signature. Both flags are mandatory: without them cosign verify-blob accepts any identity, which defeats the point.

cosign verify-blob \
  --bundle linux-dsoxlab-training-<version>.tar.gz.cosign.bundle \
  --certificate-identity-regexp "https://github.com/stephrobert/linux-dsoxlab-training/.github/workflows/release.yml@.*" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  linux-dsoxlab-training-<version>.tar.gz

Cosign version trap. The CI installs Cosign 3.x (new bundle format). A local Cosign 2.x reports no signatures found on a perfectly signed archive — the release is fine, your local tool just cannot read the format. Check cosign version and align it before concluding anything is broken.

Commits and tags are created by a human, never by an assistant.