🐋 Production Docker Build • v1.0.0 #2
Workflow file for this run
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
| # .github/workflows/docker-prod.yml | |
| name: 🐋 Docker Production Image | |
| run-name: 🐋 Production Docker Build • ${{ inputs.tag || github.ref_name }} | |
| # Production images are release artifacts. They are built only from existing, | |
| # non-empty annotated SemVer tags. Stable tags update the matching major, | |
| # minor, and `latest` aliases; prerelease tags publish only their exact version. | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: Existing annotated release tag, such as v1.0.0 or v1.0.0-rc.1 | |
| required: true | |
| type: string | |
| permissions: | |
| contents: read | |
| packages: write | |
| jobs: | |
| docker-prod: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: 📥 Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ inputs.tag || github.ref }} | |
| - name: 🐍 Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.14" | |
| - name: 🔖 Resolve and validate Path Header Scanner build version | |
| id: version | |
| shell: bash | |
| env: | |
| RELEASE_TAG: ${{ inputs.tag || github.ref_name }} | |
| run: | | |
| python -m pip install --disable-pip-version-check packaging | |
| TAG_REGEX='^v[0-9]+\.[0-9]+\.[0-9]+([.-](alpha|beta|rc|dev|post)\.[0-9]+)?(\+[0-9A-Za-z.]+)?$' | |
| STABLE_TAG_REGEX='^v[0-9]+\.[0-9]+\.[0-9]+$' | |
| TAG="$RELEASE_TAG" | |
| PUBLISH_ALIASES=false | |
| PUBLISH_LATEST=false | |
| if [[ ! "$TAG" =~ $TAG_REGEX ]]; then | |
| echo "Invalid Path Header Scanner release tag: $TAG" >&2 | |
| exit 1 | |
| fi | |
| if ! git show-ref --verify --quiet "refs/tags/$TAG"; then | |
| echo "Release tag does not exist in the repository: $TAG" >&2 | |
| exit 1 | |
| fi | |
| if [[ "$(git cat-file -t "refs/tags/$TAG")" != "tag" ]]; then | |
| echo "Production images require an annotated Git tag: $TAG" >&2 | |
| exit 1 | |
| fi | |
| TAG_MESSAGE="$(git tag -l --format='%(contents)' "$TAG")" | |
| if [[ -z "${TAG_MESSAGE//[[:space:]]/}" ]]; then | |
| echo "Annotated release tag has an empty message: $TAG" >&2 | |
| exit 1 | |
| fi | |
| VERSION="$(python -m app.core.build.version)" | |
| EXPECTED_VERSION="$(python -c 'from packaging.version import Version; import sys; print(Version(sys.argv[1].removeprefix("v")).public)' "$TAG")" | |
| if [[ -z "$VERSION" || "$VERSION" != "$EXPECTED_VERSION" ]]; then | |
| echo "Resolved build version '$VERSION' does not match release tag '$TAG' ('$EXPECTED_VERSION')." >&2 | |
| exit 1 | |
| fi | |
| if [[ "$TAG" =~ $STABLE_TAG_REGEX ]]; then | |
| PUBLISH_ALIASES=true | |
| PUBLISH_LATEST=true | |
| fi | |
| IMAGE_TAG="${TAG//+/-}" | |
| VERSION_CORE="${TAG#v}" | |
| VERSION_CORE="${VERSION_CORE%%+*}" | |
| IFS='.' read -r MAJOR MINOR _ <<< "$VERSION_CORE" | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| echo "image_tag=$IMAGE_TAG" >> "$GITHUB_OUTPUT" | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "major=$MAJOR" >> "$GITHUB_OUTPUT" | |
| echo "minor=$MINOR" >> "$GITHUB_OUTPUT" | |
| echo "publish_aliases=$PUBLISH_ALIASES" >> "$GITHUB_OUTPUT" | |
| echo "publish_latest=$PUBLISH_LATEST" >> "$GITHUB_OUTPUT" | |
| - name: 🐳 Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: 🔐 Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: 🔤 Normalize image name | |
| id: vars | |
| shell: bash | |
| run: | | |
| echo "image_name=${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT" | |
| - name: 🏷️ Extract Docker metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: | | |
| ghcr.io/${{ steps.vars.outputs.image_name }} | |
| tags: | | |
| type=raw,value=${{ steps.version.outputs.image_tag }} | |
| type=raw,value=v${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }},enable=${{ steps.version.outputs.publish_aliases }} | |
| type=raw,value=v${{ steps.version.outputs.major }},enable=${{ steps.version.outputs.publish_aliases }} | |
| type=raw,value=latest,enable=${{ steps.version.outputs.publish_latest }} | |
| - name: 🏗️ Build and push production image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: Dockerfile | |
| target: production | |
| build-args: | | |
| PHS_BUILD_VERSION=${{ steps.version.outputs.version }} | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max |