Skip to content

SCALRCORE-38942

SCALRCORE-38942 #44

name: Build and Release Runner Image (EU dev GAR)
# Triggered when a PR carries the `build-gar-images` label. Pushes test
# builds of all three variants to the EU dev GAR mirror only — Docker
# Hub and the US production mirror are never touched here (production
# is reserved for the release.yaml workflow).
#
# Tags are derived from the PR source branch, lower-cased and prefixed
# with `branch-` so they cannot collide with the semver release tags
# produced by release.yaml. Example: branch `0.2.0` → `branch-0.2.0`,
# not `0.2.0`. This prevents a PR from a maliciously-named branch from
# overwriting an existing release image.
on:
pull_request:
types: [labeled, synchronize, reopened]
permissions:
contents: read
# Required by google-github-actions/auth for workload identity federation.
id-token: write
jobs:
build:
name: Build and Push to GAR (EU dev)
if: contains(github.event.pull_request.labels.*.name, 'build-gar-images')
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
# Defense-in-depth: refuse to build on behalf of senders without
# write-or-better access on this repo. Covers two paths:
# - `labeled` event: the user who added the label must be trusted.
# - `synchronize` event: the user who pushed must be trusted.
# GitHub already strips secrets/vars for `pull_request` runs from
# forks, so this is layered on top of that built-in protection.
- name: Verify sender authorization
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ACTOR: ${{ github.event.sender.login }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
role=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" --jq .permission)
case "$role" in
admin|maintain|write) echo "OK: ${ACTOR} has ${role} on ${REPO}";;
*) echo "::error::Sender ${ACTOR} has '${role}' permission; needs write or higher"; exit 1;;
esac
- name: Authenticate to Google Cloud
id: gcp-auth
uses: google-github-actions/auth@v3
with:
workload_identity_provider: ${{vars.GOOGLE_WORKLOAD_IDENTITY_POOL_PROVIDER}}
service_account: ${{vars.GOOGLE_SERVICE_ACCOUNT_EMAIL}}
token_format: access_token
- name: Login to GAR (EU dev)
uses: docker/login-action@v3
with:
registry: ${{ vars.EU_DEV_MIRROR_LOCATION }}-docker.pkg.dev
username: oauth2accesstoken
password: ${{ steps.gcp-auth.outputs.access_token }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# PR source branch → Docker tag. Lower-cased (downstream agents
# lower-case image refs), slashes → dashes (illegal in tags), and
# prefixed with `branch-` so the tag can never collide with a
# semver release tag pushed by release.yaml.
- name: Resolve branch tag
id: branch
run: |
raw='${{ github.head_ref }}'
sanitized="${raw,,}"
sanitized="${sanitized//\//-}"
echo "tag=branch-${sanitized}" | tee -a $GITHUB_OUTPUT
- name: Compose GAR image path
id: gar
run: |
echo "image=${{ vars.EU_DEV_MIRROR_LOCATION }}-docker.pkg.dev/${{ vars.EU_DEV_GOOGLE_PROJECT }}/main/scalr/runner" | tee -a $GITHUB_OUTPUT
- name: Build and push images
uses: docker/bake-action@v5
env:
VERSION: ${{ steps.branch.outputs.tag }}
with:
files: |
docker-bake.hcl
versions.json
push: true
# Replace each target's tag list (`tags=` removes the Docker Hub
# default from docker-bake.hcl) so the build pushes only to the EU
# dev GAR mirror, which also holds the per-branch buildcache.
set: |
full.tags=${{ steps.gar.outputs.image }}:${{ steps.branch.outputs.tag }}
python39.tags=${{ steps.gar.outputs.image }}:${{ steps.branch.outputs.tag }}-python39
slim.tags=${{ steps.gar.outputs.image }}:${{ steps.branch.outputs.tag }}-slim
full.cache-from=type=registry,ref=${{ steps.gar.outputs.image }}:buildcache-${{ steps.branch.outputs.tag }}
python39.cache-from=type=registry,ref=${{ steps.gar.outputs.image }}:buildcache-${{ steps.branch.outputs.tag }}-python39
slim.cache-from=type=registry,ref=${{ steps.gar.outputs.image }}:buildcache-${{ steps.branch.outputs.tag }}-slim
full.cache-to=type=registry,ref=${{ steps.gar.outputs.image }}:buildcache-${{ steps.branch.outputs.tag }},mode=max
python39.cache-to=type=registry,ref=${{ steps.gar.outputs.image }}:buildcache-${{ steps.branch.outputs.tag }}-python39,mode=max
slim.cache-to=type=registry,ref=${{ steps.gar.outputs.image }}:buildcache-${{ steps.branch.outputs.tag }}-slim,mode=max
- name: Report published images
env:
BRANCH: ${{ github.head_ref }}
TAG: ${{ steps.branch.outputs.tag }}
IMG: ${{ steps.gar.outputs.image }}
run: |
echo "Published GAR (EU dev) images for branch ${BRANCH}"
echo ""
echo " ${IMG}:${TAG}"
echo " ${IMG}:${TAG}-python39"
echo " ${IMG}:${TAG}-slim"
{
echo "## Published runner images — branch \`${BRANCH}\`"
echo ""
echo "**GAR — EU dev**"
echo ""
echo "- \`${IMG}:${TAG}\`"
echo "- \`${IMG}:${TAG}-python39\`"
echo "- \`${IMG}:${TAG}-slim\`"
} >> "$GITHUB_STEP_SUMMARY"