diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6f7e7b..e7ba724 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -158,7 +158,13 @@ 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. + # 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" > "$RUNNER_TEMP/gpg.key" + - name: Binaries, archives, Linux packages, the GitHub release and the Homebrew cask uses: goreleaser/goreleaser-action@v7 with: version: '~> v2' @@ -167,6 +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: ${{ runner.temp }}/gpg.key + NFPM_PASSPHRASE: ${{ secrets.MAVEN_GPG_PRIVATE_KEY_PASSWORD }} + - 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. @@ -183,6 +194,103 @@ 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 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/') + # 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 + 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.ref == 'refs/heads/main' + env: + SECRET: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} + run: | + 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: + version: '~> v2' + args: release --snapshot --clean + 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 + 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.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 }} + 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/.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 diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 8ead9bc..ed2f33f 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -38,11 +38,48 @@ 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 + # 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 + 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: '{{ envOrDefault "NFPM_KEY_FILE" "" }}' + rpm: + signature: + key_file: '{{ envOrDefault "NFPM_KEY_FILE" "" }}' + 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. diff --git a/CHANGELOG.md b/CHANGELOG.md index 9973773..3c89e7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,8 @@ - Experiment templates, environments, teams, property definitions, hubs and integrations have a `diff`, and their `apply` a `--dry-run`. The actions a team is given when sent none, and the target attributes a webhook reports when sent none, are not differences. +- 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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1e8bc56..40ed970 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -95,7 +95,11 @@ 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 (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 repository. A stable release also moves the major tag (`v6`) that `uses: steadybit/cli@v6` diff --git a/README.md b/README.md index 90cccf1..e852d6a 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,18 @@ completions: brew install steadybit/tap/steadybit ``` +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 -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 -fsSLO 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 [releases](https://github.com/steadybit/cli/releases) (`checksums.txt` lists their SHA-256) and put `steadybit` on your `PATH`: