From b9ceb3e0c8a6b3469ae7460e25a9eed9d2134365 Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Wed, 20 May 2026 13:27:52 +0000 Subject: [PATCH 1/4] fix(CI): inline dual-approval-check workflow, install curl/jq (PRI-1636) Co-Authored-By: Paperclip --- .github/workflows/dual-approval.yaml | 105 +++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dual-approval.yaml b/.github/workflows/dual-approval.yaml index df21295..d255129 100644 --- a/.github/workflows/dual-approval.yaml +++ b/.github/workflows/dual-approval.yaml @@ -1,6 +1,5 @@ name: Promotion Gate -# Calls the shared promotion gate workflow. # dev PRs: no gate (engineer self-merges). # uat PRs: QA approval required. # main PRs: UAT approval required (uat→main promotions). @@ -14,7 +13,103 @@ on: jobs: promotion-gate: - uses: privilegedescalation/.github/.github/workflows/dual-approval-check.yaml@main - secrets: inherit - with: - pr_number: ${{ github.event.pull_request.number }} + name: Promotion Gate + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Install dependencies + run: apt-get update -qq && apt-get install -y --no-install-recommends curl jq + + - name: Check promotion approval + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + REPO: ${{ github.repository }} + BASE_REF: ${{ github.base_ref }} + run: | + if [ -z "${PR_NUMBER}" ] || [ "${PR_NUMBER}" = "null" ]; then + echo "::notice::No PR number in context. Skipping promotion gate." + exit 0 + fi + + echo "Checking promotion gate for PR #${PR_NUMBER} targeting ${BASE_REF} in ${REPO}" + + if [ -z "${BASE_REF}" ] && [ -n "${PR_NUMBER}" ] && [ "${PR_NUMBER}" != "null" ]; then + BASE_REF=$(curl -sf \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Accept: application/json" \ + "https://git.farh.net/api/v1/repos/${REPO}/pulls/${PR_NUMBER}" | jq -r '.base.ref') + echo "BASE_REF was empty; resolved from PR #${PR_NUMBER} API: ${BASE_REF}" + fi + + # Determine required reviewer based on target branch + case "${BASE_REF}" in + dev) + echo "Target is dev — no review required. Engineers self-merge." + exit 0 + ;; + uat) + REQUIRED_REVIEWER="pe_regina" + GATE_NAME="QA" + ;; + main) + REQUIRED_REVIEWER="pe_regina" + GATE_NAME="QA" + # For plugin repos (Pipeline A), UAT approval is needed for uat→main + # Check if the source branch is uat + SOURCE_REF=$(curl -sf \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Accept: application/json" \ + "https://git.farh.net/api/v1/repos/${REPO}/pulls/${PR_NUMBER}" | jq -r '.head.ref') + + if [ "${SOURCE_REF}" = "uat" ]; then + REQUIRED_REVIEWER="pe_patty" + GATE_NAME="UAT" + fi + ;; + *) + echo "::notice::Target branch '${BASE_REF}' has no promotion gate configured." + exit 0 + ;; + esac + + echo "Required reviewer: ${REQUIRED_REVIEWER} (${GATE_NAME})" + + # For uat→main promotions, pe_patty may not be able to review (bot account). + # Accept pe_nancy (CTO) as a valid alternative reviewer. + ALT_REVIEWER="" + if [ "${REQUIRED_REVIEWER}" = "pe_patty" ]; then + ALT_REVIEWER="pe_nancy" + fi + + REVIEWS=$(curl -sf \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Accept: application/json" \ + "https://git.farh.net/api/v1/repos/${REPO}/pulls/${PR_NUMBER}/reviews") + + if [ -z "${REVIEWS}" ] || [ "${REVIEWS}" = "null" ]; then + echo "::warning::Could not fetch reviews for PR #${PR_NUMBER}." + exit 1 + fi + + REVIEWER_APPROVED=$(echo "${REVIEWS}" | jq -r --arg user "${REQUIRED_REVIEWER}" \ + '[.[] | select(.user.login == $user)] | last | if .state then .state == "APPROVED" else false end') + + echo "${GATE_NAME} (${REQUIRED_REVIEWER}) approved: ${REVIEWER_APPROVED}" + + # Fallback: check if CTO approved as alternative for uat→main + if [ "${REVIEWER_APPROVED}" != "true" ] && [ -n "${ALT_REVIEWER}" ]; then + REVIEWER_APPROVED=$(echo "${REVIEWS}" | jq -r --arg user "${ALT_REVIEWER}" \ + '[.[] | select(.user.login == $user)] | last | if .state then .state == "APPROVED" else false end') + if [ "${REVIEWER_APPROVED}" = "true" ]; then + echo "CTO (${ALT_REVIEWER}) approved as fallback for UAT gate." + fi + fi + + if [ "${REVIEWER_APPROVED}" = "true" ]; then + echo "Promotion gate passed: ${GATE_NAME} has approved." + else + echo "Promotion gate failed: waiting for ${GATE_NAME} approval from ${REQUIRED_REVIEWER}." + exit 1 + fi \ No newline at end of file From 4bac80683e96b5370e0376ad864dc894910ae149 Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Wed, 20 May 2026 13:38:45 +0000 Subject: [PATCH 2/4] fix(CI): add container ubuntu:latest for apt-get (PRI-1636) Co-Authored-By: Paperclip --- .github/workflows/dual-approval.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/dual-approval.yaml b/.github/workflows/dual-approval.yaml index d255129..bfc42f2 100644 --- a/.github/workflows/dual-approval.yaml +++ b/.github/workflows/dual-approval.yaml @@ -15,6 +15,8 @@ jobs: promotion-gate: name: Promotion Gate runs-on: ubuntu-latest + container: ubuntu:latest + container: ubuntu:latest timeout-minutes: 5 steps: From e12914b295990f823d1c306bd58b27e205248e01 Mon Sep 17 00:00:00 2001 From: "Regression Regina [agent]" Date: Wed, 20 May 2026 13:48:45 +0000 Subject: [PATCH 3/4] fix(ci): remove duplicate container key in dual-approval.yaml (PRI-1636) Co-Authored-By: Paperclip --- .github/workflows/dual-approval.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/dual-approval.yaml b/.github/workflows/dual-approval.yaml index bfc42f2..81b55ff 100644 --- a/.github/workflows/dual-approval.yaml +++ b/.github/workflows/dual-approval.yaml @@ -16,7 +16,6 @@ jobs: name: Promotion Gate runs-on: ubuntu-latest container: ubuntu:latest - container: ubuntu:latest timeout-minutes: 5 steps: @@ -114,4 +113,4 @@ jobs: else echo "Promotion gate failed: waiting for ${GATE_NAME} approval from ${REQUIRED_REVIEWER}." exit 1 - fi \ No newline at end of file + fi From 5aa76c9eb8e253044ad23249a1ba715428e31975 Mon Sep 17 00:00:00 2001 From: Gandalf the Greybeard <9+pe_gandalf@noreply.git.farh.net> Date: Wed, 20 May 2026 14:13:57 +0000 Subject: [PATCH 4/4] fix: add ca-certificates for SSL CA verification in promotion gate --- .github/workflows/dual-approval.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dual-approval.yaml b/.github/workflows/dual-approval.yaml index 81b55ff..2462cc9 100644 --- a/.github/workflows/dual-approval.yaml +++ b/.github/workflows/dual-approval.yaml @@ -20,7 +20,7 @@ jobs: steps: - name: Install dependencies - run: apt-get update -qq && apt-get install -y --no-install-recommends curl jq + run: apt-get update -qq && apt-get install -y --no-install-recommends ca-certificates curl jq - name: Check promotion approval env: