From e1478450ef672318d11360d563b3d9d0f9fefc80 Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Tue, 29 Sep 2026 11:48:27 +0200 Subject: [PATCH 01/11] build: Linux packages in the Steadybit apt and yum repositories Linux users had Homebrew, a download or go install, none of which their system keeps up to date. goreleaser now builds signed `steadybit-cli` .deb and .rpm packages, with shell completions, attaches them to every release, and a stable release publishes them to packages.steadybit.com next to the agent's packages, with the same signing key and upload action the extensions use. A new job builds the packages on every other run, installs the .deb on Ubuntu and the .rpm on Fedora and runs them. On main, and when started by hand, it also uploads them to the dev repositories, so the upload is exercised before a release depends on it. Pull requests build them unsigned and upload nothing. --- .github/workflows/ci.yml | 75 +++++++++++++++++++++++++++++++++++++++- .goreleaser.yaml | 31 +++++++++++++++++ CHANGELOG.md | 6 ++++ CONTRIBUTING.md | 4 ++- README.md | 22 ++++++++++++ 5 files changed, 136 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6f7e7b..63f47cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -151,6 +151,8 @@ jobs: runs-on: ubuntu-latest permissions: contents: write + # Uploading the Linux packages authenticates to Google Cloud as the workflow. + id-token: write steps: - uses: actions/checkout@v7 with: @@ -158,7 +160,12 @@ jobs: - uses: actions/setup-go@v7 with: go-version-file: go.mod - - name: Binaries, archives, the GitHub release and the Homebrew cask + # The key the agent's packages are signed with, so apt and yum trust the CLI's too. + - name: Export the package signing key + env: + SECRET: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} + run: echo -n "$SECRET" > gpg.key + - name: Binaries, archives, Linux packages, the GitHub release and the Homebrew cask uses: goreleaser/goreleaser-action@v7 with: version: '~> v2' @@ -167,6 +174,19 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Pushes the cask to steadybit/homebrew-tap, which GITHUB_TOKEN cannot write to. HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + NFPM_KEY_FILE: gpg.key + NFPM_DEFAULT_PASSPHRASE: ${{ secrets.MAVEN_GPG_PRIVATE_KEY_PASSWORD }} + - name: Publish the Linux packages to packages.steadybit.com + if: ${{ !contains(github.ref_name, '-') }} + uses: steadybit/.github/actions/gar-upload-linux-packages@main + with: + workload_identity_provider: ${{ secrets.GCP_ARTIFACT_REGISTRY_IDENTITY_PROVIDER }} + service_account: ${{ vars.GCP_ARTIFACT_REGISTRY_PROJECT_SA }} + project_id: ${{ vars.GCP_ARTIFACT_REGISTRY_PROJECT_ID }} + location: ${{ vars.GCP_ARTIFACT_REGISTRY_PROJECT_LOCATION }} + deb_repository: deb-public + yum_repository: yum-public + packages_dir: ./dist # `uses: steadybit/cli@v6` resolves through the major tag, so each stable release # moves it. A push with GITHUB_TOKEN triggers no workflow, so the tag does not start # another release. @@ -183,6 +203,59 @@ jobs: with: command: monitor + # The packages of every build, installed and run on the distributions they are for. On + # main, and when started by hand, they also go to the dev repositories, which is how the + # upload is checked before a release depends on it. Pull requests build them unsigned. + linux-packages: + if: github.event_name != 'schedule' && !startsWith(github.ref, 'refs/tags/') + needs: [verify] + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + - uses: actions/setup-go@v7 + with: + go-version-file: go.mod + - name: Export the package signing key + if: github.event_name != 'pull_request' + env: + SECRET: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} + run: | + echo -n "$SECRET" > gpg.key + echo "NFPM_KEY_FILE=gpg.key" >> "$GITHUB_ENV" + - name: Build the packages + uses: goreleaser/goreleaser-action@v7 + with: + version: '~> v2' + args: release --snapshot --clean + env: + HOMEBREW_TAP_TOKEN: unused + NFPM_DEFAULT_PASSPHRASE: ${{ secrets.MAVEN_GPG_PRIVATE_KEY_PASSWORD }} + - name: Install the .deb and run it + run: | + sudo apt-get install -y ./dist/steadybit-cli_amd64.deb + steadybit -V + test -f /usr/share/bash-completion/completions/steadybit + - name: Install the .rpm and run it + run: | + docker run --rm -v "$PWD/dist:/dist" fedora:latest \ + sh -c 'dnf install -y -q /dist/steadybit-cli_amd64.rpm && steadybit -V' + - name: Publish to the dev repositories + if: github.event_name != 'pull_request' + uses: steadybit/.github/actions/gar-upload-linux-packages@main + with: + workload_identity_provider: ${{ secrets.GCP_ARTIFACT_REGISTRY_IDENTITY_PROVIDER }} + service_account: ${{ vars.GCP_ARTIFACT_REGISTRY_PROJECT_SA }} + project_id: ${{ vars.GCP_ARTIFACT_REGISTRY_PROJECT_ID }} + location: ${{ vars.GCP_ARTIFACT_REGISTRY_PROJECT_LOCATION }} + deb_repository: deb-dev + yum_repository: yum-dev + packages_dir: ./dist + # The action downloads from the release, so it can only be checked once one exists. verify-action: if: startsWith(github.ref, 'refs/tags/v') diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 8ead9bc..67075cd 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -38,6 +38,37 @@ archives: formats: [zip] files: [LICENSE, README.md, CHANGELOG.md, completions/*] +# .deb and .rpm packages for Linux, attached to the release and published to the apt and +# yum repositories at packages.steadybit.com, next to steadybit-agent. Without the version +# in the file name, as the archives, so that releases/latest/download/ is stable. +nfpms: + - id: steadybit + package_name: steadybit-cli + file_name_template: '{{ .PackageName }}_{{ .Arch }}' + formats: [deb, rpm] + vendor: Steadybit GmbH + homepage: https://github.com/steadybit/cli + maintainer: Steadybit + description: Command-line interface for the Steadybit chaos engineering platform + license: MIT + contents: + - src: completions/steadybit.bash + dst: /usr/share/bash-completion/completions/steadybit + - src: completions/_steadybit + dst: /usr/share/zsh/vendor-completions/_steadybit + - src: completions/steadybit.fish + dst: /usr/share/fish/vendor_completions.d/steadybit.fish + - src: LICENSE + dst: /usr/share/doc/steadybit-cli/copyright + # Signed with the key the agent's packages are signed with, when CI provides it; a + # local snapshot builds them unsigned. + deb: + signature: + key_file: '{{ if index .Env "NFPM_KEY_FILE" }}{{ .Env.NFPM_KEY_FILE }}{{ end }}' + rpm: + signature: + key_file: '{{ if index .Env "NFPM_KEY_FILE" }}{{ .Env.NFPM_KEY_FILE }}{{ end }}' + checksum: name_template: checksums.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index a2723f4..e311ec6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v6.1.0 + +- Linux packages: `steadybit-cli` is published to the apt and yum repositories at + packages.steadybit.com, next to the agent, so `apt-get install steadybit-cli` or + `dnf install steadybit-cli` installs it and the system's updates keep it current. The + signed `.deb` and `.rpm` files, with shell completions, are attached to every release. ## v6.0.1 - `experiment apply` and `experiment run` no longer send a `version` from the file. The diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1e8bc56..e079ff5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -95,7 +95,9 @@ not generated structs, so that fields the spec does not know yet are never dropp ## Releasing Releases are published by CI, not from a workstation: pushing a `v*` tag builds the -binaries with goreleaser, creates the GitHub release with them, pushes the Homebrew cask to +binaries and the signed `.deb`/`.rpm` packages with goreleaser, creates the GitHub release +with them, publishes the packages to the apt and yum repositories at packages.steadybit.com, +pushes the Homebrew cask to [steadybit/homebrew-tap](https://github.com/steadybit/homebrew-tap), and pushes the Docker image. The cask needs the `HOMEBREW_TAP_TOKEN` secret, a token that can write to that repository. A stable release also moves the major tag (`v6`) that `uses: steadybit/cli@v6` diff --git a/README.md b/README.md index 91871d1..e80fb84 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,28 @@ completions: brew install steadybit/tap/steadybit ``` +On Debian or Ubuntu, from the package repository the Steadybit agent comes from, so that +`apt upgrade` updates the CLI too: + +```sh +sudo mkdir -p /etc/apt/keyrings +curl -fsSL https://europe-west1-apt.pkg.dev/doc/repo-signing-key.gpg | sudo tee /etc/apt/keyrings/steadybit.asc >/dev/null +printf 'Types: deb\nURIs: https://packages.steadybit.com\nSuites: deb-public\nComponents: main\nSigned-By: /etc/apt/keyrings/steadybit.asc\n' \ + | sudo tee /etc/apt/sources.list.d/steadybit.sources >/dev/null +sudo apt-get update && sudo apt-get install steadybit-cli +``` + +On Fedora, RHEL or Amazon Linux, the same way with `dnf` (or `yum`): + +```sh +printf '[steadybit]\nname=steadybit\nbaseurl=https://packages.steadybit.com/yum-public\nenabled=1\ngpgcheck=0\n' \ + | sudo tee /etc/yum.repos.d/steadybit.repo >/dev/null +sudo dnf install steadybit-cli +``` + +The `.deb` and `.rpm` files are also attached to every release, as +`steadybit-cli_amd64.deb` and so on. + Or download the archive for your platform from the [releases](https://github.com/steadybit/cli/releases) (`checksums.txt` lists their SHA-256) and put `steadybit` on your `PATH`: From 80989c980ff58467abcbcc447cfaa76f67b00495 Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Tue, 29 Sep 2026 11:51:55 +0200 Subject: [PATCH 02/11] build: pass the package signing passphrase under the name goreleaser reads goreleaser reads NFPM__PASSPHRASE and then NFPM_PASSPHRASE. The extensions' packages have no id, hence their NFPM_DEFAULT_PASSPHRASE; the CLI's are named steadybit, so the generic variable is the one that reaches them. --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63f47cd..bb92809 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -175,7 +175,7 @@ jobs: # Pushes the cask to steadybit/homebrew-tap, which GITHUB_TOKEN cannot write to. HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} NFPM_KEY_FILE: gpg.key - NFPM_DEFAULT_PASSPHRASE: ${{ secrets.MAVEN_GPG_PRIVATE_KEY_PASSWORD }} + NFPM_PASSPHRASE: ${{ secrets.MAVEN_GPG_PRIVATE_KEY_PASSWORD }} - name: Publish the Linux packages to packages.steadybit.com if: ${{ !contains(github.ref_name, '-') }} uses: steadybit/.github/actions/gar-upload-linux-packages@main @@ -234,7 +234,7 @@ jobs: args: release --snapshot --clean env: HOMEBREW_TAP_TOKEN: unused - NFPM_DEFAULT_PASSPHRASE: ${{ secrets.MAVEN_GPG_PRIVATE_KEY_PASSWORD }} + NFPM_PASSPHRASE: ${{ secrets.MAVEN_GPG_PRIVATE_KEY_PASSWORD }} - name: Install the .deb and run it run: | sudo apt-get install -y ./dist/steadybit-cli_amd64.deb From 15d40e7a7909d70ea6e2137dc2699654bce2c8d7 Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Tue, 29 Sep 2026 11:55:25 +0200 Subject: [PATCH 03/11] build: publish the Linux packages only once Google Cloud allows it The workload identity provider the extensions upload through does not trust steadybit/cli yet, so the upload is refused. Both uploads now wait for the repository variable PUBLISH_LINUX_PACKAGES, and main and releases keep working until then: the packages are still built, tested and attached to the release. --- .github/workflows/ci.yml | 6 ++++-- CONTRIBUTING.md | 4 +++- README.md | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb92809..44a1598 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -176,8 +176,10 @@ jobs: HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} NFPM_KEY_FILE: gpg.key NFPM_PASSPHRASE: ${{ secrets.MAVEN_GPG_PRIVATE_KEY_PASSWORD }} + # Off until Google Cloud lets this repository upload: the workload identity provider + # has to trust steadybit/cli first. The repository variable turns it on. - name: Publish the Linux packages to packages.steadybit.com - if: ${{ !contains(github.ref_name, '-') }} + if: ${{ !contains(github.ref_name, '-') && vars.PUBLISH_LINUX_PACKAGES == 'true' }} uses: steadybit/.github/actions/gar-upload-linux-packages@main with: workload_identity_provider: ${{ secrets.GCP_ARTIFACT_REGISTRY_IDENTITY_PROVIDER }} @@ -245,7 +247,7 @@ jobs: docker run --rm -v "$PWD/dist:/dist" fedora:latest \ sh -c 'dnf install -y -q /dist/steadybit-cli_amd64.rpm && steadybit -V' - name: Publish to the dev repositories - if: github.event_name != 'pull_request' + if: github.event_name != 'pull_request' && vars.PUBLISH_LINUX_PACKAGES == 'true' uses: steadybit/.github/actions/gar-upload-linux-packages@main with: workload_identity_provider: ${{ secrets.GCP_ARTIFACT_REGISTRY_IDENTITY_PROVIDER }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e079ff5..40ed970 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -96,7 +96,9 @@ not generated structs, so that fields the spec does not know yet are never dropp Releases are published by CI, not from a workstation: pushing a `v*` tag builds the binaries and the signed `.deb`/`.rpm` packages with goreleaser, creates the GitHub release -with them, publishes the packages to the apt and yum repositories at packages.steadybit.com, +with them, publishes the packages to the apt and yum repositories at packages.steadybit.com (when +the repository variable `PUBLISH_LINUX_PACKAGES` is `true`; that needs the Google Cloud +workload identity provider to trust `steadybit/cli`), pushes the Homebrew cask to [steadybit/homebrew-tap](https://github.com/steadybit/homebrew-tap), and pushes the Docker image. The cask needs the `HOMEBREW_TAP_TOKEN` secret, a token that can write to that diff --git a/README.md b/README.md index e80fb84..44289ac 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ completions: brew install steadybit/tap/steadybit ``` + On Debian or Ubuntu, from the package repository the Steadybit agent comes from, so that `apt upgrade` updates the CLI too: From 87b42dbbc5967d16729182d0ae9833308622de04 Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Tue, 29 Sep 2026 11:55:40 +0200 Subject: [PATCH 04/11] docs: install the attached Linux packages, until the repositories carry them --- CHANGELOG.md | 7 ++----- README.md | 23 ++++------------------- 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e311ec6..7c856ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,8 @@ ## v6.1.0 -- Linux packages: `steadybit-cli` is published to the apt and yum repositories at - packages.steadybit.com, next to the agent, so `apt-get install steadybit-cli` or - `dnf install steadybit-cli` installs it and the system's updates keep it current. The - signed `.deb` and `.rpm` files, with shell completions, are attached to every release. -## v6.0.1 +- Linux packages: a signed `steadybit-cli` `.deb` and `.rpm`, with shell completions, are + attached to every release (`steadybit-cli_amd64.deb`, `steadybit-cli_arm64.rpm`, …). - `experiment apply` and `experiment run` no longer send a `version` from the file. The platform now rejects a stale one with `409 Conflict`, so a file downloaded from the UI, diff --git a/README.md b/README.md index 44289ac..25d6b19 100644 --- a/README.md +++ b/README.md @@ -25,29 +25,14 @@ completions: brew install steadybit/tap/steadybit ``` - -On Debian or Ubuntu, from the package repository the Steadybit agent comes from, so that -`apt upgrade` updates the CLI too: +On Debian, Ubuntu, Fedora or RHEL, install the package attached to every release, which also +installs the shell completions: ```sh -sudo mkdir -p /etc/apt/keyrings -curl -fsSL https://europe-west1-apt.pkg.dev/doc/repo-signing-key.gpg | sudo tee /etc/apt/keyrings/steadybit.asc >/dev/null -printf 'Types: deb\nURIs: https://packages.steadybit.com\nSuites: deb-public\nComponents: main\nSigned-By: /etc/apt/keyrings/steadybit.asc\n' \ - | sudo tee /etc/apt/sources.list.d/steadybit.sources >/dev/null -sudo apt-get update && sudo apt-get install steadybit-cli +curl -sLO https://github.com/steadybit/cli/releases/latest/download/steadybit-cli_amd64.deb +sudo apt-get install ./steadybit-cli_amd64.deb # or: sudo dnf install ./steadybit-cli_amd64.rpm ``` -On Fedora, RHEL or Amazon Linux, the same way with `dnf` (or `yum`): - -```sh -printf '[steadybit]\nname=steadybit\nbaseurl=https://packages.steadybit.com/yum-public\nenabled=1\ngpgcheck=0\n' \ - | sudo tee /etc/yum.repos.d/steadybit.repo >/dev/null -sudo dnf install steadybit-cli -``` - -The `.deb` and `.rpm` files are also attached to every release, as -`steadybit-cli_amd64.deb` and so on. - Or download the archive for your platform from the [releases](https://github.com/steadybit/cli/releases) (`checksums.txt` lists their SHA-256) and put `steadybit` on your `PATH`: From c95ef46de617d4f349fe3d829c6e36d2ed99e81d Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Tue, 29 Sep 2026 11:56:04 +0200 Subject: [PATCH 05/11] docs: one download per package format --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 25d6b19..620cc23 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,12 @@ On Debian, Ubuntu, Fedora or RHEL, install the package attached to every release installs the shell completions: ```sh +# Debian, Ubuntu curl -sLO https://github.com/steadybit/cli/releases/latest/download/steadybit-cli_amd64.deb -sudo apt-get install ./steadybit-cli_amd64.deb # or: sudo dnf install ./steadybit-cli_amd64.rpm +sudo apt-get install ./steadybit-cli_amd64.deb +# Fedora, RHEL, Amazon Linux +curl -sLO https://github.com/steadybit/cli/releases/latest/download/steadybit-cli_amd64.rpm +sudo dnf install ./steadybit-cli_amd64.rpm ``` Or download the archive for your platform from the From 8e31479ee252c7ef94720c8a8e9e0b127ae86c06 Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Tue, 29 Sep 2026 14:10:51 +0200 Subject: [PATCH 06/11] fix: keep the released v6.0.1 changelog entry under its own heading The heading had been renamed to v6.1.0, which moved the v6.0.1 fix into the next release's notes. The Linux packages get their own v6.1.0 section above it. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c856ed..839d531 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ - Linux packages: a signed `steadybit-cli` `.deb` and `.rpm`, with shell completions, are attached to every release (`steadybit-cli_amd64.deb`, `steadybit-cli_arm64.rpm`, …). +## v6.0.1 + - `experiment apply` and `experiment run` no longer send a `version` from the file. The platform now rejects a stale one with `409 Conflict`, so a file downloaded from the UI, which carries one, failed to apply once the experiment was edited after the download. From c046459e719f7786faade5c3cb0fec77006f3a72 Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Tue, 29 Sep 2026 14:11:37 +0200 Subject: [PATCH 07/11] fix: install the zsh completion where Fedora, RHEL and Amazon Linux read it Their zsh only looks in /usr/share/zsh/site-functions, so the rpm's completion in vendor-completions, which is Debian's directory, was never loaded. The deb keeps vendor-completions. --- .goreleaser.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 67075cd..d39c475 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -54,8 +54,14 @@ nfpms: contents: - src: completions/steadybit.bash dst: /usr/share/bash-completion/completions/steadybit + # Debian and Ubuntu read zsh completions from vendor-completions; Fedora, RHEL and + # Amazon Linux only from site-functions. - src: completions/_steadybit dst: /usr/share/zsh/vendor-completions/_steadybit + packager: deb + - src: completions/_steadybit + dst: /usr/share/zsh/site-functions/_steadybit + packager: rpm - src: completions/steadybit.fish dst: /usr/share/fish/vendor_completions.d/steadybit.fish - src: LICENSE From ff1fada5c115e10642e27a04be1dad5463470c98 Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Tue, 29 Sep 2026 14:11:37 +0200 Subject: [PATCH 08/11] build: read the package signing key path with envOrDefault The same, shorter than the conditional on .Env it replaces in both places. --- .goreleaser.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index d39c475..1f59e4b 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -70,10 +70,10 @@ nfpms: # local snapshot builds them unsigned. deb: signature: - key_file: '{{ if index .Env "NFPM_KEY_FILE" }}{{ .Env.NFPM_KEY_FILE }}{{ end }}' + key_file: '{{ envOrDefault "NFPM_KEY_FILE" "" }}' rpm: signature: - key_file: '{{ if index .Env "NFPM_KEY_FILE" }}{{ .Env.NFPM_KEY_FILE }}{{ end }}' + key_file: '{{ envOrDefault "NFPM_KEY_FILE" "" }}' checksum: name_template: checksums.txt From 26ae7dc8f109c2286827221c6231ff830d2ce505 Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Tue, 29 Sep 2026 14:12:04 +0200 Subject: [PATCH 09/11] fix: give every snapshot its own package version All snapshots were 6.0.2-next, so after the first upload the dev repositories rejected each later one, as updated packages must bear a new version, and the upload still reported success. The commit timestamp makes each version newer than the last, as in the extensions: the deb is 6.0.2~-next and the rpm 6.0.2~_next-1, both sorting before the 6.0.2 release. --- .goreleaser.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 1f59e4b..ed2f33f 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -79,7 +79,7 @@ checksum: name_template: checksums.txt snapshot: - version_template: '{{ incpatch .Version }}-next' + version_template: '{{ incpatch .Version }}-{{ .CommitTimestamp }}-next' release: # The changelog is written by hand in CHANGELOG.md. From d1b5053ffdd4132f329aaa35c54f919861bc1a99 Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Tue, 29 Sep 2026 14:12:59 +0200 Subject: [PATCH 10/11] fix: release the Linux packages without blocking the rest of a release The signing key was written to gpg.key in the checkout, and goreleaser refuses to release from a tree with untracked files, so every tag would have failed. Both jobs now write it to $RUNNER_TEMP and remove it after goreleaser, whatever the outcome, and /gpg.key is git-ignored in case it ever lands in the checkout again. The public upload ran before the major tag was moved, so a failing upload left v6 where it was and skipped verify-action. It moves to a job of its own after the release, which downloads the signed packages from it, installs the amd64 .deb and checks that it reports the tag's version before uploading: the install checks otherwise only ever ran on unsigned snapshots. The production key signed snapshots of any branch started by hand. Only main is signed and uploaded to the dev repositories now; other refs build unsigned and upload nothing. And, as docker-build, linux-packages waits for api-compatibility, so nothing reaches the dev repositories from a build whose checks fail. --- .github/workflows/ci.yml | 81 ++++++++++++++++++++++++++++------------ .gitignore | 3 ++ 2 files changed, 60 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44a1598..e7ba724 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -151,8 +151,6 @@ jobs: runs-on: ubuntu-latest permissions: contents: write - # Uploading the Linux packages authenticates to Google Cloud as the workflow. - id-token: write steps: - uses: actions/checkout@v7 with: @@ -161,10 +159,11 @@ jobs: with: go-version-file: go.mod # The key the agent's packages are signed with, so apt and yum trust the CLI's too. + # Outside the checkout: goreleaser refuses to release from a tree with untracked files. - name: Export the package signing key env: SECRET: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} - run: echo -n "$SECRET" > gpg.key + run: echo -n "$SECRET" > "$RUNNER_TEMP/gpg.key" - name: Binaries, archives, Linux packages, the GitHub release and the Homebrew cask uses: goreleaser/goreleaser-action@v7 with: @@ -174,21 +173,11 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Pushes the cask to steadybit/homebrew-tap, which GITHUB_TOKEN cannot write to. HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} - NFPM_KEY_FILE: gpg.key + NFPM_KEY_FILE: ${{ runner.temp }}/gpg.key NFPM_PASSPHRASE: ${{ secrets.MAVEN_GPG_PRIVATE_KEY_PASSWORD }} - # Off until Google Cloud lets this repository upload: the workload identity provider - # has to trust steadybit/cli first. The repository variable turns it on. - - name: Publish the Linux packages to packages.steadybit.com - if: ${{ !contains(github.ref_name, '-') && vars.PUBLISH_LINUX_PACKAGES == 'true' }} - uses: steadybit/.github/actions/gar-upload-linux-packages@main - with: - workload_identity_provider: ${{ secrets.GCP_ARTIFACT_REGISTRY_IDENTITY_PROVIDER }} - service_account: ${{ vars.GCP_ARTIFACT_REGISTRY_PROJECT_SA }} - project_id: ${{ vars.GCP_ARTIFACT_REGISTRY_PROJECT_ID }} - location: ${{ vars.GCP_ARTIFACT_REGISTRY_PROJECT_LOCATION }} - deb_repository: deb-public - yum_repository: yum-public - packages_dir: ./dist + - name: Remove the package signing key + if: always() + run: rm -f "$RUNNER_TEMP/gpg.key" # `uses: steadybit/cli@v6` resolves through the major tag, so each stable release # moves it. A push with GITHUB_TOKEN triggers no workflow, so the tag does not start # another release. @@ -205,12 +194,53 @@ jobs: with: command: monitor + # The signed packages of a release, installed before they are published to the apt and yum + # repositories. A job of its own, so that a failing upload leaves the release, the cask and + # the major tag in place and does not skip verify-action. + release-linux-packages: + if: startsWith(github.ref, 'refs/tags/v') + needs: release + runs-on: ubuntu-latest + permissions: + contents: read + # Uploading authenticates to Google Cloud as the workflow. + id-token: write + steps: + - name: Download the packages from the release + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release download "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" \ + --dir packages --pattern 'steadybit-cli_*.deb' --pattern 'steadybit-cli_*.rpm' + ls -l packages + - name: Install the .deb and run it + run: | + sudo apt-get install -y ./packages/steadybit-cli_amd64.deb + steadybit -V + test "$(steadybit -V)" = "${GITHUB_REF_NAME#v}" + # Off until Google Cloud lets this repository upload: the workload identity provider + # has to trust steadybit/cli first. The repository variable turns it on. Prereleases + # stay off the repositories, as they stay off releases/latest. + - name: Publish the Linux packages to packages.steadybit.com + if: ${{ !contains(github.ref_name, '-') && vars.PUBLISH_LINUX_PACKAGES == 'true' }} + uses: steadybit/.github/actions/gar-upload-linux-packages@main + with: + workload_identity_provider: ${{ secrets.GCP_ARTIFACT_REGISTRY_IDENTITY_PROVIDER }} + service_account: ${{ vars.GCP_ARTIFACT_REGISTRY_PROJECT_SA }} + project_id: ${{ vars.GCP_ARTIFACT_REGISTRY_PROJECT_ID }} + location: ${{ vars.GCP_ARTIFACT_REGISTRY_PROJECT_LOCATION }} + deb_repository: deb-public + yum_repository: yum-public + packages_dir: ./packages + # The packages of every build, installed and run on the distributions they are for. On - # main, and when started by hand, they also go to the dev repositories, which is how the - # upload is checked before a release depends on it. Pull requests build them unsigned. + # main they are also signed and go to the dev repositories, which is how the upload is + # checked before a release depends on it. Other refs, pull requests and branches started + # by hand, build them unsigned and upload nothing, so the production key signs only main. linux-packages: if: github.event_name != 'schedule' && !startsWith(github.ref, 'refs/tags/') - needs: [verify] + # As docker-build: nothing reaches the dev repositories from a build whose checks fail. + needs: [verify, api-compatibility] runs-on: ubuntu-latest permissions: contents: read @@ -223,12 +253,12 @@ jobs: with: go-version-file: go.mod - name: Export the package signing key - if: github.event_name != 'pull_request' + if: github.ref == 'refs/heads/main' env: SECRET: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} run: | - echo -n "$SECRET" > gpg.key - echo "NFPM_KEY_FILE=gpg.key" >> "$GITHUB_ENV" + echo -n "$SECRET" > "$RUNNER_TEMP/gpg.key" + echo "NFPM_KEY_FILE=$RUNNER_TEMP/gpg.key" >> "$GITHUB_ENV" - name: Build the packages uses: goreleaser/goreleaser-action@v7 with: @@ -237,6 +267,9 @@ jobs: env: HOMEBREW_TAP_TOKEN: unused NFPM_PASSPHRASE: ${{ secrets.MAVEN_GPG_PRIVATE_KEY_PASSWORD }} + - name: Remove the package signing key + if: always() + run: rm -f "$RUNNER_TEMP/gpg.key" - name: Install the .deb and run it run: | sudo apt-get install -y ./dist/steadybit-cli_amd64.deb @@ -247,7 +280,7 @@ jobs: docker run --rm -v "$PWD/dist:/dist" fedora:latest \ sh -c 'dnf install -y -q /dist/steadybit-cli_amd64.rpm && steadybit -V' - name: Publish to the dev repositories - if: github.event_name != 'pull_request' && vars.PUBLISH_LINUX_PACKAGES == 'true' + if: github.ref == 'refs/heads/main' && vars.PUBLISH_LINUX_PACKAGES == 'true' uses: steadybit/.github/actions/gar-upload-linux-packages@main with: workload_identity_provider: ${{ secrets.GCP_ARTIFACT_REGISTRY_IDENTITY_PROVIDER }} diff --git a/.gitignore b/.gitignore index 85164cc..bfbec30 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,6 @@ node_modules .env.local .env.*.local /completions + +# The package signing key, should a job ever write it into the checkout. +/gpg.key From 861863ecb850882999c892adf10711fe70728b9a Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Tue, 29 Sep 2026 14:13:09 +0200 Subject: [PATCH 11/11] docs: fail the package download on an HTTP error, and say from which release Without -f, curl saves a 404 page as the package and apt or dnf then fails on it with a confusing error. Releases before v6.1.0 carry no packages. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 620cc23..97311bb 100644 --- a/README.md +++ b/README.md @@ -25,15 +25,15 @@ completions: brew install steadybit/tap/steadybit ``` -On Debian, Ubuntu, Fedora or RHEL, install the package attached to every release, which also -installs the shell completions: +On Debian, Ubuntu, Fedora or RHEL, install the package attached to every release from +v6.1.0 on, which also installs the shell completions: ```sh # Debian, Ubuntu -curl -sLO https://github.com/steadybit/cli/releases/latest/download/steadybit-cli_amd64.deb +curl -fsSLO https://github.com/steadybit/cli/releases/latest/download/steadybit-cli_amd64.deb sudo apt-get install ./steadybit-cli_amd64.deb # Fedora, RHEL, Amazon Linux -curl -sLO https://github.com/steadybit/cli/releases/latest/download/steadybit-cli_amd64.rpm +curl -fsSLO https://github.com/steadybit/cli/releases/latest/download/steadybit-cli_amd64.rpm sudo dnf install ./steadybit-cli_amd64.rpm ```