feat(tabs): add CycleMostRecentTab as third Ctrl+Tab behavior option #3462
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
| name: Check Approvals | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, ready_for_review] | |
| pull_request_review: | |
| types: [submitted] | |
| jobs: | |
| check_owners: | |
| name: Check OWNERS approval | |
| runs-on: ubuntu-latest | |
| # Skip draft PRs. | |
| if: github.event.pull_request.draft == false | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| fetch-depth: 0 | |
| # We don't actually need the contents of the files, just their names. | |
| filter: blob:none | |
| - name: Get changed files | |
| id: changed_files | |
| run: | | |
| HEAD_SHA=${{ github.event.pull_request.head.sha }} | |
| BASE_SHA=${{ github.event.pull_request.base.sha }} | |
| MERGE_BASE=$(git merge-base "$BASE_SHA" "$HEAD_SHA") | |
| CHANGED_FILES=$(git diff --name-only "$MERGE_BASE" "$HEAD_SHA" | tr '\n' ' ') | |
| echo "files=$CHANGED_FILES" >> "$GITHUB_OUTPUT" | |
| - name: Get PR approvers | |
| id: approvers | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| PR_NUMBER=${{ github.event.pull_request.number }} | |
| PR_AUTHOR=${{ github.event.pull_request.user.login }} | |
| # Get all approved reviews. | |
| REVIEWERS=$(gh pr view "$PR_NUMBER" --json reviews --jq '[.reviews[] | select(.state == "APPROVED") | .author.login] | unique | join(" ")') | |
| # Combine PR author (self-approves) with reviewers. | |
| ALL_APPROVERS="$PR_AUTHOR $REVIEWERS" | |
| echo "approvers=$ALL_APPROVERS" >> "$GITHUB_OUTPUT" | |
| echo "has_reviewers=$( [[ -n "$REVIEWERS" ]] && echo true || echo false )" >> "$GITHUB_OUTPUT" | |
| echo "PR author: $PR_AUTHOR" | |
| echo "Reviewers who approved: $REVIEWERS" | |
| echo "All approvers: $ALL_APPROVERS" | |
| - name: Check OWNERS approval | |
| env: | |
| CHANGED_FILES: ${{ steps.changed_files.outputs.files }} | |
| APPROVERS: ${{ steps.approvers.outputs.approvers }} | |
| run: | | |
| set -e | |
| # Convert approvers to an array. | |
| read -ra APPROVER_ARRAY <<< "$APPROVERS" | |
| # Function to get owners for a file by walking up the directory tree. | |
| get_owners_for_file() { | |
| local file="$1" | |
| local dir | |
| dir=$(dirname "$file") | |
| local owners=() | |
| # Walk up the directory tree. | |
| while [[ "$dir" != "." && "$dir" != "/" ]]; do | |
| if [[ -f "$dir/OWNERS" ]]; then | |
| # Read owners file, skip comments and empty lines. | |
| while IFS= read -r line || [[ -n "$line" ]]; do | |
| # Skip comments and empty lines. | |
| line=$(echo "$line" | sed 's/#.*//' | xargs) | |
| if [[ -n "$line" ]]; then | |
| owners+=("$line") | |
| fi | |
| done < "$dir/OWNERS" | |
| fi | |
| dir=$(dirname "$dir") | |
| done | |
| # Check root directory. | |
| if [[ -f "OWNERS" ]]; then | |
| while IFS= read -r line || [[ -n "$line" ]]; do | |
| line=$(echo "$line" | sed 's/#.*//' | xargs) | |
| if [[ -n "$line" ]]; then | |
| owners+=("$line") | |
| fi | |
| done < "OWNERS" | |
| fi | |
| # Return unique owners. | |
| printf '%s\n' "${owners[@]}" | sort -u | tr '\n' ' ' | sed 's/ $//' | |
| } | |
| # Function to check if any approver is in the owners list. | |
| has_owner_approval() { | |
| local owners="$1" | |
| for approver in "${APPROVER_ARRAY[@]}"; do | |
| if echo "$owners" | grep -qw "$approver"; then | |
| return 0 | |
| fi | |
| done | |
| return 1 | |
| } | |
| MISSING_APPROVAL=() | |
| FILES_CHECKED=0 | |
| for file in $CHANGED_FILES; do | |
| # Skip if file doesn't exist (deleted files). | |
| FILES_CHECKED=$((FILES_CHECKED + 1)) | |
| OWNERS=$(get_owners_for_file "$file") | |
| if [[ -z "$OWNERS" ]]; then | |
| # No OWNERS files in ancestry - automatically passes. | |
| echo "✓ $file (no OWNERS requirement)" | |
| continue | |
| fi | |
| if has_owner_approval "$OWNERS"; then | |
| echo "✓ $file" | |
| else | |
| echo "✗ $file (owners: $OWNERS)" | |
| MISSING_APPROVAL+=("$file") | |
| fi | |
| done | |
| echo "" | |
| echo "Files checked: $FILES_CHECKED" | |
| echo "Approvers: ${APPROVER_ARRAY[*]}" | |
| # Save missing files for comment step (must happen before exit). | |
| echo "MISSING_FILES<<EOF" >> "$GITHUB_ENV" | |
| for file in "${MISSING_APPROVAL[@]}"; do | |
| OWNERS=$(get_owners_for_file "$file") | |
| echo "- \`$file\` (needs approval from: $OWNERS)" >> "$GITHUB_ENV" | |
| done | |
| echo "EOF" >> "$GITHUB_ENV" | |
| if [[ ${#MISSING_APPROVAL[@]} -gt 0 ]]; then | |
| echo "" | |
| echo "::error::The following files are missing OWNERS approval:" | |
| for file in "${MISSING_APPROVAL[@]}"; do | |
| OWNERS=$(get_owners_for_file "$file") | |
| echo "::error:: - $file (needs approval from: $OWNERS)" | |
| done | |
| exit 1 | |
| fi | |
| echo "" | |
| echo "All files have required OWNERS approval." | |
| - name: Comment on PR about missing approvals | |
| if: failure() && github.event_name == 'pull_request_review' && steps.approvers.outputs.has_reviewers == 'true' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| PR_NUMBER=${{ github.event.pull_request.number }} | |
| gh pr comment "$PR_NUMBER" --body "The following files still need approval from their OWNERS:\n\n$MISSING_FILES" |