release #13
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: release | |
| # Everything a release publishes, driven by the tag release-please pushes. | |
| # Nothing here decides a version — the tag already did. | |
| # | |
| # Publishes: | |
| # * CLI binaries attached to the GitHub release: linux and macOS for | |
| # amd64 and arm64, Windows for amd64 only (there is no meaningful | |
| # arm64 Windows demand for this CLI) | |
| # * a multi-arch container image, to Docker Hub and GHCR | |
| # * the Helm chart, to GHCR as an OCI artifact | |
| # * the protobuf schemas, to the Buf Schema Registry | |
| # | |
| # Every job re-runs the end-to-end suite's ingredients implicitly by using | |
| # the same Dockerfile CI already exercised; nothing is built here that CI | |
| # has not already built on main. | |
| on: | |
| push: | |
| tags: ["v*.*.*"] | |
| # For rebuilding a release whose publish step failed. The tag has | |
| # already been cut, so this is safe to re-run. | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Existing tag to build (e.g. v0.3.0)" | |
| required: true | |
| permissions: | |
| contents: write | |
| packages: write | |
| env: | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| binaries: | |
| name: cli (${{ matrix.target }}) | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # gnu rather than musl for the x86_64 Linux build would tie the | |
| # binary to the glibc of whatever built it. A CLI that people | |
| # download and run on an unknown distro should not care. | |
| - target: x86_64-unknown-linux-musl | |
| runner: ubuntu-latest | |
| - target: aarch64-unknown-linux-musl | |
| runner: ubuntu-24.04-arm | |
| - target: x86_64-apple-darwin | |
| runner: macos-latest | |
| - target: aarch64-apple-darwin | |
| runner: macos-latest | |
| - target: x86_64-pc-windows-msvc | |
| runner: windows-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ inputs.tag || github.ref }} | |
| # build.rs stamps the commit into the binary, so the checkout | |
| # has to be a real git repository. | |
| fetch-depth: 0 | |
| - name: Install protoc | |
| shell: bash | |
| run: | | |
| if [ "$RUNNER_OS" = "macOS" ]; then | |
| brew install protobuf | |
| elif [ "$RUNNER_OS" = "Windows" ]; then | |
| choco install protoc -y | |
| else | |
| sudo apt-get update && sudo apt-get install -y protobuf-compiler musl-tools | |
| fi | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| key: ${{ matrix.target }} | |
| - name: build | |
| shell: bash | |
| run: | | |
| # A reproducible build stamp: the same tag built twice produces | |
| # the same binary rather than differing by a timestamp. Derived | |
| # from the built commit itself, as a plain Unix epoch integer — | |
| # `github.event.repository.pushed_at` is an ISO 8601 string in | |
| # this event context (and workflow_dispatch reruns have no push | |
| # event at all), and cc/ring reject anything but a decimal int. | |
| SOURCE_DATE_EPOCH=$(git log -1 --format=%ct) | |
| export SOURCE_DATE_EPOCH | |
| cargo build --release --locked --target ${{ matrix.target }} -p silo-cli | |
| - name: package | |
| shell: bash | |
| run: | | |
| VERSION="${{ inputs.tag || github.ref_name }}" | |
| NAME="silo-${VERSION}-${{ matrix.target }}" | |
| mkdir -p "dist/$NAME" | |
| BIN="target/${{ matrix.target }}/release/silo" | |
| [ "${{ matrix.target }}" = "x86_64-pc-windows-msvc" ] && BIN="$BIN.exe" | |
| cp "$BIN" "dist/$NAME/" | |
| cp README.md LICENSE "dist/$NAME/" | |
| # tar rather than zip even for the Windows build: bsdtar ships in | |
| # every image this matrix uses (Windows 10 1803+ included), so | |
| # one archive format covers all three OSes with no extra tool. | |
| tar -C dist -czf "dist/$NAME.tar.gz" "$NAME" | |
| # A checksum per artifact rather than one combined file: each is | |
| # verifiable on its own, without downloading the others. shasum | |
| # is Perl and macOS-only; sha256sum (coreutils) is what Linux and | |
| # Git Bash on Windows both ship instead. | |
| if [ "$RUNNER_OS" = "macOS" ]; then | |
| (cd dist && shasum -a 256 "$NAME.tar.gz" > "$NAME.tar.gz.sha256") | |
| else | |
| (cd dist && sha256sum "$NAME.tar.gz" > "$NAME.tar.gz.sha256") | |
| fi | |
| - name: the binary reports the version it was tagged with | |
| shell: bash | |
| run: | | |
| VERSION="${{ inputs.tag || github.ref_name }}" | |
| # Only the native-arch Linux and macOS builds can run here; the | |
| # cross-built one is checked by the job that matches its arch. | |
| if [ "${{ matrix.target }}" = "x86_64-apple-darwin" ] && [ "$(uname -m)" = "arm64" ]; then | |
| echo "cross-built for x86_64 on arm64; skipping the run check" | |
| exit 0 | |
| fi | |
| BIN="target/${{ matrix.target }}/release/silo" | |
| [ "${{ matrix.target }}" = "x86_64-pc-windows-msvc" ] && BIN="$BIN.exe" | |
| reported=$("$BIN" --version | awk '{print $2}') | |
| if [ "v$reported" != "$VERSION" ]; then | |
| echo "::error::binary reports $reported but the tag is $VERSION" | |
| exit 1 | |
| fi | |
| echo "silo $reported" | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: silo-${{ matrix.target }} | |
| path: dist/*.tar.gz* | |
| if-no-files-found: error | |
| attach: | |
| needs: binaries | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| pattern: silo-* | |
| path: dist | |
| merge-multiple: true | |
| - name: attach binaries to the release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh release upload "${{ inputs.tag || github.ref_name }}" dist/* \ | |
| --repo "${{ github.repository }}" --clobber | |
| image-binaries: | |
| name: image binaries (${{ matrix.arch }}) | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| matrix: | |
| include: | |
| - arch: amd64 | |
| target: x86_64-unknown-linux-musl | |
| runner: ubuntu-latest | |
| - arch: arm64 | |
| target: aarch64-unknown-linux-musl | |
| runner: ubuntu-24.04-arm | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ inputs.tag || github.ref }} | |
| fetch-depth: 0 | |
| - name: Install protoc | |
| run: sudo apt-get update && sudo apt-get install -y protobuf-compiler musl-tools | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| key: image-${{ matrix.target }} | |
| - name: build | |
| run: | | |
| SOURCE_DATE_EPOCH=$(git log -1 --format=%ct) | |
| export SOURCE_DATE_EPOCH | |
| cargo build --release --locked --target ${{ matrix.target }} -p silo-server -p silo-cli | |
| # Staged under linux/<arch> so it matches buildx's TARGETPLATFORM | |
| # exactly, and Dockerfile.release can COPY it with no arch mapping. | |
| - name: stage for the image build | |
| run: | | |
| mkdir -p "image-bin/linux/${{ matrix.arch }}" | |
| cp target/${{ matrix.target }}/release/silo-server \ | |
| target/${{ matrix.target }}/release/silo \ | |
| "image-bin/linux/${{ matrix.arch }}/" | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: image-bin-${{ matrix.arch }} | |
| path: image-bin | |
| image: | |
| needs: image-binaries | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ inputs.tag || github.ref }} | |
| fetch-depth: 0 | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| pattern: image-bin-* | |
| path: image-ctx | |
| merge-multiple: true | |
| - uses: docker/setup-qemu-action@v4 | |
| - uses: docker/setup-buildx-action@v4 | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: version | |
| id: version | |
| shell: bash | |
| run: | | |
| # docker/metadata-action's type=semver tags read the git ref, | |
| # not the commit — they need GITHUB_REF to be refs/tags/vX.Y.Z. | |
| # This job only ever runs via workflow_dispatch (release-please | |
| # dispatches it because a GITHUB_TOKEN-pushed tag cannot trigger | |
| # a workflow itself), where GITHUB_REF is refs/heads/main. So | |
| # type=semver silently matched nothing and every push only ever | |
| # got tagged `latest`. Computing the version ourselves and using | |
| # type=raw sidesteps the ref-detection entirely. | |
| VERSION="${{ inputs.tag || github.ref_name }}" | |
| VERSION="${VERSION#v}" | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "major_minor=${VERSION%.*}" >> "$GITHUB_OUTPUT" | |
| - name: Docker metadata | |
| id: meta | |
| uses: docker/metadata-action@v6 | |
| with: | |
| # Both registries, because Docker Hub's pull limits make GHCR | |
| # the better default for a Kubernetes cluster. | |
| images: | | |
| ${{ secrets.DOCKERHUB_USERNAME }}/silo | |
| ghcr.io/${{ github.repository }} | |
| tags: | | |
| type=raw,value=${{ steps.version.outputs.version }} | |
| type=raw,value=${{ steps.version.outputs.major_minor }} | |
| type=raw,value=latest | |
| - name: Build and push | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: image-ctx | |
| file: Dockerfile.release | |
| # arm64 is not optional: it is what Graviton and Apple silicon | |
| # clusters run. Both binaries are cross-compiled natively by the | |
| # image-binaries job above (real arm64/amd64 runners, no QEMU), | |
| # so this step only copies a prebuilt binary into the image | |
| # rather than compiling Rust under emulation. | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| chart: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ inputs.tag || github.ref }} | |
| - uses: azure/setup-helm@v5 | |
| - name: Log in to GHCR | |
| run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin | |
| - name: the chart version matches the tag | |
| run: | | |
| TAG="${{ inputs.tag || github.ref_name }}" | |
| CHART=$(grep '^version:' charts/silo/Chart.yaml | awk '{print $2}') | |
| if [ "v$CHART" != "$TAG" ]; then | |
| echo "::error::chart version $CHART does not match tag $TAG" | |
| exit 1 | |
| fi | |
| - name: Package and push chart | |
| run: | | |
| helm package charts/silo -d .cr-release-packages | |
| # OCI references must be lowercase; the org name is not | |
| # guaranteed to be. | |
| owner=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') | |
| helm push .cr-release-packages/silo-*.tgz "oci://ghcr.io/$owner/charts" | |
| protos: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ inputs.tag || github.ref }} | |
| - uses: bufbuild/buf-action@v1 | |
| with: | |
| token: ${{ secrets.BUF_TOKEN }} | |
| input: proto | |
| push: true | |
| # There is no pull request on a tag build, but the action would | |
| # still try; saying so keeps it from needing a permission this | |
| # job has no other use for. | |
| pr_comment: false |