ci: add pull_request_target image build/mirror workflow flow

This commit is contained in:
kevinveenbirkenbach committed 2026-03-17 23:43:53 +01:00
commit e2bc2ea9e2
16 files changed
+384 -60

No files matched your search

+51 -34
View File
@@ -7,13 +7,28 @@ on:
description: "Space-separated distro list"
required: true
type: string
checkout_ref:
description: "Optional git ref to checkout before build (default: event ref)"
required: false
type: string
default: ""
image_tag:
description: "Optional image tag override (default: ci-${github.sha})"
required: false
type: string
default: ""
concurrency_channel:
description: "Optional channel to separate concurrency groups between callers"
required: false
type: string
default: "default"
permissions:
contents: read
packages: write
concurrency:
group: infinito-build-${{ github.ref }}
group: infinito-build-${{ github.repository }}-${{ inputs.concurrency_channel }}-${{ inputs.checkout_ref || github.ref }}
cancel-in-progress: false
jobs:
@@ -22,7 +37,7 @@ jobs:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.out.outputs.matrix }}
image_tag: ci-${{ github.sha }}
image_tag: ${{ steps.out.outputs.image_tag }}
steps:
- name: Install jq
run: |
@@ -36,8 +51,14 @@ jobs:
set -euo pipefail
distros="${{ inputs.distros }}"
json="$(printf '%s' "$distros" | jq -Rc 'split(" ") | map(select(length>0))')"
image_tag="${{ inputs.image_tag }}"
if [ -z "${image_tag}" ]; then
image_tag="ci-${GITHUB_SHA}"
fi
echo "matrix=${json}" >> "$GITHUB_OUTPUT"
echo "image_tag=${image_tag}" >> "$GITHUB_OUTPUT"
echo "Using matrix: ${json}"
echo "Using image tag: ${image_tag}"
build:
name: 🏗️ Build image (${{ matrix.distro }})
@@ -50,8 +71,19 @@ jobs:
distro: ${{ fromJson(needs.prepare.outputs.matrix) }}
steps:
- name: Checkout
- name: Checkout trusted workflow repo (helpers)
uses: actions/checkout@v6
with:
ref: ${{ github.sha }}
persist-credentials: false
- name: Checkout source to build
uses: actions/checkout@v6
with:
repository: ${{ github.repository }}
ref: ${{ inputs.checkout_ref || github.ref }}
path: _src
persist-credentials: false
- name: Disk pressure (start)
shell: bash
@@ -96,39 +128,24 @@ jobs:
run: echo "owner=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
- name: Build & push (retry 7x, 20s)
if: ${{ !(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
shell: bash
run: |
set -euo pipefail
env:
BUILD_CONTEXT_DIR: _src
MATRIX_DISTRO: ${{ matrix.distro }}
IMAGE_TAG: ${{ needs.prepare.outputs.image_tag }}
USE_NIX_TOKEN: ${{ !(github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository) }}
NIX_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MAX_ATTEMPTS: "7"
RETRY_DELAY_SECONDS: "20"
run: ./scripts/ci/build_and_push_ci_image.sh
max_attempts=7
retry_delay_seconds=20
attempt=1
while true; do
echo "=== Build & push attempt ${attempt}/${max_attempts} ==="
if docker buildx build \
--file ./Dockerfile \
--push \
--tag "ghcr.io/${{ steps.lower_owner.outputs.owner }}/infinito-${{ matrix.distro }}:${{ needs.prepare.outputs.image_tag }}" \
--build-arg "PKGMGR_IMAGE=ghcr.io/kevinveenbirkenbach/pkgmgr-${{ matrix.distro }}:stable" \
--build-arg "NIX_CONFIG=access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" \
--cache-from "type=gha,scope=infinito-${{ matrix.distro }}" \
--cache-to "type=gha,mode=max,scope=infinito-${{ matrix.distro }}" \
.; then
echo "Build & push succeeded on attempt ${attempt}/${max_attempts}."
break
fi
if [ "${attempt}" -ge "${max_attempts}" ]; then
echo "Build & push failed after ${max_attempts} attempts."
exit 1
fi
echo "Attempt ${attempt} failed. Retrying in ${retry_delay_seconds}s..."
sleep "${retry_delay_seconds}"
attempt=$((attempt + 1))
done
- name: Wait for prebuilt image (fork PR)
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository }}
env:
MATRIX_DISTRO: ${{ matrix.distro }}
IMAGE_TAG: ${{ needs.prepare.outputs.image_tag }}
run: ./scripts/ci/wait_prebuilt_ci_image.sh
- name: Disk pressure (post-build)
if: always()
+3
View File
@@ -48,6 +48,7 @@ jobs:
uses: ./.github/workflows/build-ci-images.yml
with:
distros: "arch debian ubuntu fedora centos"
concurrency_channel: ci-orchestrator-build-consumer
# Code Testing
test-code-integration:
@@ -85,6 +86,8 @@ jobs:
# Sync Missing Images
images-mirror-missing:
uses: ./.github/workflows/images-mirror-missing.yml
with:
concurrency_channel: ci-orchestrator-mirror-consumer
secrets: inherit
# Deploy tests
@@ -0,0 +1,42 @@
name: CI (On Pull-Request Target / Build CI Images For Forks)
on:
pull_request_target:
types:
- opened
- synchronize
- reopened
- ready_for_review
permissions:
contents: read
packages: write
jobs:
prepare-fork-merge-ref:
if: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
runs-on: ubuntu-latest
outputs:
checkout_ref: ${{ steps.out.outputs.checkout_ref }}
image_tag: ${{ steps.out.outputs.image_tag }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Resolve merge ref and image tag
id: out
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha || '' }}
run: ./scripts/ci/resolve_pr_merge_ref.sh
build-ci-images-fork:
if: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
needs: prepare-fork-merge-ref
uses: ./.github/workflows/build-ci-images.yml
with:
distros: "arch debian ubuntu fedora centos"
checkout_ref: ${{ needs.prepare-fork-merge-ref.outputs.checkout_ref }}
image_tag: ${{ needs.prepare-fork-merge-ref.outputs.image_tag }}
concurrency_channel: pr-target-build-producer
secrets: inherit
@@ -0,0 +1,43 @@
name: CI (On Pull-Request Target / Mirror Fork Images)
on:
pull_request_target:
types:
- opened
- synchronize
- reopened
- ready_for_review
permissions:
contents: read
packages: write
jobs:
prepare-fork-merge-ref:
if: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
runs-on: ubuntu-latest
outputs:
checkout_ref: ${{ steps.out.outputs.checkout_ref }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Resolve merge ref
id: out
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha || '' }}
run: ./scripts/ci/resolve_pr_merge_ref.sh
images-mirror-missing-fork:
if: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
needs: prepare-fork-merge-ref
uses: ./.github/workflows/images-mirror-missing.yml
with:
repo_root: _pr_source
source_repository: ${{ github.repository }}
source_ref: ${{ needs.prepare-fork-merge-ref.outputs.checkout_ref }}
source_path: _pr_source
concurrency_channel: pr-target-mirror-producer
concurrency_key: pr-${{ github.event.pull_request.number }}
secrets: inherit
+7 -8
View File
@@ -7,7 +7,6 @@ concurrency:
on:
push:
branches:
- master
- main
- "feature/**"
- "hotfix/**"
@@ -76,7 +75,7 @@ jobs:
distros: ${{ needs.pick-distro.outputs.distros }}
secrets: inherit
# Guard job: only allow version releases if the tag commit is contained in origin/master
# Guard job: only allow version releases if the tag commit is contained in origin/main
release-guard:
name: "🛡️ Guard release"
if: github.event.created == false && needs.pick-distro.outputs.is_version == 'true'
@@ -93,13 +92,13 @@ jobs:
fetch-depth: 0
fetch-tags: true
- name: Fetch master
- name: Fetch main
shell: bash
run: |
set -euo pipefail
git fetch origin master:refs/remotes/origin/master --force
git fetch origin main:refs/remotes/origin/main --force
- name: "Guard: tag commit must be contained in origin/master"
- name: "Guard: tag commit must be contained in origin/main"
id: guard
shell: bash
env:
@@ -117,12 +116,12 @@ jobs:
tag_sha="$(git rev-list -n 1 "${tag}")"
echo "Tag: ${tag} -> ${tag_sha}"
if git merge-base --is-ancestor "${tag_sha}" "origin/master"; then
if git merge-base --is-ancestor "${tag_sha}" "origin/main"; then
echo "allowed=true" >> "$GITHUB_OUTPUT"
echo "✅ Tag commit is on origin/master"
echo "✅ Tag commit is on origin/main"
else
echo "allowed=false" >> "$GITHUB_OUTPUT"
echo "⛔ Tag commit is NOT on origin/master"
echo "⛔ Tag commit is NOT on origin/main"
fi
release-version:
+46 -2
View File
@@ -17,11 +17,36 @@ on:
required: false
type: string
default: "."
source_repository:
description: "Optional source repository for image config scan (e.g. fork owner/repo)"
required: false
type: string
default: ""
source_ref:
description: "Optional source ref/SHA for image config scan"
required: false
type: string
default: ""
source_path:
description: "Optional checkout path for source_repository/source_ref"
required: false
type: string
default: "_pr_source"
images_per_hour:
description: "Optional throttle: max images mirrored per hour"
required: false
type: string
default: ""
concurrency_channel:
description: "Optional channel to separate concurrency groups between callers"
required: false
type: string
default: "default"
concurrency_key:
description: "Optional key (e.g. pr-123) to scope concurrency within a channel"
required: false
type: string
default: ""
secrets:
DOCKERHUB_USERNAME:
required: false
@@ -33,7 +58,7 @@ permissions:
packages: write
concurrency:
group: mirror-images-${{ github.repository }}-${{ github.ref_name }}
group: mirror-images-${{ github.repository }}-${{ inputs.concurrency_channel }}-${{ inputs.concurrency_key || github.ref_name }}
# Cancel scheduled Mirror sync, because may it works with outdatet mirror lists
cancel-in-progress: true
@@ -52,6 +77,16 @@ jobs:
- name: Checkout
uses: actions/checkout@v6
- name: Checkout source repo for scan (optional)
if: ${{ inputs.source_repository != '' && inputs.source_ref != '' }}
uses: actions/checkout@v6
with:
repository: ${{ inputs.source_repository }}
ref: ${{ inputs.source_ref }}
path: ${{ inputs.source_path }}
fetch-depth: 1
persist-credentials: false
- name: Setup Python
uses: actions/setup-python@v6
with:
@@ -69,7 +104,7 @@ jobs:
# Optional: Docker Hub login to avoid rate limits while mirroring
- name: Login to Docker Hub (optional)
if: ${{ env.DOCKERHUB_USERNAME != '' && env.DOCKERHUB_TOKEN != '' }}
if: ${{ env.DOCKERHUB_USERNAME != '' && env.DOCKERHUB_TOKEN != '' && !(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
uses: docker/login-action@v4
with:
username: ${{ env.DOCKERHUB_USERNAME }}
@@ -82,7 +117,16 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Wait for mirrored images (fork PR)
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository }}
env:
GHCR_NAMESPACE: ${{ inputs.ghcr_namespace || github.repository_owner }}
GHCR_PREFIX: ${{ inputs.ghcr_prefix || 'mirror' }}
REPO_ROOT: ${{ inputs.repo_root || '.' }}
run: ./scripts/ci/wait_mirrored_images.sh
- name: Mirror images to GHCR (only missing, best-effort)
if: ${{ !(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
shell: bash
run: |
set -euo pipefail