Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 76 additions & 39 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,16 +129,32 @@ jobs:
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true

# One CLI archive per platform, each built and smoke-tested on its own runner. macOS builds link libfido2
# statically and support security keys; Linux builds use the nofido tag (go-libfido2 links libfido2 dynamically
# there). Intel Macs build from source. `rack-gateway update` and the install script download these archives, so
# each one is published with its checksum and its build provenance attestation bundle.
release_build:
runs-on: ubuntu-24.04
name: release_build (${{ matrix.platform }})
runs-on: ${{ matrix.runner }}
needs: verify_tag
strategy:
fail-fast: true
matrix:
include:
- platform: linux-amd64
runner: ubuntu-24.04
- platform: linux-arm64
runner: ubuntu-24.04-arm
- platform: darwin-arm64
runner: macos-15
permissions:
contents: read
checks: read # wait-for-checks.js reads check runs for the tagged commit
id-token: write # sign the build provenance attestation
attestations: write
env:
VERSION: ${{ needs.verify_tag.outputs.version }}
ASSET: rack-gateway-${{ matrix.platform }}.tar.gz
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand All @@ -152,30 +168,22 @@ jobs:
go-version: "1.27.3"
cache: false

- name: Build binary
# The formulae come from the runner image's Homebrew snapshot (no auto-update).
- name: Install libfido2
if: startsWith(matrix.platform, 'darwin-')
env:
HOMEBREW_NO_AUTO_UPDATE: "1"
HOMEBREW_NO_INSTALL_CLEANUP: "1"
run: brew install libfido2 openssl@3

- name: Build archive and checksum
env:
CGO_ENABLED: 0
GOOS: linux
GOARCH: amd64
GOFLAGS: -mod=readonly
run: |
set -euo pipefail
BUILD_TIME="$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
go build \
-tags nofido \
-buildvcs=false \
-trimpath \
-ldflags "-s -w -X main.version=${VERSION} -X main.buildTime=${BUILD_TIME}" \
-o rack-gateway-linux-amd64 \
./cmd/rack-gateway/
./rack-gateway-linux-amd64 version | grep -F "client: ${VERSION}"

- name: Create archive and checksum
run: |
set -euo pipefail
mkdir -p dist
tar czf dist/rack-gateway-linux-amd64.tar.gz rack-gateway-linux-amd64
(cd dist && sha256sum rack-gateway-linux-amd64.tar.gz > rack-gateway-linux-amd64.tar.gz.sha256)
REPOSITORY: ${{ github.repository }}
run: ./scripts/build-cli-release.sh "$VERSION" "$REPOSITORY"

- name: Check the archive is for this platform
run: test -f "dist/${ASSET}"

- name: Wait for CI and E2E checks on the tagged commit
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
Expand All @@ -185,14 +193,24 @@ jobs:
await script({ github, context, core });

- name: Attest binary build provenance
id: attest
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: dist/rack-gateway-linux-amd64.tar.gz
subject-path: dist/${{ env.ASSET }}

- name: Save the attestation bundle
env:
BUNDLE: ${{ steps.attest.outputs.bundle-path }}
run: cp "$BUNDLE" "dist/${ASSET}.sigstore.json"

- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: rack-gateway-linux-amd64
path: dist/
name: rack-gateway-${{ matrix.platform }}
path: |
dist/${{ env.ASSET }}
dist/${{ env.ASSET }}.sha256
dist/${{ env.ASSET }}.sigstore.json
if-no-files-found: error

release_create:
runs-on: ubuntu-24.04
Expand All @@ -208,33 +226,50 @@ jobs:
REPOSITORY: ${{ github.repository }}
IMAGE_DIGEST: ${{ needs.docker_build.outputs.digest }}
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: rack-gateway-linux-amd64
pattern: rack-gateway-*
path: dist
merge-multiple: true

- name: Verify checksum
- name: Verify checksums
run: |
set -euo pipefail
cd dist
sha256sum --check --strict rack-gateway-linux-amd64.tar.gz.sha256
for platform in linux-amd64 linux-arm64 darwin-arm64; do
sha256sum --check --strict "rack-gateway-${platform}.tar.gz.sha256"
test -s "rack-gateway-${platform}.tar.gz.sigstore.json"
done

# Published as the release's install.sh, so .../releases/latest/download/install.sh is the latest installer.
- name: Add the install script
run: cp scripts/install-release.sh dist/install.sh

