Create unified slots service for CC Hybrid scheduling #33624
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Validates backend approval requirements for PRs | |
| # Runs on PR updates and review submissions to ensure proper approval from backend-review-group | |
| # or exemption through team-based file ownership | |
| name: Backend Approval | |
| on: | |
| pull_request_review: | |
| types: [submitted, dismissed] | |
| permissions: | |
| id-token: write | |
| contents: read | |
| pull-requests: write | |
| concurrency: | |
| group: backend-approval-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| env: | |
| AWS_REGION: us-gov-west-1 | |
| SSM_BOT_TOKEN_PATH: /devops/VA_VSP_BOT_GITHUB_TOKEN | |
| jobs: | |
| backend-approval-check: | |
| if: ${{ github.actor != 'github-copilot' }} | |
| name: Succeed if backend approval is confirmed | |
| runs-on: ubuntu-latest | |
| permissions: write-all | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| fetch-depth: 0 | |
| - name: Fetch PR data and changed files | |
| id: pr_info | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| PR_NUMBER="${{ github.event.pull_request.number }}" | |
| echo "pr_number=${PR_NUMBER}" >> "$GITHUB_OUTPUT" | |
| # Fetch PR metadata | |
| PR_DATA=$(gh api /repos/${{ github.repository }}/pulls/${PR_NUMBER} --jq '{ | |
| author: .user.login, | |
| draft: .draft, | |
| labels: [.labels[].name] | |
| }') | |
| echo "pr_author=$(echo "$PR_DATA" | jq -r '.author')" >> "$GITHUB_OUTPUT" | |
| echo "pr_draft=$(echo "$PR_DATA" | jq -r '.draft')" >> "$GITHUB_OUTPUT" | |
| echo "pr_labels=$(echo "$PR_DATA" | jq -c '.labels')" >> "$GITHUB_OUTPUT" | |
| # Fetch changed files (handles pagination automatically) | |
| CHANGED_FILES=$(gh api "/repos/${{ github.repository }}/pulls/${PR_NUMBER}/files" \ | |
| --paginate --jq '.[] | select(.status != "removed") | .filename') | |
| echo "$CHANGED_FILES" > /tmp/changed_files.txt | |
| FILE_COUNT=$(echo "$CHANGED_FILES" | wc -l) | |
| echo "file_count=${FILE_COUNT}" >> "$GITHUB_OUTPUT" | |
| echo "PR #${PR_NUMBER} Analysis" | |
| echo " Author: $(echo "$PR_DATA" | jq -r '.author')" | |
| echo " Draft: $(echo "$PR_DATA" | jq -r '.draft')" | |
| echo " Changed files: ${FILE_COUNT}" | |
| - name: Analyze file ownership against CODEOWNERS | |
| id: file_ownership | |
| run: | | |
| if [[ ! -s /tmp/changed_files.txt ]]; then | |
| echo "all_files_exempt=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| CODEOWNERS_FILE=".github/CODEOWNERS" | |
| if [[ ! -f "$CODEOWNERS_FILE" ]]; then | |
| echo "[ERROR] CODEOWNERS file not found" | |
| echo "all_files_exempt=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| EXEMPT_TEAM_ARRAY=( | |
| "octo-identity" | |
| "mobile-api-team" | |
| ) | |
| all_files_exempt=true | |
| echo "Analyzing Code Owners" | |
| echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | |
| echo "" | |
| while IFS= read -r file || [[ -n "$file" ]]; do | |
| [[ -z "$file" ]] && continue | |
| echo "File: $file" | |
| result=$(awk -v f="$file" ' | |
| $0 !~ /^#/ && NF > 0 { | |
| p = $1 | |
| orig = p | |
| gsub(/\*\*\//, "<!DS!>", p) | |
| gsub(/\*/, "<!S!>", p) | |
| if (p ~ /\/$/) sub(/\/$/, "/<!S!>", p) | |
| else if (p !~ /<!S!>/ && p !~ /<!DS!>/ && p !~ /\./) p = "(" p "$|" p "/<!S!>)" | |
| gsub(/\./, "\\.", p) | |
| gsub(/<!DS!>/, ".*", p) | |
| gsub(/<!S!>/, ".*", p) | |
| if (f ~ "^" p) { | |
| pattern = orig | |
| owners = "" | |
| for (i=2; i<=NF; i++) owners = owners $i " " | |
| } | |
| } | |
| END { | |
| if (pattern && owners) { | |
| print pattern | |
| print owners | |
| exit 0 | |
| } | |
| exit 1 | |
| } | |
| ' "$CODEOWNERS_FILE") | |
| if [[ $? -ne 0 || -z "$result" ]]; then | |
| echo " Pattern matched: (none)" | |
| echo " Code owners: (none)" | |
| echo " Status: [X] REQUIRES APPROVAL" | |
| echo "" | |
| all_files_exempt=false | |
| continue | |
| fi | |
| pattern=$(echo "$result" | head -1) | |
| owners=$(echo "$result" | tail -1) | |
| echo " Pattern matched: \"$pattern\"" | |
| echo " Code owners: $owners" | |
| file_is_exempt=false | |
| for team in "${EXEMPT_TEAM_ARRAY[@]}"; do | |
| if echo "$owners" | grep -q "@department-of-veterans-affairs/${team}"; then | |
| echo " Status: [OK] EXEMPT (via team: $team)" | |
| file_is_exempt=true | |
| break | |
| fi | |
| done | |
| if [[ "$file_is_exempt" == "false" ]]; then | |
| echo " Status: [X] REQUIRES APPROVAL" | |
| all_files_exempt=false | |
| fi | |
| echo "" | |
| done < /tmp/changed_files.txt | |
| echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | |
| if [[ "$all_files_exempt" == "true" ]]; then | |
| echo "All files are exempt" | |
| else | |
| echo "Some files require backend approval" | |
| fi | |
| echo "all_files_exempt=${all_files_exempt}" >> "$GITHUB_OUTPUT" | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6 | |
| with: | |
| role-to-assume: ${{ vars.AWS_ASSUME_ROLE }} | |
| aws-region: ${{ env.AWS_REGION }} | |
| # VA_VSP_BOT_GITHUB_TOKEN is required to access organization team membership | |
| - name: Retrieve bot token from AWS SSM | |
| uses: marvinpinto/action-inject-ssm-secrets@40db08dfe313758837e611cac1679e3a89b35531 # latest | |
| with: | |
| ssm_parameter: ${{ env.SSM_BOT_TOKEN_PATH }} | |
| env_variable_name: VA_VSP_BOT_GITHUB_TOKEN | |
| - name: Determine approval status | |
| id: approval_status | |
| env: | |
| GITHUB_TOKEN: ${{ env.VA_VSP_BOT_GITHUB_TOKEN }} | |
| PR_NUMBER: ${{ steps.pr_info.outputs.pr_number }} | |
| ALL_FILES_EXEMPT: ${{ steps.file_ownership.outputs.all_files_exempt }} | |
| run: | | |
| exempt=false | |
| exempt_reason="" | |
| backend_approved=false | |
| requires_approval=true | |
| echo "Determining approval requirements" | |
| echo "----------------------------------------" | |
| # Check: Are all files owned by exempt teams? | |
| if [[ "${ALL_FILES_EXEMPT}" == "true" ]]; then | |
| echo "[PASS] Exempt: All files owned by exempt teams" | |
| exempt=true | |
| exempt_reason="file ownership" | |
| requires_approval=false | |
| fi | |
| # If not exempt, verify backend approval | |
| if [[ "${requires_approval}" == "true" ]]; then | |
| echo "Checking for backend-review-group approval..." | |
| # Fetch backend-review-group members | |
| BACKEND_REVIEWERS=$(gh api /orgs/department-of-veterans-affairs/teams/backend-review-group/members --jq '.[].login') | |
| BACKEND_REVIEWERS=$(echo "$BACKEND_REVIEWERS" | tr '\n' '|' | sed 's/|$//') | |
| echo " Backend reviewers: ${BACKEND_REVIEWERS}" | |
| # Get most recent review state per user | |
| REVIEWS_JSON=$(gh api /repos/${{ github.repository }}/pulls/${PR_NUMBER}/reviews --paginate --jq ' | |
| .[] | select(.state == "APPROVED") | {login: .user.login, submitted_at: .submitted_at} | |
| ' 2>/dev/null || echo "[]") | |
| readarray -t APPROVALS < <( | |
| echo "$REVIEWS_JSON" | jq -s ' | |
| sort_by(.submitted_at) | |
| | reverse | |
| | unique_by(.login) | |
| | .[].login | |
| ' -r 2>/dev/null || true | |
| ) | |
| # Check if any approver is from backend-review-group | |
| for approver in "${APPROVALS[@]}"; do | |
| if echo "$approver" | grep -iqE "^(${BACKEND_REVIEWERS})$"; then | |
| echo "[PASS] Backend approval confirmed by: ${approver}" | |
| backend_approved=true | |
| requires_approval=false | |
| break | |
| fi | |
| done | |
| if [[ "${backend_approved}" == "false" ]]; then | |
| echo "No approval from backend-review-group" | |
| fi | |
| fi | |
| echo "----------------------------------------" | |
| echo "Final Status:" | |
| echo " Exempt: ${exempt}" | |
| [[ "${exempt}" == "true" ]] && echo " Exempt Reason: ${exempt_reason}" | |
| echo " Backend Approved: ${backend_approved}" | |
| echo " Requires Approval: ${requires_approval}" | |
| # Output results for subsequent steps | |
| echo "exempt=${exempt}" >> "$GITHUB_OUTPUT" | |
| echo "exempt_reason=${exempt_reason}" >> "$GITHUB_OUTPUT" | |
| echo "backend_approved=${backend_approved}" >> "$GITHUB_OUTPUT" | |
| echo "requires_approval=${requires_approval}" >> "$GITHUB_OUTPUT" | |
| - name: Update PR labels | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PR_NUMBER: ${{ steps.pr_info.outputs.pr_number }} | |
| EXEMPT: ${{ steps.approval_status.outputs.exempt }} | |
| REQUIRES_APPROVAL: ${{ steps.approval_status.outputs.requires_approval }} | |
| run: | | |
| echo "Updating PR labels..." | |
| # Determine which labels to add/remove based on status | |
| LABELS_TO_ADD=() | |
| LABELS_TO_REMOVE=() | |
| if [[ "${EXEMPT}" == "true" ]]; then | |
| LABELS_TO_ADD+=("exempt-be-review") | |
| LABELS_TO_REMOVE+=("require-backend-approval") | |
| else | |
| LABELS_TO_REMOVE+=("exempt-be-review") | |
| fi | |
| if [[ "${REQUIRES_APPROVAL}" == "true" ]]; then | |
| LABELS_TO_ADD+=("require-backend-approval") | |
| else | |
| LABELS_TO_REMOVE+=("require-backend-approval") | |
| fi | |
| # Apply label changes | |
| for label in "${LABELS_TO_REMOVE[@]}"; do | |
| echo " Removing: ${label}" | |
| gh pr edit "${PR_NUMBER}" --remove-label "${label}" 2>/dev/null || echo " [WARN] Could not remove ${label}" | |
| done | |
| for label in "${LABELS_TO_ADD[@]}"; do | |
| echo " Adding: ${label}" | |
| gh pr edit "${PR_NUMBER}" --add-label "${label}" 2>/dev/null || echo " [WARN] Could not add ${label}" | |
| done | |
| - name: Fail if backend approval required | |
| if: steps.approval_status.outputs.requires_approval == 'true' | |
| run: | | |
| echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | |
| echo "[FAIL] BACKEND APPROVAL REQUIRED" | |
| echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | |
| echo "" | |
| echo "This PR requires approval from the backend-review-group before merging." | |
| echo "" | |
| echo "To get your PR exempt from this requirement, ensure:" | |
| echo " • All changed files are owned exclusively by exempt teams in CODEOWNERS" | |
| echo " • Exempt teams: octo-identity, mobile-api-team" | |
| echo "" | |
| echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | |
| exit 1 | |
| - name: Confirm approval success | |
| if: steps.approval_status.outputs.requires_approval == 'false' | |
| run: | | |
| echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | |
| echo "[PASS] APPROVAL CHECK PASSED" | |
| echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | |
| if [[ "${{ steps.approval_status.outputs.exempt }}" == "true" ]]; then | |
| echo "" | |
| echo "PR is exempt from backend approval requirement" | |
| echo "Reason: ${{ steps.approval_status.outputs.exempt_reason }}" | |
| else | |
| echo "" | |
| echo "Backend approval has been confirmed" | |
| fi | |
| echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | |
| exit 0 |