Kai Ole Hartwig
yasrt · documentation

CI pipelines

Every release ships yasrt-linux-{amd64,arm64} and yasrt-darwin-{arm64,amd64} with SHA256SUMS and a detached cosign signature over it. The binaries are built and signed once in my pipeline; GitHub carries the same files, nothing is rebuilt there.

  • Binary:go install github.com/ohartwig/yasrt/cmd/yasrt@latest or the signed releases
  • Image:registry.ole-hartwig.eu/devops/images/yasrt:1 carries the binary with git, gpg and ssh-keygen
  • Verify:cosign verify-blob --key <public-key> --signature SHA256SUMS.sig --insecure-ignore-tlog=true SHA256SUMS, then sha256sum -c SHA256SUMS

GitLab CI

stages: [version, build, release]

default:
  image:
    name: registry.ole-hartwig.eu/devops/images/yasrt:1
    entrypoint: [""]

version:
  stage: version
  variables: { GIT_DEPTH: 0 }          # yasrt needs the whole history
  script: yasrt next --output .release.env
  artifacts:
    reports: { dotenv: .release.env }
  rules: [{ if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH }]

build:
  stage: build
  image: your/build-image
  script:
    - '[ "$RELEASE_STATUS" = "release" ] || exit 0'   # gate in the script, not in rules:
    - make build VERSION=$RELEASE_VERSION
  rules: [{ if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH }]

release:
  stage: release
  variables: { GIT_DEPTH: 0 }
  script: yasrt release
  rules: [{ if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH }]

 

GitLab evaluates rules: when the pipeline is created, before .release.env exists. Downstream jobs therefore gate in the script on RELEASE_STATUS. The project must allow job-token pushes (Settings → CI/CD → Job token permissions → Allow Git push requests to the repository), and whoever may merge to the default branch must be allowed to create the release tag; yasrt check verifies both. A push with the job token starts no pipeline, and that is exactly what keeps the release commit from releasing itself again.

A repository whose publishing has to run on the tag (say because a signing role's OIDC trust is limited to ref_type:tag) keeps that pipeline by having the release start it: after_release.triggers expands ${tag}, ${version} and CI variables in project, ref and variables; { project: "${CI_PROJECT_PATH}", ref: "${tag}" } starts the tag pipeline the job-token push did not.

A trigger goes out with the job token unless the entry names, with token_var, a variable holding a pipeline trigger token of the target project. The difference is who the started pipeline runs as: with the job token, as the user behind the release job, who must be allowed to start pipelines in the target project; with a trigger token, as its owner, whoever merged.

A CI/CD component of this shape (version, release, a shared defaults layer, product and tag-format as inputs) lives on my instance under devops/ci-cd-components/release-tools.

GitHub Actions and Forgejo Actions

permissions: { contents: write }
jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with: { fetch-depth: 0 }
      - run: yasrt next --output .release.env
      - run: make build                    # gates on RELEASE_STATUS inside
      - run: yasrt release
        env: { GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} }   # FORGEJO_TOKEN on Forgejo

 

A push with GITHUB_TOKEN starts no workflow, so the changelog commit does not release itself. Where the platform cannot be told from its variables, say --forge gitlab|github|forgejo or set YASRT_FORGE. Complete files are under docs/examples in the repository.

Next

Back to yasrt

The overview: the problem, the fit check, three commands, every chapter.

yasrt →
Back to yasrt

Configuration and hooks

One line per repository, organisation defaults underneath, hooks at five points of the release.

Read →
Configuration and hooks