- name: Generate changelog
run: |
set -euo pipefail
{
echo "## Installation"
echo "## Install the CLI"
echo
echo "macOS (Apple Silicon) and Linux (x86_64 and arm64):"
echo
echo '```sh'
echo "curl -fsSL https://github.com/${REPOSITORY}/releases/download/${RELEASE_TAG}/install.sh | RACK_GATEWAY_VERSION=${RELEASE_TAG} sh"
echo '```'
echo
echo "Already installed? Run \`rack-gateway update\`."
echo
echo "### Linux (x86_64)"
echo "The macOS build supports security keys. The Linux builds don't: build the CLI from source for that."
echo "Each archive has a SHA-256 checksum and a build provenance attestation (\`.sigstore.json\`), which"
echo "the install script and \`rack-gateway update\` check. To check one by hand:"
echo
echo '```sh'
echo "curl -fsSLO https://github.com/${REPOSITORY}/releases/download/${RELEASE_TAG}/rack-gateway-linux-amd64.tar.gz"
echo "curl -fsSLO https://github.com/${REPOSITORY}/releases/download/${RELEASE_TAG}/rack-gateway-linux-amd64.tar.gz.sha256"
echo "sha256sum --check rack-gateway-linux-amd64.tar.gz.sha256"
echo "gh attestation verify rack-gateway-linux-amd64.tar.gz --repo ${REPOSITORY}"
echo "tar xzf rack-gateway-linux-amd64.tar.gz"
echo "sudo install -m 0755 rack-gateway-linux-amd64 /usr/local/bin/rack-gateway"
echo "rack-gateway version"
echo '```'
echo
echo "### Docker image"
Expand All @@ -249,8 +284,10 @@ jobs:
- uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
with:
files: |
dist/rack-gateway-linux-amd64.tar.gz
dist/rack-gateway-linux-amd64.tar.gz.sha256
dist/rack-gateway-*.tar.gz
dist/rack-gateway-*.tar.gz.sha256
dist/rack-gateway-*.tar.gz.sigstore.json
dist/install.sh
prerelease: ${{ contains(env.RELEASE_TAG, '-') }}
body_path: changelog.md
name: rack-gateway v${{ env.RELEASE_VERSION }}
Expand Down
11 changes: 8 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -617,9 +617,14 @@ a PR with green CI (admins can bypass). The Release workflow only runs on `v*` t
The Release workflow (`.github/workflows/release.yml`):
- Checks the tag matches `web/package.json` and waits for CI + E2E to pass on the tagged commit
- Builds the Docker image without any build cache and pushes it by digest, without a tag
- Builds the CLI (`rack-gateway version` reports the release version) with checksums
- Publishes signed build provenance attestations for the image and the CLI archive
- Creates a draft GitHub release with the CLI archive and checksum
- Builds the CLI archives with `scripts/build-cli-release.sh`, each on its own runner: `darwin-arm64` (with security
key support: libfido2 linked statically from Homebrew), `linux-amd64` and `linux-arm64` (`nofido`). The script
checks the binary's `build-info` reports the release version
- Publishes signed build provenance attestations for the image and each CLI archive
- Creates a draft GitHub release with each CLI archive, its checksum and its attestation bundle (`.sigstore.json`),
and `scripts/install-release.sh` as `install.sh`. The install script and `rack-gateway update` download these, so
keep the asset names (`rack-gateway-<os>-<arch>.tar.gz`, holding one `rack-gateway` binary) and the workflow's
path (`.github/workflows/release.yml`, the signer identity the CLI checks) as they are
- Only then tags the image `:vX.Y.Z` and `:<short-sha>` (plus `:latest` when this is the highest stable version,
whatever order releases finish in) and publishes the release. If that last job fails, re-run it: nothing public
points at a release that didn't finish
Expand Down
6 changes: 5 additions & 1 deletion cmd/rack-gateway/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@ func main() {
cli.BuildTime = buildTime

// Execute CLI
if err := cli.Execute(); err != nil {
err := cli.Execute()
if err != nil {
// Errors can quote gateway responses; DisplayError keeps them from driving the terminal.
fmt.Fprintf(os.Stderr, "Error: %s\n", cli.DisplayError(err))
}
cli.PrintUpdateNotice()
if err != nil {
os.Exit(1)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ monitoring service's key, so set them as secrets. An invalid value stops the gat
| `RACK_GATEWAY_URL` | Gateway URL for `RACK_GATEWAY_API_TOKEN`; without one it must match the rack's configured URL. Not allowed with several racks |
| `RACK_GATEWAY_API_TOKEN` | API token for automation |
| `GATEWAY_CLI_CONFIG_DIR` | Override CLI config dir (default `~/.config/rack-gateway`) |
| `RACK_GATEWAY_NO_UPDATE_CHECK` | Set to turn off the daily check for a newer CLI release (see [Updating](/user-guide/cli/installation/#updating)) |
| `CONVOX_APP` | Default app for commands that take `--app` (after `--app` and before `.convox/app`) |

## Redaction
Expand Down
4 changes: 2 additions & 2 deletions docs/src/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ Users install the `rack-gateway` CLI, which handles authentication and wraps Con
## Quick Example

```bash
# Install the rack-gateway CLI
# (download from releases or build from source)
# Install the rack-gateway CLI (macOS on Apple Silicon, Linux)
curl -fsSL https://github.com/DocSpring/rack-gateway/releases/latest/download/install.sh | sh

# Login with Google OAuth
rack-gateway login staging https://gateway.example.com
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,13 @@ commands:
- run:
name: Install the rack-gateway CLI
command: |
VERSION=v0.1.10
BASE=https://github.com/docspring/rack-gateway/releases/download/$VERSION
VERSION=v0.1.29
BASE=https://github.com/DocSpring/rack-gateway/releases/download/$VERSION
curl -fsSLO $BASE/rack-gateway-linux-amd64.tar.gz
curl -fsSLO $BASE/rack-gateway-linux-amd64.tar.gz.sha256
sha256sum --check rack-gateway-linux-amd64.tar.gz.sha256
tar xzf rack-gateway-linux-amd64.tar.gz
sudo install -m 0755 rack-gateway-linux-amd64 /usr/local/bin/rack-gateway
sudo install -m 0755 rack-gateway /usr/local/bin/rack-gateway
```

Set `RACK_GATEWAY_API_TOKEN` (a token with the `cicd` role) and `RACK_GATEWAY_URL` in a CircleCI context or the
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/operations/upgrades.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ After upgrading:
- [ ] Test critical functionality (login, deploy)
- [ ] Run `verify-audit`, or check the next hourly verification on the **Audit Logs** page
- [ ] Monitor error rates for 24 hours
- [ ] Upgrade the CLI to the same release
- [ ] Upgrade the CLI to the same release (`rack-gateway update --version vX.Y.Z`)

## Troubleshooting Upgrades

Expand Down
72 changes: 56 additions & 16 deletions docs/src/content/docs/user-guide/cli/installation.mdx
Original file line number Diff line number Diff line change
@@ -1,28 +1,50 @@
---
title: "CLI Installation"
description: "How to install the rack-gateway CLI on macOS, Linux, and Windows."
description: "How to install and update the rack-gateway CLI on macOS and Linux."
---

import { Aside, Tabs, TabItem, Steps } from '@astrojs/starlight/components';

The `rack-gateway` CLI is distributed as a single binary. Choose the installation method that works best for your platform.

## Download from GitHub Releases (Linux x86_64)
## Install a Release

Each release publishes a Linux x86_64 build with a checksum and a signed build provenance attestation:
Releases include builds for macOS on Apple Silicon and for Linux (x86_64 and arm64). Install the latest one with:

```bash
VERSION=v0.1.10 # see https://github.com/docspring/rack-gateway/releases
curl -fsSLO https://github.com/docspring/rack-gateway/releases/download/$VERSION/rack-gateway-linux-amd64.tar.gz
curl -fsSLO https://github.com/docspring/rack-gateway/releases/download/$VERSION/rack-gateway-linux-amd64.tar.gz.sha256
sha256sum --check rack-gateway-linux-amd64.tar.gz.sha256
gh attestation verify rack-gateway-linux-amd64.tar.gz --repo DocSpring/rack-gateway
tar xzf rack-gateway-linux-amd64.tar.gz
sudo install -m 0755 rack-gateway-linux-amd64 /usr/local/bin/rack-gateway
curl -fsSL https://github.com/DocSpring/rack-gateway/releases/latest/download/install.sh | sh
```

The release build has no WebAuthn support (no CGO), so it verifies MFA with TOTP, YubiKey OTP or backup codes. To
use a security key from the CLI, or on macOS and other platforms, [build from source](#building-from-source).
The script downloads the build for your machine and checks its SHA-256 checksum. When the GitHub CLI (`gh`) is
installed, it also checks the build provenance attestation: the archive must have been built by the repository's
Release workflow. It installs `rack-gateway` into `/usr/local/bin` when you can write to it, or `~/.local/bin`.

| Variable | Description |
|----------|-------------|
| `RACK_GATEWAY_VERSION` | The release to install, such as `v0.1.29` (default: the latest release) |
| `RACK_GATEWAY_INSTALL_DIR` | Where to install the binary |
| `RACK_GATEWAY_REPOSITORY` | The GitHub repository to install from (default: `DocSpring/rack-gateway`) |

The macOS build supports security keys (WebAuthn). The Linux builds don't: they verify MFA with TOTP, YubiKey OTP or
backup codes. To use a security key from Linux, or to run the CLI on an Intel Mac,
[build from source](#building-from-source).

### Installing by hand

Each release has, for each platform, an archive holding the `rack-gateway` binary, its checksum and its attestation
bundle:

```bash
VERSION=v0.1.29 # see https://github.com/DocSpring/rack-gateway/releases
PLATFORM=linux-amd64 # or linux-arm64, darwin-arm64
BASE=https://github.com/DocSpring/rack-gateway/releases/download/$VERSION
curl -fsSLO $BASE/rack-gateway-$PLATFORM.tar.gz
curl -fsSLO $BASE/rack-gateway-$PLATFORM.tar.gz.sha256
shasum -a 256 --check rack-gateway-$PLATFORM.tar.gz.sha256
gh attestation verify rack-gateway-$PLATFORM.tar.gz --repo DocSpring/rack-gateway
tar xzf rack-gateway-$PLATFORM.tar.gz
sudo install -m 0755 rack-gateway /usr/local/bin/rack-gateway
```

## Verify Installation

Expand All @@ -33,7 +55,7 @@ rack-gateway version
You should see output like:

```
client: v0.1.10
client: 0.1.29
gateway: (not logged in)
server: (not logged in)
```
Expand Down Expand Up @@ -121,7 +143,7 @@ Security keys (WebAuthn/FIDO2) in the CLI need a build with libfido2. Install it
</Tabs>

<Aside type="note" title="WebAuthn is Optional">
WebAuthn support is optional. Without it (the release build, or `task go:build:cli:nofido`), the CLI offers your
WebAuthn support is optional. Without it (the Linux releases, or `task go:build:cli:nofido`), the CLI offers your
other MFA methods: an authenticator app (TOTP), a YubiKey OTP or a backup code.
</Aside>

Expand Down Expand Up @@ -168,7 +190,24 @@ task go:build:cli:nofido

## Updating

To update, download the new release (Linux x86_64), or rebuild from source:
```bash
rack-gateway update
```

`rack-gateway update` downloads the latest release for your platform, checks its checksum and its build provenance
attestation (signed by the Release workflow for that release's tag), runs the new binary to check it works, and then
replaces the installed one. `rack-gateway update --check` only reports whether there's a newer release, and
`rack-gateway update --version v0.1.29` installs that release, even an older one.

If you can't write to the directory the CLI is installed in, run `sudo rack-gateway update`. A Linux build from
source with security key support isn't replaced by a release, which has none: rebuild it from source instead.

Once a day, the CLI checks GitHub for a newer release in the background, and after a command, prints a line
suggesting `rack-gateway update` when there is one. Only release builds check, and only when stderr is a terminal:
never when `CI` is set, or for `version`, `update` and `completion`. Set `RACK_GATEWAY_NO_UPDATE_CHECK=1` to turn it
off. The last result is kept in `update-check.json` in the CLI's config directory.

If you built the CLI from source, rebuild it:

```bash
cd rack-gateway
Expand Down Expand Up @@ -204,7 +243,8 @@ chmod +x /usr/local/bin/rack-gateway

### macOS security warning

On macOS, you may see a security warning the first time you run the CLI. Either:
The install script and `rack-gateway update` download with `curl` and Go, which don't mark the binary as downloaded
from the internet. If you downloaded the archive with a browser, macOS may refuse to run it the first time. Either:

1. Right-click the binary in Finder and select "Open", then click "Open" in the dialog
2. Or run: `xattr -d com.apple.quarantine /usr/local/bin/rack-gateway`
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/user-guide/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ If you're new to Rack Gateway, start with these guides:
The `rack-gateway` CLI is your primary interface for running Convox commands through the gateway.

- [Overview](/user-guide/cli/) - Introduction to the CLI
- [Installation](/user-guide/cli/installation/) - The Linux release, or build from source
- [Installation](/user-guide/cli/installation/) - Install and update the CLI, or build it from source
- [Authentication](/user-guide/cli/authentication/) - Login and session management
- [Multi-Rack](/user-guide/cli/multi-rack/) - Work with multiple environments
- [Commands](/user-guide/cli/commands/) - Complete command reference
Expand Down
Loading