Skip to content

chore(deps): bump picomatch in /src/microsoft-learn-mock #188

chore(deps): bump picomatch in /src/microsoft-learn-mock

chore(deps): bump picomatch in /src/microsoft-learn-mock #188

# 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
}