From fc4e07d64307452cd647e609c0b1b7ba1dd54898 Mon Sep 17 00:00:00 2001 From: Ben Lovell Date: Fri, 17 Apr 2026 21:26:30 +0200 Subject: [PATCH] feat: publish multi-arch Docker image on release --- .github/workflows/publish-docker.yml | 44 ++++++++++++++++++++++++++++ .github/workflows/release.yml | 23 ++++++--------- Dockerfile | 18 ++++++++++++ dist-workspace.toml | 4 +-- 4 files changed, 73 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/publish-docker.yml create mode 100644 Dockerfile diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml new file mode 100644 index 00000000..e47b062e --- /dev/null +++ b/.github/workflows/publish-docker.yml @@ -0,0 +1,44 @@ +# Publish a release Docker image to GHCR. +# +# Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a publish job +# within `cargo-dist`. Runs after `host`, so the GitHub Release and its binary artifacts exist; +# the Dockerfile fetches the prebuilt musl binary from the release at image-build time. + +name: "[tower] Publish Docker image" + +on: + workflow_call: + inputs: + plan: + required: true + type: string + +jobs: + docker-publish: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v6 + - uses: docker/setup-buildx-action@v3 + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push + env: + TAG: ${{ fromJson(inputs.plan).announcement_tag }} + PRERELEASE: ${{ fromJson(inputs.plan).announcement_is_prerelease }} + run: | + VERSION="${TAG#v}" + TAGS=(--tag "ghcr.io/tower/tower-cli:$VERSION") + if [ "$PRERELEASE" != "true" ]; then + TAGS+=(--tag "ghcr.io/tower/tower-cli:latest") + fi + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --build-arg VERSION="$VERSION" \ + "${TAGS[@]}" \ + --push . diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 15429ade..d4cecc44 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -312,20 +312,15 @@ jobs: plan: ${{ needs.plan.outputs.val }} secrets: inherit - announce: + custom-publish-docker: needs: - plan - host - - custom-publish-pypi - # use "always() && ..." to allow us to wait for all publish jobs while - # still allowing individual publish jobs to skip themselves (for prereleases). - # "host" however must run to completion, no skipping allowed! - if: ${{ always() && needs.host.result == 'success' && (needs.custom-publish-pypi.result == 'skipped' || needs.custom-publish-pypi.result == 'success') }} - runs-on: "ubuntu-22.04" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v6 - with: - persist-credentials: false - submodules: recursive + if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }} + uses: ./.github/workflows/publish-docker.yml + with: + plan: ${{ needs.plan.outputs.val }} + secrets: inherit + permissions: + "contents": "read" + "packages": "write" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..46368135 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM --platform=$BUILDPLATFORM alpine:3 AS fetch +ARG TARGETARCH +ARG VERSION +RUN apk add --no-cache curl tar xz \ + && case "$TARGETARCH" in \ + amd64) ARCH=x86_64 ;; \ + arm64) ARCH=aarch64 ;; \ + *) echo "unsupported TARGETARCH: $TARGETARCH" >&2; exit 1 ;; \ + esac \ + && curl -fsSL -o /tmp/tower.tar.xz \ + "https://github.com/tower/tower-cli/releases/download/v${VERSION}/tower-${ARCH}-unknown-linux-musl.tar.xz" \ + && mkdir -p /out \ + && tar -xJf /tmp/tower.tar.xz -C /out --strip-components=1 \ + && chmod +x /out/tower + +FROM gcr.io/distroless/static-debian12 +COPY --from=fetch /out/tower /usr/local/bin/tower +ENTRYPOINT ["/usr/local/bin/tower"] diff --git a/dist-workspace.toml b/dist-workspace.toml index 646bc15f..f3e372dd 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -12,11 +12,11 @@ installers = ["shell", "homebrew", "msi"] # A GitHub repo to push Homebrew formulas to tap = "tower/tower-cli" # Target platforms to build apps for (Rust target-triple syntax) -targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl"] +targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "aarch64-unknown-linux-musl", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl"] # Path that installers should place binaries in install-path = "CARGO_HOME" # Publish jobs to run in CI -publish-jobs = ["./publish-pypi"] +publish-jobs = ["./publish-pypi", "./publish-docker"] # Whether to install an updater program install-updater = false # Whether dist should create a Github Release or use an existing draft