From 6c0dcde8b514130c65be5dc2508c5c50f29b522c Mon Sep 17 00:00:00 2001 From: "privilegedescalation-ceo[bot]" <269721483+privilegedescalation-ceo[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 21:37:00 +0000 Subject: [PATCH] Replace dual-approval with promotion gate workflow (#177) New model: no review for dev PRs, QA gates uat, UAT gates main. Replaces the old CTO+QA dual-approval check. Co-authored-by: Chris Farhood Co-authored-by: Paperclip --- .github/workflows/dual-approval-check.yaml | 82 +++++++++++++--------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/.github/workflows/dual-approval-check.yaml b/.github/workflows/dual-approval-check.yaml index 57ef00f..d759e70 100644 --- a/.github/workflows/dual-approval-check.yaml +++ b/.github/workflows/dual-approval-check.yaml @@ -1,4 +1,4 @@ -name: Dual Approval Check +name: Promotion Gate on: workflow_call: @@ -7,38 +7,60 @@ on: description: "Pull request number" required: false type: number - cto-reviewer: - description: "GitHub username of the CTO reviewer" - required: false - type: string - default: "privilegedescalation-cto" - qa-reviewer: - description: "GitHub username of the QA reviewer" - required: false - type: string - default: "privilegedescalation-qa" jobs: - dual-approval: - name: Dual Approval (CTO + QA) + promotion-gate: + name: Promotion Gate runs-on: ubuntu-latest timeout-minutes: 5 steps: - - name: Check dual approval + - name: Check promotion approval env: GH_TOKEN: ${{ github.token }} - CTO_REVIEWER: ${{ inputs.cto-reviewer }} - QA_REVIEWER: ${{ inputs.qa-reviewer }} PR_NUMBER: ${{ inputs.pr_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 (dismissed review or workflow_call without pr_number). Skipping dual approval check — no action needed." + echo "::notice::No PR number in context. Skipping promotion gate." exit 0 fi - echo "Checking approvals on PR #${PR_NUMBER} in ${REPO}" + echo "Checking promotion gate for PR #${PR_NUMBER} targeting ${BASE_REF} in ${REPO}" + + # 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="privilegedescalation-qa" + GATE_NAME="QA" + ;; + main) + REQUIRED_REVIEWER="privilegedescalation-qa" + 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: Bearer ${GH_TOKEN}" \ + -H "Accept: application/vnd.github.v3+json" \ + "https://api.github.com/repos/${REPO}/pulls/${PR_NUMBER}" | jq -r '.head.ref') + + if [ "${SOURCE_REF}" = "uat" ]; then + REQUIRED_REVIEWER="privilegedescalation-uat" + 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})" REVIEWS=$(curl -sf \ -H "Authorization: Bearer ${GH_TOKEN}" \ @@ -46,28 +68,18 @@ jobs: "https://api.github.com/repos/${REPO}/pulls/${PR_NUMBER}/reviews") if [ -z "${REVIEWS}" ] || [ "${REVIEWS}" = "null" ]; then - echo "::warning::Could not fetch reviews for PR #${PR_NUMBER}. Assuming no approvals yet." + echo "::warning::Could not fetch reviews for PR #${PR_NUMBER}." exit 1 fi - CTO_APPROVED=$(echo "${REVIEWS}" | jq -r --arg user "${CTO_REVIEWER}" \ + REVIEWER_APPROVED=$(echo "${REVIEWS}" | jq -r --arg user "${REQUIRED_REVIEWER}" \ '[.[] | select(.user.login == $user or .user.login == ($user + "[bot]"))] | last | if .state then .state == "APPROVED" else false end') - QA_APPROVED=$(echo "${REVIEWS}" | jq -r --arg user "${QA_REVIEWER}" \ - '[.[] | select(.user.login == $user or .user.login == ($user + "[bot]"))] | last | if .state then .state == "APPROVED" else false end') + echo "${GATE_NAME} (${REQUIRED_REVIEWER}) approved: ${REVIEWER_APPROVED}" - echo "CTO (${CTO_REVIEWER}) approved: ${CTO_APPROVED}" - echo "QA (${QA_REVIEWER}) approved: ${QA_APPROVED}" - - if [ "${CTO_APPROVED}" = "true" ] && [ "${QA_APPROVED}" = "true" ]; then - echo "Both CTO and QA have approved. Dual approval check passed." + if [ "${REVIEWER_APPROVED}" = "true" ]; then + echo "Promotion gate passed: ${GATE_NAME} has approved." else - echo "Dual approval check failed." - if [ "${CTO_APPROVED}" != "true" ]; then - echo " Missing: CTO approval from ${CTO_REVIEWER}" - fi - if [ "${QA_APPROVED}" != "true" ]; then - echo " Missing: QA approval from ${QA_REVIEWER}" - fi + echo "Promotion gate failed: waiting for ${GATE_NAME} approval from ${REQUIRED_REVIEWER}." exit 1 - fi \ No newline at end of file + fi