mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-12 13:47:01 +00:00
Untrusted fork PRs mirrored anonymously after the credential hardening (empty Docker Hub creds -> Login to Docker Hub skipped -> anonymous pulls), which got rate-limited and failed images-mirror-missing-fork. Restore secrets: inherit on the fork mirror job and the GHCR login token so the mirror authenticates again. The build keeps its hardening (it pulls its parent from GHCR and needs no Docker Hub creds). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
179 lines
6.2 KiB
YAML
179 lines
6.2 KiB
YAML
name: "⚡ CI: Pull Request"
|
|
run-name: >-
|
|
${{ github.event_name }} / PR #${{ github.event.pull_request.number }} / ${{ github.event.action }} / ${{ github.event.pull_request.head.label }}
|
|
|
|
on:
|
|
pull_request:
|
|
types:
|
|
- opened
|
|
- synchronize
|
|
- reopened
|
|
- ready_for_review
|
|
- labeled
|
|
pull_request_target:
|
|
types:
|
|
- opened
|
|
- synchronize
|
|
- reopened
|
|
- ready_for_review
|
|
- labeled
|
|
|
|
concurrency:
|
|
# Keep only the newest run per PR and event type.
|
|
# This avoids pull_request and pull_request_target cancelling each other.
|
|
group: pr-ci-${{ github.repository }}-${{ github.event_name }}-pr-${{ github.event.pull_request.number }}
|
|
cancel-in-progress: true
|
|
|
|
permissions:
|
|
actions: write
|
|
contents: read
|
|
packages: write
|
|
security-events: write
|
|
|
|
jobs:
|
|
detect-pr-scope:
|
|
name: 🔎 Detect PR scope
|
|
if: ${{ github.event.pull_request.draft == false && (github.event_name == 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository) }}
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
scope: ${{ steps.resolve.outputs.scope }}
|
|
run_ci_orchestrator: ${{ steps.resolve.outputs.run_ci_orchestrator }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v7
|
|
|
|
- name: Resolve PR scope
|
|
id: resolve
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
REPOSITORY: ${{ github.repository }}
|
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
run: ./scripts/meta/resolve/pr/scope.sh
|
|
|
|
validate-pr-branch-prefix:
|
|
name: 🧭 Validate PR branch prefix
|
|
needs: detect-pr-scope
|
|
if: ${{ github.event_name == 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v7
|
|
|
|
- name: Validate branch prefix for detected scope
|
|
env:
|
|
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
|
PR_SCOPE: ${{ needs.detect-pr-scope.outputs.scope }}
|
|
run: ./scripts/meta/resolve/pr/branch_prefix.sh
|
|
|
|
pr-scope-gate:
|
|
name: 🚦 Gate PR scope
|
|
needs:
|
|
- detect-pr-scope
|
|
- validate-pr-branch-prefix
|
|
if: ${{ needs.detect-pr-scope.outputs.run_ci_orchestrator == 'false' }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Confirm lightweight PR scope
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
echo "Lightweight PR scope '${{ needs.detect-pr-scope.outputs.scope }}' passed."
|
|
|
|
detect-affected-roles:
|
|
name: 🧭 Detect affected roles
|
|
needs: detect-pr-scope
|
|
if: ${{ needs.detect-pr-scope.outputs.run_ci_orchestrator == 'true' }}
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
whitelist: ${{ steps.resolve.outputs.whitelist }}
|
|
roles_only: ${{ steps.resolve.outputs.roles_only }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@v6
|
|
with:
|
|
python-version: "3.12"
|
|
cache: pip
|
|
cache-dependency-path: pyproject.toml
|
|
|
|
- name: Install project (host venv)
|
|
shell: bash
|
|
run: make install
|
|
|
|
- name: Resolve affected roles
|
|
id: resolve
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
resolved="$(./scripts/meta/resolve/diff/affected_roles.sh)"
|
|
echo "Resolver output: ${resolved}"
|
|
if [[ "${resolved}" == "__ALL__" ]]; then
|
|
echo "whitelist=__ALL__" >>"${GITHUB_OUTPUT}"
|
|
echo "roles_only=false" >>"${GITHUB_OUTPUT}"
|
|
else
|
|
echo "whitelist=${resolved}" >>"${GITHUB_OUTPUT}"
|
|
echo "roles_only=true" >>"${GITHUB_OUTPUT}"
|
|
fi
|
|
|
|
ci-orchestrator:
|
|
needs:
|
|
- detect-pr-scope
|
|
- validate-pr-branch-prefix
|
|
- detect-affected-roles
|
|
if: ${{ github.event_name == 'pull_request' && needs.detect-pr-scope.outputs.run_ci_orchestrator == 'true' }}
|
|
uses: ./.github/workflows/ci-orchestrator.yml
|
|
with:
|
|
distros: "arch debian ubuntu fedora centos"
|
|
whitelist: ${{ needs.detect-affected-roles.outputs.whitelist }}
|
|
roles_only: ${{ needs.detect-affected-roles.outputs.roles_only }}
|
|
trusted_pr: ${{ contains(github.event.pull_request.labels.*.name, '🛡️ Trusted') }}
|
|
|
|
prepare-fork-merge-ref:
|
|
needs:
|
|
- detect-pr-scope
|
|
- validate-pr-branch-prefix
|
|
if: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository && needs.detect-pr-scope.outputs.run_ci_orchestrator == 'true' }}
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
checkout_ref: ${{ steps.out.outputs.checkout_ref }}
|
|
image_tag: ${{ steps.out.outputs.image_tag }}
|
|
trusted: ${{ contains(github.event.pull_request.labels.*.name, '🛡️ Trusted') }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v7
|
|
|
|
- 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/meta/resolve/pr/merge_ref.sh
|
|
|
|
build-ci-images-fork:
|
|
needs: prepare-fork-merge-ref
|
|
uses: ./.github/workflows/images-build-ci.yml
|
|
with:
|
|
distros: "arch debian ubuntu fedora centos"
|
|
checkout_ref: ${{ needs.prepare-fork-merge-ref.outputs.trusted == 'true' && github.event.pull_request.head.sha || needs.prepare-fork-merge-ref.outputs.checkout_ref }}
|
|
source_repository: ${{ needs.prepare-fork-merge-ref.outputs.trusted == 'true' && github.event.pull_request.head.repo.full_name || github.repository }}
|
|
image_tag: ${{ needs.prepare-fork-merge-ref.outputs.image_tag }}
|
|
concurrency_channel: pr-target-build-producer
|
|
allow_unsafe_checkout: true
|
|
|
|
images-mirror-missing-fork:
|
|
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 }}
|
|
allow_unsafe_checkout: true
|
|
secrets: inherit
|