Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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
96 changes: 86 additions & 10 deletions .github/workflows/docker-build-startOs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,23 @@ on:
workflow_dispatch:
inputs:
image_tag:
description: 'Image tag (used when run manually; default: latest)'
description: 'Image tag for manual runs (cannot be "latest" or a vX.Y.Z release tag; default: dev-<short-sha>)'
required: false
default: 'latest'
default: ''

# The images are pushed with the Docker Hub credentials and the layer cache
# uses the runner's own token, so nothing here needs GITHUB_TOKEN beyond the
# checkout. Narrow it rather than inheriting the repository default.
permissions:
contents: read

# Two runs of the same ref must never push to the registry at once: they would
# race on the tags they share. The group is per-ref because GitHub keeps only one
# pending run per group, so a single global group would drop an intermediate
# version tag when several tag pushes arrive together.
concurrency:
group: docker-images-${{ github.ref }}
cancel-in-progress: false
Comment thread
AndreaDiazCorreia marked this conversation as resolved.

jobs:
build-and-push-plain:
Expand All @@ -20,17 +34,48 @@ jobs:

- name: Set image tag for metadata
Comment thread
AndreaDiazCorreia marked this conversation as resolved.
id: set_tag
env:
INPUT_TAG: ${{ github.event.inputs.image_tag }}
run: |
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
set -euo pipefail
if [[ "$GITHUB_EVENT_NAME" == "push" && "$GITHUB_REF" == refs/tags/* ]]; then
# The v*.*.* trigger is a glob and also admits refs like v1.2.3foo or
# v1.2.3-rc.1. Only an exact vX.Y.Z is published.
release_re='^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$'
if [[ ! "$GITHUB_REF" =~ $release_re ]]; then
echo "::error::not a release tag, nothing is published: $GITHUB_REF"
exit 1
fi
tag="${GITHUB_REF#refs/tags/}"
else
echo "tag=${{ github.event.inputs.image_tag || 'latest' }}" >> "$GITHUB_OUTPUT"
# Anything that is not a tag push takes the guarded path, including a
# dispatch aimed at a tag: latest and vX.Y.Z name published images,
# so only a tag push is allowed to write them.
tag="${INPUT_TAG:-dev-${GITHUB_SHA::7}}"
release_re='^v[0-9]+\.[0-9]+\.[0-9]+'
if [[ "$tag" == "latest" || "$tag" =~ $release_re ]]; then
echo "::error::manual runs must not publish release tags (latest or vX.Y.Z); got '$tag'"
exit 1
fi
fi
# Anything outside the OCI tag charset is rejected before the write:
# a newline would append a second tag= line and silently override the
# value checked above.
oci_re='^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$'
if [[ ! "$tag" =~ $oci_re ]]; then
echo "::error::not a valid OCI image tag: '$tag'"
exit 1
fi
echo "tag=$tag" >> "$GITHUB_OUTPUT"

- name: Check if stable release
id: check_stable
run: |
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/v && "${{ github.ref }}" != *"-"* ]]; then
set -euo pipefail
# Anchored: the v*.*.* trigger is a glob, so it also admits refs like
# v1.2.3foo. Only an exact vX.Y.Z names a stable release.
release_re='^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$'
if [[ "$GITHUB_EVENT_NAME" == "push" && "$GITHUB_REF" =~ $release_re ]]; then
echo "is_stable=true" >> "$GITHUB_OUTPUT"
else
echo "is_stable=false" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -77,17 +122,48 @@ jobs:

- name: Set image tag for metadata
id: set_tag
env:
INPUT_TAG: ${{ github.event.inputs.image_tag }}
run: |
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
set -euo pipefail
if [[ "$GITHUB_EVENT_NAME" == "push" && "$GITHUB_REF" == refs/tags/* ]]; then
# The v*.*.* trigger is a glob and also admits refs like v1.2.3foo or
# v1.2.3-rc.1. Only an exact vX.Y.Z is published.
release_re='^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$'
if [[ ! "$GITHUB_REF" =~ $release_re ]]; then
echo "::error::not a release tag, nothing is published: $GITHUB_REF"
exit 1
fi
tag="${GITHUB_REF#refs/tags/}"
else
echo "tag=${{ github.event.inputs.image_tag || 'latest' }}" >> "$GITHUB_OUTPUT"
# Anything that is not a tag push takes the guarded path, including a
# dispatch aimed at a tag: latest and vX.Y.Z name published images,
# so only a tag push is allowed to write them.
tag="${INPUT_TAG:-dev-${GITHUB_SHA::7}}"
release_re='^v[0-9]+\.[0-9]+\.[0-9]+'
if [[ "$tag" == "latest" || "$tag" =~ $release_re ]]; then
echo "::error::manual runs must not publish release tags (latest or vX.Y.Z); got '$tag'"
exit 1
fi
fi
# Anything outside the OCI tag charset is rejected before the write:
# a newline would append a second tag= line and silently override the
# value checked above.
oci_re='^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$'
if [[ ! "$tag" =~ $oci_re ]]; then
echo "::error::not a valid OCI image tag: '$tag'"
exit 1
fi
echo "tag=$tag" >> "$GITHUB_OUTPUT"

- name: Check if stable release
id: check_stable
run: |
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" =~ ^refs/tags/v && "${{ github.ref }}" != *"-"* ]]; then
set -euo pipefail
# Anchored: the v*.*.* trigger is a glob, so it also admits refs like
# v1.2.3foo. Only an exact vX.Y.Z names a stable release.
release_re='^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$'
if [[ "$GITHUB_EVENT_NAME" == "push" && "$GITHUB_REF" =~ $release_re ]]; then
echo "is_stable=true" >> "$GITHUB_OUTPUT"
else
echo "is_stable=false" >> "$GITHUB_OUTPUT"
Expand Down
29 changes: 25 additions & 4 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,32 @@ jobs:
name: artifact-${{ matrix.target }}
path: artifacts/*

# Decides whether this run is a release. Actions expressions have no regex, so
# the exact tag check lives in a job the release jobs gate on. A
# workflow_dispatch can target a tag as well as a branch, hence the event name.
version-tag:
runs-on: ubuntu-latest
outputs:
is_release: ${{ steps.check.outputs.is_release }}
steps:
- id: check
run: |
set -euo pipefail
release_re='^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$'
if [[ "$GITHUB_EVENT_NAME" == "push" && "$GITHUB_REF" =~ $release_re ]]; then
echo "is_release=true" >> "$GITHUB_OUTPUT"
else
echo "is_release=false" >> "$GITHUB_OUTPUT"
fi

# Publish to crates.io (only if all builds succeed)
# This job will only run if both test and build jobs succeed.
# With fail-fast: false, the build job fails if ANY matrix build fails,
# ensuring all artifacts are built before publishing.
publish:
runs-on: ubuntu-latest
needs: [test, build]
if: success()
needs: [test, build, version-tag]
if: success() && needs.version-tag.outputs.is_release == 'true'
permissions:
contents: read
steps:
Expand All @@ -165,10 +183,13 @@ jobs:
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}

# Create release with all artifacts
# Create release with all artifacts for the pushed version tag (same guard as
# publish). Without it a workflow_dispatch would tag github.ref_name, which on
# a branch run means a release named after the branch.
release:
runs-on: ubuntu-latest
needs: [changelog, build]
needs: [changelog, build, version-tag]
if: needs.version-tag.outputs.is_release == 'true'
permissions:
contents: write
steps:
Expand Down