chore(deps): bump picomatch in /src/microsoft-learn-mock #188
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
| # Dependabot major-version merge executor | |
| # | |
| # This is a companion to the AI review workflow. It is triggered in two ways: | |
| # 1. workflow_run: when the AI reviewer completes — labels added by GITHUB_TOKEN | |
| # do NOT fire pull_request:labeled events (GitHub security restriction), so | |
| # we listen for the reviewer workflow completing and scan for labeled PRs. | |
| # 2. workflow_dispatch: for manual one-off triggering by a maintainer. | |
| # 3. pull_request:labeled: kept for future cases where a PAT is used for labeling. | |
| # | |
| # DEFENSE-IN-DEPTH: This workflow does NOT blindly trust the label. Before | |
| # enabling auto-merge it re-verifies every safety condition: | |
| # 1. The PR was opened by dependabot[bot]. | |
| # 2. The PR has the "ai-approved-major-update" label. | |
| # 3. ALL CI workflow runs on the PR branch have passed (latest per workflow). | |
| # If any condition fails, the workflow skips/exits without merging. | |
| # | |
| # NOTE: The existing dependabot-auto-merge.yml already unconditionally | |
| # approves all dependabot PRs, so by the time this workflow runs the PR | |
| # already has an approval. This workflow only needs to enable auto-merge. | |
| name: Dependabot major-version auto-merge | |
| on: | |
| workflow_run: | |
| workflows: ["Dependabot Major Version Reviewer"] | |
| types: [completed] | |
| pull_request: | |
| types: [labeled] | |
| workflow_dispatch: | |
| inputs: | |
| pr_number: | |
| description: 'PR number(s) to enable auto-merge on (comma-separated, must have ai-approved-major-update label)' | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| jobs: | |
| auto-merge-major: | |
| runs-on: ubuntu-latest | |
| if: >- | |
| (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') | |
| || github.event_name == 'workflow_dispatch' | |
| || (github.event.label.name == 'ai-approved-major-update' | |
| && github.event.pull_request.user.login == 'dependabot[bot]' | |
| && github.repository == 'IntelliTect/try') | |
| steps: | |
| # ── workflow_run: loop over all labeled Dependabot PRs ────────── | |
| # Labels added by GITHUB_TOKEN never fire pull_request:labeled events. | |
| # Instead we trigger on reviewer completion and scan for labeled PRs. | |
| - name: Process all labeled PRs (workflow_run) | |
| if: github.event_name == 'workflow_run' | |
| env: | |
| GH_TOKEN: ${{ secrets.GH_PAT_WORKFLOW }} | |
| run: | | |
| echo "Fetching open Dependabot PRs with ai-approved-major-update label..." | |
| PR_NUMBERS=$(gh api "/repos/${{ github.repository }}/pulls?state=open&per_page=100" \ | |
| --jq '[.[] | select(.user.login == "dependabot[bot]") | select(any(.labels[]; .name == "ai-approved-major-update")) | .number] | .[]') | |
| if [ -z "$PR_NUMBERS" ]; then | |
| echo "No labeled Dependabot PRs found — nothing to do." | |
| exit 0 | |
| fi | |
| for PR_NUMBER in $PR_NUMBERS; do | |
| echo "" | |
| echo "════════════════════════════════════════" | |
| echo "Processing PR #$PR_NUMBER..." | |
| PR_DATA=$(gh api "/repos/${{ github.repository }}/pulls/$PR_NUMBER") | |
| PR_URL=$(echo "$PR_DATA" | jq -r '.html_url') | |
| PR_BRANCH=$(echo "$PR_DATA" | jq -r '.head.ref') | |
| PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.user.login') | |
| if [ "$PR_AUTHOR" != "dependabot[bot]" ]; then | |
| echo "::warning::PR #$PR_NUMBER author is '$PR_AUTHOR' — skipping." | |
| continue | |
| fi | |
| echo "Checking CI for branch: $PR_BRANCH" | |
| ALL_RUNS=$(gh api "/repos/${{ github.repository }}/actions/runs?branch=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))" "$PR_BRANCH")&per_page=100" \ | |
| --jq '[.workflow_runs[] | select(.name != "Dependabot major-version auto-merge")]') | |
| TOTAL=$(echo "$ALL_RUNS" | jq 'length') | |
| if [ "$TOTAL" -eq 0 ]; then | |
| echo "::warning::PR #$PR_NUMBER has no CI workflow runs — skipping." | |
| continue | |
| fi | |
| # Latest run per workflow (highest run_number) | |
| LATEST_RUNS=$(echo "$ALL_RUNS" | jq '[group_by(.name)[] | sort_by(.run_number) | last]') | |
| PENDING=$(echo "$LATEST_RUNS" | jq '[.[] | select(.status != "completed")] | length') | |
| FAILED=$(echo "$LATEST_RUNS" | jq '[.[] | select(.status == "completed" and .conclusion != "success" and .conclusion != "skipped")] | length') | |
| if [ "$PENDING" -gt 0 ]; then | |
| echo "::warning::PR #$PR_NUMBER has $PENDING pending CI run(s) — skipping." | |
| continue | |
| fi | |
| if [ "$FAILED" -gt 0 ]; then | |
| echo "::warning::PR #$PR_NUMBER has $FAILED failed CI run(s) — skipping." | |
| echo "$LATEST_RUNS" | jq '[.[] | select(.conclusion != "success" and .conclusion != "skipped")] | .[] | {name, conclusion}' | |
| continue | |
| fi | |
| echo "✅ CI passed for PR #$PR_NUMBER — enabling auto-merge..." | |
| MERGE_OUTPUT=$(gh pr merge --auto --squash "$PR_URL" 2>&1) && \ | |
| echo "✅ Auto-merge enabled for PR #$PR_NUMBER." || \ | |
| { | |
| if echo "$MERGE_OUTPUT" | grep -q "clean status"; then | |
| echo "Branch protection has no required checks — merging directly..." | |
| gh pr merge --squash --delete-branch "$PR_URL" | |
| echo "✅ PR #$PR_NUMBER merged directly." | |
| else | |
| echo "::error::Failed to merge PR #$PR_NUMBER: $MERGE_OUTPUT" | |
| exit 1 | |
| fi | |
| } | |
| done | |
| # ── pull_request / workflow_dispatch: single or multiple PRs ────── | |
| - name: Resolve PR metadata | |
| id: resolve | |
| if: github.event_name != 'workflow_run' | |
| env: | |
| GH_TOKEN: ${{ secrets.GH_PAT_WORKFLOW }} | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| # Loop over comma-separated PR numbers, handling each fully inline. | |
| IFS=',' read -ra PR_LIST <<< "${{ inputs.pr_number }}" | |
| for PR_NUMBER in "${PR_LIST[@]}"; do | |
| PR_NUMBER=$(echo "$PR_NUMBER" | tr -d ' ') | |
| echo "" | |
| echo "════════════════════════════════════════" | |
| echo "Processing PR #$PR_NUMBER..." | |
| PR_DATA=$(gh api "/repos/${{ github.repository }}/pulls/$PR_NUMBER") | |
| PR_URL=$(echo "$PR_DATA" | jq -r '.html_url') | |
| PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.user.login') | |
| PR_BRANCH=$(echo "$PR_DATA" | jq -r '.head.ref') | |
| HAS_LABEL=$(echo "$PR_DATA" | jq '[.labels[].name] | contains(["ai-approved-major-update"])') | |
| if [ "$PR_AUTHOR" != "dependabot[bot]" ]; then | |
| echo "::error::PR #$PR_NUMBER author is '$PR_AUTHOR', not dependabot[bot] — skipping." | |
| continue | |
| fi | |
| if [ "$HAS_LABEL" != "true" ]; then | |
| echo "::error::PR #$PR_NUMBER does not have the 'ai-approved-major-update' label — skipping." | |
| continue | |
| fi | |
| echo "Checking CI for branch: $PR_BRANCH" | |
| ENCODED_BRANCH=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))" "$PR_BRANCH") | |
| ALL_RUNS=$(gh api "/repos/${{ github.repository }}/actions/runs?branch=$ENCODED_BRANCH&per_page=100" \ | |
| --jq '[.workflow_runs[] | select(.name != "Dependabot major-version auto-merge")]') | |
| TOTAL=$(echo "$ALL_RUNS" | jq 'length') | |
| if [ "$TOTAL" -eq 0 ]; then | |
| echo "::warning::PR #$PR_NUMBER has no CI workflow runs — skipping." | |
| continue | |
| fi | |
| LATEST_RUNS=$(echo "$ALL_RUNS" | jq '[group_by(.name)[] | sort_by(.run_number) | last]') | |
| PENDING=$(echo "$LATEST_RUNS" | jq '[.[] | select(.status != "completed")] | length') | |
| FAILED=$(echo "$LATEST_RUNS" | jq '[.[] | select(.status == "completed" and .conclusion != "success" and .conclusion != "skipped")] | length') | |
| if [ "$PENDING" -gt 0 ]; then | |
| echo "::warning::PR #$PR_NUMBER has $PENDING pending CI run(s) — skipping." | |
| continue | |
| fi | |
| if [ "$FAILED" -gt 0 ]; then | |
| echo "::warning::PR #$PR_NUMBER has $FAILED failed CI run(s) — skipping." | |
| echo "$LATEST_RUNS" | jq '[.[] | select(.conclusion != "success" and .conclusion != "skipped")] | .[] | {name, conclusion}' | |
| continue | |
| fi | |
| echo "✅ CI passed for PR #$PR_NUMBER — merging..." | |
| MERGE_OUTPUT=$(gh pr merge --auto --squash "$PR_URL" 2>&1) && \ | |
| echo "✅ Auto-merge enabled for PR #$PR_NUMBER." || \ | |
| { | |
| if echo "$MERGE_OUTPUT" | grep -q "clean status"; then | |
| echo "Branch protection has no required checks — merging directly..." | |
| gh pr merge --squash --delete-branch "$PR_URL" | |
| echo "✅ PR #$PR_NUMBER merged directly." | |
| else | |
| echo "::error::Failed to merge PR #$PR_NUMBER: $MERGE_OUTPUT" | |
| exit 1 | |
| fi | |
| } | |
| done | |
| else | |
| PR_URL="${{ github.event.pull_request.html_url }}" | |
| PR_BRANCH="${{ github.event.pull_request.head.ref }}" | |
| echo "pr_url=$PR_URL" >> "$GITHUB_OUTPUT" | |
| echo "pr_branch=$PR_BRANCH" >> "$GITHUB_OUTPUT" | |
| fi | |
| # ── 1. Fetch dependabot PR metadata (label trigger only) ──────── | |
| - name: Dependabot metadata | |
| id: metadata | |
| if: github.event_name == 'pull_request' | |
| uses: dependabot/fetch-metadata@v2 | |
| with: | |
| github-token: "${{ secrets.GITHUB_TOKEN }}" | |
| # ── 2. Verify semver-major or multi-package update type ───────── | |
| - name: Verify update type | |
| if: github.event_name == 'pull_request' | |
| run: | | |
| UPDATE_TYPE="${{ steps.metadata.outputs.update-type }}" | |
| if [ "$UPDATE_TYPE" != "version-update:semver-major" ] && [ -n "$UPDATE_TYPE" ]; then | |
| echo "::error::PR update-type is '$UPDATE_TYPE' (not semver-major or multi-package) — refusing to merge." | |
| exit 1 | |
| fi | |
| echo "✅ Update type accepted: '${UPDATE_TYPE:-multi-package/unknown}'" | |
| # ── 3. Verify CI passed (latest workflow run per workflow) ─────── | |
| - name: Verify all CI passed | |
| if: github.event_name == 'pull_request' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PR_BRANCH: ${{ steps.resolve.outputs.pr_branch }} | |
| run: | | |
| ENCODED_BRANCH=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))" "$PR_BRANCH") | |
| echo "Fetching workflow runs for branch: $PR_BRANCH" | |
| ALL_RUNS=$(gh api \ | |
| "/repos/${{ github.repository }}/actions/runs?branch=$ENCODED_BRANCH&per_page=100" \ | |
| --jq '[.workflow_runs[] | select(.name != "Dependabot major-version auto-merge")]') | |
| TOTAL=$(echo "$ALL_RUNS" | jq 'length') | |
| echo "Found $TOTAL workflow run(s) for branch." | |
| if [ "$TOTAL" -eq 0 ]; then | |
| echo "::error::No CI workflow runs found for branch '$PR_BRANCH' — refusing to merge." | |
| exit 1 | |
| fi | |
| LATEST_RUNS=$(echo "$ALL_RUNS" | jq '[group_by(.name)[] | sort_by(.run_number) | last]') | |
| PENDING=$(echo "$LATEST_RUNS" | jq '[.[] | select(.status != "completed")] | length') | |
| FAILED=$(echo "$LATEST_RUNS" | jq '[.[] | select(.status == "completed" and .conclusion != "success" and .conclusion != "skipped")] | length') | |
| if [ "$PENDING" -gt 0 ]; then | |
| echo "::error::$PENDING CI run(s) still pending — refusing to merge." | |
| exit 1 | |
| fi | |
| if [ "$FAILED" -gt 0 ]; then | |
| echo "::error::$FAILED CI run(s) failed — refusing to merge." | |
| echo "$LATEST_RUNS" | jq '[.[] | select(.conclusion != "success" and .conclusion != "skipped")] | .[] | {name, conclusion}' | |
| exit 1 | |
| fi | |
| echo "✅ All CI runs passed." | |
| # ── 4. Enable auto-merge ─────────────────────────────────────── | |
| - name: Enable auto-merge | |
| if: github.event_name == 'pull_request' | |
| env: | |
| PR_URL: ${{ steps.resolve.outputs.pr_url }} | |
| GH_TOKEN: ${{ secrets.GH_PAT_WORKFLOW }} | |
| run: | | |
| MERGE_OUTPUT=$(gh pr merge --auto --squash "$PR_URL" 2>&1) && \ | |
| echo "✅ Auto-merge enabled." || \ | |
| { | |
| if echo "$MERGE_OUTPUT" | grep -q "clean status"; then | |
| echo "Branch protection has no required checks — merging directly..." | |
| gh pr merge --squash --delete-branch "$PR_URL" | |
| echo "✅ PR merged directly." | |
| else | |
| echo "::error::Failed to merge PR: $MERGE_OUTPUT" | |
| exit 1 | |
| fi | |
| } |