diff --git a/.github/workflows/release-tests.yaml b/.github/workflows/release-tests.yaml index f4244a3b..5e8378fd 100644 --- a/.github/workflows/release-tests.yaml +++ b/.github/workflows/release-tests.yaml @@ -4,11 +4,7 @@ on: pull_request: branches: - master - - releases/* - push: - branches: - - master - - releases/* + workflow_call: jobs: build: @@ -23,6 +19,10 @@ jobs: uses: actions/checkout@v1 with: path: ./src/github.com/${{ github.repository }} + - name: Set up QEMU + uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 + with: + platforms: arm64 - name: GoReleaser Action uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7.2.3 with: @@ -30,6 +30,17 @@ jobs: args: release --snapshot --skip=publish,sign env: GOPATH: ${{ runner.workspace }} + - name: Smoke test snapshot images + run: | + set -euo pipefail; + version="$(jq -r .version dist/metadata.json)"; + for entry in "linux/amd64 amd64" "linux/arm64 arm64"; do + set -- $entry; + # snapshot builds are --load'ed with a platform-suffixed tag + image="dopplerhq/cli:${version}-$2"; + docker image inspect "$image" --format '{{.Architecture}}'; + docker run --rm --platform "$1" -e DOPPLER_ENABLE_VERSION_CHECK=false "$image" --version; + done - uses: actions/upload-artifact@master with: name: macOS build (amd64) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f72df36e..1653e6f8 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -13,8 +13,11 @@ on: - "major" jobs: + release-tests: + uses: ./.github/workflows/release-tests.yaml publish: name: publish - ${{ inputs.type }} + needs: release-tests runs-on: ubuntu-latest-16-core environment: "production" permissions: @@ -38,12 +41,16 @@ jobs: echo "DOCKER_CONFIG=$(cd "$DOCKER_CONFIG" && pwd)" >> "$GITHUB_ENV"; env: DOCKER_CONFIG: ${{ secrets.DOCKER_CONFIG }} + - name: Set up QEMU + # registers binfmt handlers so buildx can run the target-arch `apk` steps + # in docker/alpine for non-amd64 platforms. Must precede setup-buildx + uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 + with: + platforms: arm64 - name: Set up Docker Buildx # dockers_v2 builds via `docker buildx build`; goreleaser expects the # docker-container driver, which this action configures by default uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 - - name: Update alpine image - run: docker pull alpine - name: Install Doppler CLI uses: dopplerhq/cli-action@v2 - name: Install Cloudsmith CLI diff --git a/.goreleaser.yml b/.goreleaser.yml index 02cec0e8..c66bac19 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -111,6 +111,7 @@ dockers_v2: - doppler platforms: - linux/amd64 + - linux/arm64 images: - dopplerhq/cli - gcr.io/dopplerhq/cli @@ -126,6 +127,11 @@ dockers_v2: sbom: false flags: - "--provenance=false" + hooks: + # runs after the images are pushed but before the GitHub release is cut. Keep the platform list in sync with `platforms` above + post: + - cmd: ./scripts/release/verify-images.sh {{ .IsSnapshot }} linux/amd64,linux/arm64 {{ range .Images }}{{ . }} {{ end }} + output: true homebrew_casks: - name: doppler diff --git a/BUILD.md b/BUILD.md index 3cfef159..18bcb62e 100644 --- a/BUILD.md +++ b/BUILD.md @@ -28,6 +28,12 @@ Note that this will require approval from a CLI admin before the action will be - **Fix**: `export GPG_TTY=$(tty)` +**Issue**: `exec format error`, or `apk` failing, during `make test-release` + +- **Fix**: register binfmt handlers: `docker run --privileged --rm tonistiigi/binfmt --install arm64` + +- **Why**: `docker/alpine` runs `apk` inside the target architecture's image, which requires emulation when building for a non-native platform. + **Issue**: After releasing, your personal account is logged out of the docker daemon - **Fix**: Log in again with this registry manually specified: `docker login https://docker.io` diff --git a/INSTALL.md b/INSTALL.md index 0ae4bf3d..8d988f38 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -148,6 +148,8 @@ You can find the source `install.sh` file in this repo's `scripts` directory. We currently publish a `dopplerhq/cli` Docker image based on `alpine`. For more info, check out our [Docker guide](https://docs.doppler.com/docs/docker-base-image-guide). +The image is published as a multi-platform manifest for `linux/amd64` and `linux/arm64`, so `docker pull dopplerhq/cli` selects the right architecture automatically. To pull a specific one, use `docker pull --platform=linux/arm64 dopplerhq/cli`. + You can find all source Dockerfiles in this repo's `/docker` [folder](https://github.com/DopplerHQ/cli/tree/master/docker). ## GitHub Action diff --git a/scripts/release/verify-images.sh b/scripts/release/verify-images.sh new file mode 100755 index 00000000..49b0344e --- /dev/null +++ b/scripts/release/verify-images.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +set -eu -o pipefail -o functrace + +# Verifies that each published image is a manifest list containing exactly the expected platforms. + +if [ $# -lt 3 ]; then + echo "Usage: $0 IS_SNAPSHOT PLATFORMS IMAGE [IMAGE...]" + exit 1 +fi + +IS_SNAPSHOT="$1" +EXPECTED="$(echo "$2" | tr ',' '\n' | sort)" +shift 2 + +# snapshot images are built one platform at a time and never pushed, so there's no manifest list to inspect +if [ "$IS_SNAPSHOT" == "true" ]; then + echo "Snapshot build, skipping image verification" + exit 0 +fi + +# variants (ex. arm64's "v8") are intentionally omitted; the registry may or may not record them +FORMAT='{{ range .Manifest.Manifests }}{{ .Platform.OS }}/{{ .Platform.Architecture }}{{ "\n" }}{{ end }}' + +for image in "$@"; do + if ! actual="$(docker buildx imagetools inspect "$image" --format "$FORMAT" 2>&1)"; then + echo "Unable to read the platforms of $image, it may not be a manifest list:" + echo "$actual" + exit 1 + fi + + # drop the blank lines the template emits between entries + actual="$(echo "$actual" | grep . | sort)" + + if [ "$actual" != "$EXPECTED" ]; then + echo "$image was not published for the expected platforms" + echo "Expected: $(echo "$EXPECTED" | tr '\n' ' ')" + echo "Actual: $(echo "$actual" | tr '\n' ' ')" + exit 1 + fi + + echo "$image: $(echo "$actual" | tr '\n' ' ')" +done