From 56739edf65d5aebf065708654c5c61d5bce8f019 Mon Sep 17 00:00:00 2001 From: Millian Lamiaux Date: Sun, 12 Jul 2026 22:50:35 +0200 Subject: [PATCH] ci: trigger workflow on PR with build-only validation --- .github/workflows/admin-web-docker.yml | 41 +++++++- scripts/verify-admin-web-docker.sh | 132 ++++++++++++++++++++++++- 2 files changed, 167 insertions(+), 6 deletions(-) diff --git a/.github/workflows/admin-web-docker.yml b/.github/workflows/admin-web-docker.yml index 79d69de..ec01bc0 100644 --- a/.github/workflows/admin-web-docker.yml +++ b/.github/workflows/admin-web-docker.yml @@ -3,6 +3,10 @@ name: Admin Web Docker on: push: branches: [main] + pull_request: + paths: + - 'admin-web/**' + - '.github/workflows/admin-web-docker.yml' workflow_dispatch: concurrency: @@ -13,15 +17,46 @@ env: NODE_VERSION: "22" jobs: + build-validation: + name: Docker Build Validation + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + + - name: Install Docker CLI + run: sudo apt-get update -qq && sudo apt-get install -y -qq docker.io + + - name: Build admin-web image (no push) + env: + NEXT_PUBLIC_SUPABASE_URL: ${{ vars.NEXT_PUBLIC_SUPABASE_URL }} + NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ vars.NEXT_PUBLIC_SUPABASE_ANON_KEY }} + run: | + set -e + docker build \ + --file admin-web/Dockerfile \ + --build-arg "NEXT_PUBLIC_SUPABASE_URL=${NEXT_PUBLIC_SUPABASE_URL}" \ + --build-arg "NEXT_PUBLIC_SUPABASE_ANON_KEY=${NEXT_PUBLIC_SUPABASE_ANON_KEY}" \ + --tag "tabatago-admin-web:pr-validation" \ + admin-web + semantic-release: name: Semantic Release runs-on: ubuntu-latest timeout-minutes: 10 if: | - github.event_name == 'workflow_dispatch' || + github.event_name != 'pull_request' && ( - !contains(github.event.head_commit.message, '[skip ci]') && - !contains(github.event.head_commit.message, '[ci skip]') + github.event_name == 'workflow_dispatch' || + ( + !contains(github.event.head_commit.message, '[skip ci]') && + !contains(github.event.head_commit.message, '[ci skip]') + ) ) outputs: released: ${{ steps.release.outputs.released }} diff --git a/scripts/verify-admin-web-docker.sh b/scripts/verify-admin-web-docker.sh index 3306b9f..0596d92 100755 --- a/scripts/verify-admin-web-docker.sh +++ b/scripts/verify-admin-web-docker.sh @@ -263,9 +263,9 @@ check_workflow_valid() { yaml_valid ".github/workflows/admin-web-docker.yml" "admin-web-docker.yml is valid YAML" } -# --- workflow: exactly 2 jobs --------------------------------------- # +# --- workflow: exactly 3 jobs --------------------------------------- # check_workflow_jobs() { - local label="workflow has exactly 2 jobs: semantic-release + build-and-push" + local label="workflow has exactly 3 jobs: build-validation + semantic-release + build-and-push" local f=".github/workflows/admin-web-docker.yml" if [[ ! -f "$f" ]]; then fail "$label" "file missing" @@ -281,7 +281,7 @@ print(','.join(sorted(jobs))) fail "$label" "could not parse jobs" return fi - if [[ "$out" == "build-and-push,semantic-release" ]]; then + if [[ "$out" == "build-and-push,build-validation,semantic-release" ]]; then pass "$label" else fail "$label" "jobs=[$out]" @@ -495,6 +495,126 @@ check_git_commit_prefix() { fi } +# =================================================================== # +# PR BUILD-VALIDATION FEATURE CHECKS +# (trigger pull_request + job build-validation build-only/no-secret) +# =================================================================== # + +# --- workflow: pull_request trigger with admin-web/** path filter --- # +check_pr_trigger() { + local f=".github/workflows/admin-web-docker.yml" + local label="workflow triggers on pull_request with admin-web/** path filter" + if [[ ! -f "$f" ]]; then + fail "$label" "file missing" + return + fi + local out + out=$(python3 -c " +import yaml,sys +d=yaml.safe_load(open(sys.argv[1])) +on=d.get(True) or d.get('on') or {} +if not isinstance(on,dict): print('NO-PR: on not a dict'); sys.exit() +pr=on.get('pull_request') +if pr is None: print('NO-PR: no pull_request key'); sys.exit() +paths=(pr.get('paths') if isinstance(pr,dict) else None) or [] +print('|'.join(str(p) for p in paths)) +" "$f" 2>/dev/null || echo "ERR") + if echo "$out" | grep -Eq 'admin-web/\*\*'; then + pass "$label" + else + fail "$label" "pull_request.paths=[$out]" + fi +} + +# --- build-validation job: exists + if: pull_request ---------------- # +check_build_validation_if() { + local f=".github/workflows/admin-web-docker.yml" + local label="build-validation job if: github.event_name == 'pull_request'" + if [[ ! -f "$f" ]]; then + fail "$label" "file missing" + return + fi + local cond + cond=$(python3 -c " +import yaml,sys +d=yaml.safe_load(open(sys.argv[1])) +bv=d.get('jobs',{}).get('build-validation',{}) +print(bv.get('if','') or '') +" "$f" 2>/dev/null || true) + if echo "$cond" | grep -Eq "github\.event_name\s*==\s*'pull_request'"; then + pass "$label" + else + fail "$label" "if='$cond'" + fi +} + +# --- build-validation job: NO docker push (build-only) -------------- # +check_build_validation_no_push() { + local f=".github/workflows/admin-web-docker.yml" + local label="build-validation does NOT docker push (build-only)" + if [[ ! -f "$f" ]]; then + fail "$label" "file missing" + return + fi + local out + out=$(python3 -c " +import yaml,sys,json +d=yaml.safe_load(open(sys.argv[1])) +bv=d.get('jobs',{}).get('build-validation',{}) +blob=json.dumps(bv) +print('PUSH' if 'docker push' in blob else 'NO-PUSH') +" "$f" 2>/dev/null || echo "ERR") + if [[ "$out" == "NO-PUSH" ]]; then + pass "$label" + else + fail "$label" "docker push found in build-validation job" + fi +} + +# --- build-validation job: NO secrets (public vars only) ------------ # +check_build_validation_no_secrets() { + local f=".github/workflows/admin-web-docker.yml" + local label="build-validation references no secrets (build-only, public vars only)" + if [[ ! -f "$f" ]]; then + fail "$label" "file missing" + return + fi + local out + out=$(python3 -c " +import yaml,sys,json +d=yaml.safe_load(open(sys.argv[1])) +bv=d.get('jobs',{}).get('build-validation',{}) +blob=json.dumps(bv) +print('SECRET' if 'secrets.' in blob else 'NO-SECRET') +" "$f" 2>/dev/null || echo "ERR") + if [[ "$out" == "NO-SECRET" ]]; then + pass "$label" + else + fail "$label" "secrets.* referenced in build-validation job" + fi +} + +# --- semantic-release if excludes pull_request ---------------------- # +check_sr_excludes_pr() { + local f=".github/workflows/admin-web-docker.yml" + local label="semantic-release if excludes pull_request" + if [[ ! -f "$f" ]]; then + fail "$label" "file missing" + return + fi + local cond + cond=$(python3 -c " +import yaml,sys +d=yaml.safe_load(open(sys.argv[1])) +print(d.get('jobs',{}).get('semantic-release',{}).get('if','') or '') +" "$f" 2>/dev/null || true) + if echo "$cond" | grep -Eq "github\.event_name\s*!=\s*'pull_request'"; then + pass "$label" + else + fail "$label" "if missing pull_request exclusion (if='$cond')" + fi +} + # =================================================================== # # RUN ALL # =================================================================== # @@ -525,6 +645,12 @@ check_buildargs_source check_actions_pinned check_git_branch check_git_commit_prefix +# PR build-validation feature checks +check_pr_trigger +check_build_validation_if +check_build_validation_no_push +check_build_validation_no_secrets +check_sr_excludes_pr echo "" echo "# Summary: $PASS_COUNT passed, $FAIL_COUNT failed"