name: Release
on:
push:
tags:
- '*' workflow_dispatch:
permissions:
contents: write
jobs:
release-setup:
runs-on: ubuntu-latest
outputs:
package_name: ${{ steps.get_version.outputs.package_name }}
package_version: ${{ steps.get_version.outputs.package_version }}
should_release: ${{ steps.verify_tag.outputs.should_release }}
major_minor: ${{ steps.release_tags.outputs.major_minor }}
short_sha: ${{ steps.release_tags.outputs.short_sha }}
is_stable: ${{ steps.release_tags.outputs.is_stable }}
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Extract package version and name
id: get_version
run: |
# Extract the package name from Cargo.toml (first occurrence)
PACKAGE_NAME=$(grep '^name\s*=' Cargo.toml | head -n 1 | cut -d'"' -f2)
# Extract the package version from Cargo.toml (first occurrence)
PACKAGE_VERSION=$(grep '^version\s*=' Cargo.toml | head -n 1 | cut -d'"' -f2)
# Set the outputs for later steps
echo "package_name=${PACKAGE_NAME}" >> $GITHUB_OUTPUT
echo "package_version=${PACKAGE_VERSION}" >> $GITHUB_OUTPUT
# Set CARGO_VERSION for the Skaffold build
echo "CARGO_VERSION=${PACKAGE_VERSION}" >> $GITHUB_ENV
echo "Extracted package: ${PACKAGE_NAME} version -> ${PACKAGE_VERSION}"
- name: Verify Tag Equals Cargo Version
id: verify_tag
run: |
# Accept both `0.4.1` and `v0.4.1` as the pushed tag; strip a
# leading `v` before comparing to the Cargo.toml version.
PUSHED="${{ github.ref_name }}"
NORMALIZED="${PUSHED#v}"
CARGO_VERSION="${{ steps.get_version.outputs.package_version }}"
echo "Pushed Tag: ${PUSHED}"
echo "Normalized Tag: ${NORMALIZED}"
echo "Cargo Version: ${CARGO_VERSION}"
if [ "${NORMALIZED}" = "${CARGO_VERSION}" ]; then
echo "should_release=true" >> $GITHUB_OUTPUT
else
echo "should_release=false" >> $GITHUB_OUTPUT
echo "Tag does not match Cargo version. Skipping release steps."
fi
- name: Compute additional release tags
id: release_tags
if: steps.verify_tag.outputs.should_release == 'true'
run: |
# Derive the major.minor rolling tag (`0.4` for `0.4.1`) so
# consumers can pin to a minor line that picks up patch
# releases automatically. Pre-release versions (`-alpha`,
# `-rc`, etc.) are excluded from `latest` and from the
# rolling tag to avoid surprising stable consumers.
VERSION="${{ steps.get_version.outputs.package_version }}"
MAJOR_MINOR="$(echo "$VERSION" | cut -d. -f1,2)"
SHORT_SHA="$(echo '${{ github.sha }}' | cut -c1-7)"
if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
IS_STABLE="true"
else
IS_STABLE="false"
fi
echo "major_minor=${MAJOR_MINOR}" >> $GITHUB_OUTPUT
echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT
echo "is_stable=${IS_STABLE}" >> $GITHUB_OUTPUT
echo "Resolved tags: version=${VERSION} major_minor=${MAJOR_MINOR} short_sha=${SHORT_SHA} is_stable=${IS_STABLE}"
build-multiarch:
needs: release-setup
if: needs.release-setup.outputs.should_release == 'true'
strategy:
matrix:
arch: [amd64, arm64] runs-on: ${{ matrix.arch == 'amd64' && 'platform-builder-Ubuntu-22.04' || 'platform-builder-MacOSX-13.4.1-arm64' }}
env:
SKAFFOLD_DEFAULT_REPO: eccr.ecmwf.int/aviso
CARGO_VERSION: ${{ needs.release-setup.outputs.package_version }}
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Docker login
run: |
# Log in to the Docker registry using stored secrets.
echo "${{ secrets.ECMWF_DOCKER_REGISTRY_ACCESS_TOKEN }}" | \
docker login eccr.ecmwf.int --username '${{ secrets.ECMWF_DOCKER_REGISTRY_USERNAME }}' --password-stdin
- name: Install Skaffold
run: |
if [[ "$(uname)" == "Linux" && "${{ matrix.arch }}" == "amd64" ]]; then
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64
elif [[ "$(uname)" == "Linux" && "${{ matrix.arch }}" == "arm64" ]]; then
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-arm64
elif [[ "$(uname)" == "Darwin" && "${{ matrix.arch }}" == "amd64" ]]; then
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-darwin-amd64
elif [[ "$(uname)" == "Darwin" && "${{ matrix.arch }}" == "arm64" ]]; then
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-darwin-arm64
else
echo "Unknown OS or architecture: $(uname) / ${{ matrix.arch }}"
exit 1
fi
chmod +x skaffold
mkdir -p $HOME/bin
mv skaffold $HOME/bin/
echo "$HOME/bin" >> $GITHUB_PATH
- name: Build and push Docker image for ${{ matrix.arch }}
env:
TARGETARCH: ${{ matrix.arch }}
run: |
# Run the Skaffold build, which uses TARGETARCH to generate an architecture-specific tag.
skaffold build
create-manifest-and-release:
needs: [release-setup, build-multiarch]
if: needs.release-setup.outputs.should_release == 'true'
runs-on: ubuntu-latest
strategy:
matrix:
image: [aviso_server, aviso_server-debug]
steps:
- name: Docker login
run: |
echo "${{ secrets.ECMWF_DOCKER_REGISTRY_ACCESS_TOKEN }}" | \
docker login eccr.ecmwf.int --username '${{ secrets.ECMWF_DOCKER_REGISTRY_USERNAME }}' --password-stdin
- name: Create and push multiarch manifest for ${{ matrix.image }}
env:
PACKAGE_VERSION: ${{ needs.release-setup.outputs.package_version }}
MAJOR_MINOR: ${{ needs.release-setup.outputs.major_minor }}
SHORT_SHA: ${{ needs.release-setup.outputs.short_sha }}
IS_STABLE: ${{ needs.release-setup.outputs.is_stable }}
REPO: eccr.ecmwf.int/aviso/${{ matrix.image }}
run: |
# Each per-arch build pushed a single-arch image manifest
# (<version>-amd64 from the Ubuntu runner, <version>-arm64
# from the macOS-arm64 runner). `imagetools create` combines
# those source manifests into a multi-arch manifest list and
# attaches every human-friendly tag below to the same digest,
# so consumers can pick the pinning level that matches their
# risk tolerance:
#
# :<version> always (e.g. 0.4.1) — exact pin
# :<version>-<sha> always — exact-build pin for incident response
# :<major>.<minor> stable releases — picks up patch releases
# :latest stable releases — newest stable
#
# Pre-release versions (-alpha, -rc, etc.) are deliberately
# excluded from the rolling tags so consumers tracking
# :latest or :0.4 are not surprised by a beta build.
TAGS=(
"--tag $REPO:$PACKAGE_VERSION"
"--tag $REPO:$PACKAGE_VERSION-$SHORT_SHA"
)
if [ "$IS_STABLE" = "true" ]; then
TAGS+=(
"--tag $REPO:$MAJOR_MINOR"
"--tag $REPO:latest"
)
fi
docker buildx imagetools create \
${TAGS[@]} \
$REPO:$PACKAGE_VERSION-amd64 \
$REPO:$PACKAGE_VERSION-arm64
github-release:
needs: [release-setup, create-manifest-and-release]
if: needs.release-setup.outputs.should_release == 'true'
runs-on: ubuntu-latest
steps:
- name: Create GitHub Release
id: create_release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: "${{ needs.release-setup.outputs.package_name }} v${{ needs.release-setup.outputs.package_version }}"
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}