diff --git a/.github/workflows/format-auto-fix.yml b/.github/workflows/format-auto-fix.yml new file mode 100644 index 000000000000..e6f992b3d762 --- /dev/null +++ b/.github/workflows/format-auto-fix.yml @@ -0,0 +1,191 @@ +name: SDK Format Auto-Fix + +on: + workflow_dispatch: + inputs: + pr_number: + description: PR number to run format fix on + required: true + type: string + +jobs: + fix-format: + runs-on: ubuntu-latest + # One run per PR at a time; queued runs wait rather than being cancelled. + concurrency: + group: format-auto-fix-${{ github.event.inputs.pr_number }} + cancel-in-progress: false + permissions: + contents: write + pull-requests: write + steps: + - name: Fetch and validate PR + id: find-pr + uses: actions/github-script@v9 + with: + script: | + const prNumberStr = context.payload.inputs?.pr_number; + if (!prNumberStr) { + core.warning('No pr_number input provided'); + core.setOutput('has-pr', 'false'); + return; + } + const prNumber = parseInt(prNumberStr); + core.info(`Got PR #${prNumber} from workflow_dispatch input`); + const { data: pr } = await github.rest.pulls.get({ + ...context.repo, + pull_number: prNumber, + }); + if (pr.state !== 'open') { + core.info(`PR #${prNumber} is not open — skipping`); + core.setOutput('has-pr', 'false'); + core.setOutput('is-fork', 'false'); + return; + } + // Security: only auto-fix branches in the base repository. + // Checking out a fork and running its package.json scripts with a + // write token would allow an attacker to execute arbitrary code. + const baseRepo = `${context.repo.owner}/${context.repo.repo}`; + if (pr.head.repo.full_name !== baseRepo) { + core.warning(`PR #${prNumber} is from fork ${pr.head.repo.full_name} — skipping auto-fix`); + core.setOutput('has-pr', 'false'); + core.setOutput('is-fork', 'true'); + return; + } + core.info(`Processing PR #${pr.number} on branch ${pr.head.ref}`); + core.setOutput('has-pr', 'true'); + core.setOutput('is-fork', 'false'); + core.setOutput('pr-branch', pr.head.ref); + core.setOutput('pr-repo', pr.head.repo.full_name); + + - name: Notify fork PR author + if: steps.find-pr.outputs.is-fork == 'true' + uses: actions/github-script@v9 + with: + script: | + await github.rest.issues.createComment({ + ...context.repo, + issue_number: parseInt(context.payload.inputs.pr_number), + body: [ + '⚠️ **Format auto-fix skipped for fork PR.**', + '', + 'The auto-fix workflow does not run on pull requests from forks to avoid', + 'executing untrusted code with repository write access.', + '', + 'Please run `pnpm format` locally in the affected package(s) and push the', + 'formatting changes to your branch.', + ].join('\n'), + }); + + - name: Checkout PR branch + if: steps.find-pr.outputs.has-pr == 'true' + uses: actions/checkout@v4 + with: + ref: ${{ steps.find-pr.outputs.pr-branch }} + repository: ${{ steps.find-pr.outputs.pr-repo }} + fetch-depth: 0 + + - name: Setup Node.js + if: steps.find-pr.outputs.has-pr == 'true' + uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + + - name: Setup pnpm + if: steps.find-pr.outputs.has-pr == 'true' + uses: pnpm/action-setup@v4 + with: + version: '10.33.0' + + - name: Find affected packages and run pnpm format + if: steps.find-pr.outputs.has-pr == 'true' + run: | + UPSTREAM_REPO_URL="${{ github.server_url }}/${{ github.repository }}.git" + UPSTREAM_DEFAULT_BRANCH="${{ github.event.repository.default_branch }}" + + # `origin` points at the PR head repository after checkout, which may be a fork. + # Fetch the workflow repository's default branch explicitly and diff against that. + if git remote get-url upstream >/dev/null 2>&1; then + git remote set-url upstream "$UPSTREAM_REPO_URL" + else + git remote add upstream "$UPSTREAM_REPO_URL" + fi + git fetch --no-tags upstream "${UPSTREAM_DEFAULT_BRANCH}:refs/remotes/upstream/${UPSTREAM_DEFAULT_BRANCH}" + + # Find unique package directories (sdk//) from changed files + PKGS=$(git diff --name-only "upstream/${UPSTREAM_DEFAULT_BRANCH}...HEAD" \ + | grep '^sdk/' \ + | sed 's|\(sdk/[^/]*/[^/]*\)/.*|\1|' \ + | sort -u) + + echo "Affected packages:" + echo "$PKGS" + + if [ -z "$PKGS" ]; then + echo "No sdk/ packages changed, nothing to format" + exit 0 + fi + + # Build pnpm/turbo filter args (e.g. --filter=@azure/arm-foo...) + FILTERS="" + for pkg in $PKGS; do + if [ -f "$pkg/package.json" ]; then + PKG_NAME=$(node -p "require('./$pkg/package.json').name") + FILTERS="$FILTERS --filter=${PKG_NAME}" + fi + done + + # Install only the affected packages and their dependencies. + # --ignore-scripts prevents pre/postinstall lifecycle scripts from running. + # Frozen lockfile is the default in CI; if the lockfile is out of sync the + # PR author must fix it — this workflow only fixes formatting. + pnpm install $FILTERS --ignore-scripts + + # Run format across all affected packages in one turbo invocation + pnpm turbo run format $FILTERS + + - name: Commit and push formatting changes + if: steps.find-pr.outputs.has-pr == 'true' + id: commit + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + if git diff --quiet; then + echo "No formatting changes needed" + echo "CHANGES=false" >> $GITHUB_OUTPUT + else + git diff --name-only \ + | grep -E '\.(ts|mts|cts|tsx|js|mjs|cjs|jsx|json|md|yaml|yml)$' \ + | grep -v '^pnpm-lock\.yaml$' \ + | xargs -r git add -- + + if git diff --cached --quiet; then + echo "Formatting changes were detected, but none matched the commit allow-list" + echo "CHANGES=false" >> $GITHUB_OUTPUT + else + git commit -m "chore: apply prettier format fixes" + git push + echo "CHANGES=true" >> $GITHUB_OUTPUT + fi + fi + + - name: Post result comment + if: steps.find-pr.outputs.has-pr == 'true' + uses: actions/github-script@v9 + env: + CHANGES: ${{ steps.commit.outputs.CHANGES }} + with: + script: | + const prNumber = parseInt(context.payload.inputs.pr_number); + + if (process.env.CHANGES === 'true') { + await github.rest.issues.createComment({ + ...context.repo, + issue_number: prNumber, + body: '✅ Automatically ran `pnpm format` and pushed the formatting fixes. Check-format should now pass.' + }); + core.info(`Posted success comment on PR #${prNumber}`); + } else { + core.info(`No formatting changes were needed for PR #${prNumber}`); + } diff --git a/.github/workflows/mgmt-review.lock.yml b/.github/workflows/mgmt-review.lock.yml index 391b740bda70..2d47a0ebe278 100644 --- a/.github/workflows/mgmt-review.lock.yml +++ b/.github/workflows/mgmt-review.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"09d7f11d0254f657100889077bd5f3bc611661f6c48f15cc08be84e8430b1935","compiler_version":"v0.72.1","agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"e1397a104be4309da2ca828208d4bfbbf3496e2b0cbddd4525c978d03a78ed61","compiler_version":"v0.72.1","agent_id":"copilot"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9"},{"repo":"actions/github-script","sha":"ed597411d8f924073f98dfc5c65a23a2325f34cd","version":"v8"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"bc56a0cad2f450c562810785ef38649c04db812a","version":"v0.72.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.41"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.41"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -243,22 +243,22 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_40a18c8b9e7331b3_EOF' + cat << 'GH_AW_PROMPT_1afa52324c87869c_EOF' - GH_AW_PROMPT_40a18c8b9e7331b3_EOF + GH_AW_PROMPT_1afa52324c87869c_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/cache_memory_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/repo_memory_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_40a18c8b9e7331b3_EOF' + cat << 'GH_AW_PROMPT_1afa52324c87869c_EOF' - Tools: add_comment, create_pull_request_review_comment(max:10), submit_pull_request_review, add_labels, remove_labels, missing_tool, missing_data, noop + Tools: add_comment, create_pull_request_review_comment(max:10), submit_pull_request_review, add_labels, remove_labels, dispatch_workflow, missing_tool, missing_data, noop - GH_AW_PROMPT_40a18c8b9e7331b3_EOF + GH_AW_PROMPT_1afa52324c87869c_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_40a18c8b9e7331b3_EOF' + cat << 'GH_AW_PROMPT_1afa52324c87869c_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -287,12 +287,12 @@ jobs: {{/if}} - GH_AW_PROMPT_40a18c8b9e7331b3_EOF + GH_AW_PROMPT_1afa52324c87869c_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_40a18c8b9e7331b3_EOF' + cat << 'GH_AW_PROMPT_1afa52324c87869c_EOF' {{#runtime-import .github/workflows/mgmt-review.md}} - GH_AW_PROMPT_40a18c8b9e7331b3_EOF + GH_AW_PROMPT_1afa52324c87869c_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -519,9 +519,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_a11439a1683e99a4_EOF' - {"add_comment":{"footer":false,"hide_older_comments":true,"max":1,"target":"${{ github.event.pull_request.number || github.event.issue.number }}"},"add_labels":{"max":1,"target":"${{ github.event.pull_request.number || github.event.issue.number }}"},"create_pull_request_review_comment":{"max":10,"side":"RIGHT","target":"${{ github.event.pull_request.number || github.event.issue.number }}"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":10240,"max_patch_size":10240}]},"remove_labels":{"max":1,"target":"${{ github.event.pull_request.number || github.event.issue.number }}"},"report_incomplete":{},"submit_pull_request_review":{"footer":"if-body","max":1,"target":"${{ github.event.pull_request.number || github.event.issue.number }}"}} - GH_AW_SAFE_OUTPUTS_CONFIG_a11439a1683e99a4_EOF + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_c4d2bac8c024f04c_EOF' + {"add_comment":{"footer":false,"hide_older_comments":true,"max":1,"target":"${{ github.event.pull_request.number || github.event.issue.number }}"},"add_labels":{"max":1,"target":"${{ github.event.pull_request.number || github.event.issue.number }}"},"create_pull_request_review_comment":{"max":10,"side":"RIGHT","target":"${{ github.event.pull_request.number || github.event.issue.number }}"},"create_report_incomplete_issue":{},"dispatch_workflow":{"max":1,"workflow_files":{"format-auto-fix":".yml"},"workflows":["format-auto-fix"]},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":10240,"max_patch_size":10240}]},"remove_labels":{"max":1,"target":"${{ github.event.pull_request.number || github.event.issue.number }}"},"report_incomplete":{},"submit_pull_request_review":{"footer":"if-body","max":1,"target":"${{ github.event.pull_request.number || github.event.issue.number }}"}} + GH_AW_SAFE_OUTPUTS_CONFIG_c4d2bac8c024f04c_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -534,7 +534,26 @@ jobs: "submit_pull_request_review": " CONSTRAINTS: Maximum 1 review(s) can be submitted." }, "repo_params": {}, - "dynamic_tools": [] + "dynamic_tools": [ + { + "_workflow_name": "format-auto-fix", + "description": "Dispatch the 'format-auto-fix' workflow with workflow_dispatch trigger. This workflow must support workflow_dispatch and be in .github/workflows/ directory in the same repository.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "pr_number": { + "description": "PR number to run format fix on", + "type": "string" + } + }, + "required": [ + "pr_number" + ], + "type": "object" + }, + "name": "format_auto_fix" + } + ] } GH_AW_VALIDATION_JSON: | { @@ -807,7 +826,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_76aced737ddda585_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_b177f1edecb94cb7_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -848,7 +867,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_76aced737ddda585_EOF + GH_AW_MCP_CONFIG_b177f1edecb94cb7_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1094,6 +1113,7 @@ jobs: needs.activation.outputs.stale_lock_file_failed == 'true') runs-on: ubuntu-slim permissions: + actions: write contents: read discussions: write issues: write @@ -1569,6 +1589,7 @@ jobs: if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' runs-on: ubuntu-slim permissions: + actions: write contents: read discussions: write issues: write @@ -1637,7 +1658,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deno.land,dev.azure.com,esm.sh,get.pnpm.io,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"footer\":false,\"hide_older_comments\":true,\"max\":1,\"target\":\"${{ github.event.pull_request.number || github.event.issue.number }}\"},\"add_labels\":{\"max\":1,\"target\":\"${{ github.event.pull_request.number || github.event.issue.number }}\"},\"create_pull_request_review_comment\":{\"max\":10,\"side\":\"RIGHT\",\"target\":\"${{ github.event.pull_request.number || github.event.issue.number }}\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"remove_labels\":{\"max\":1,\"target\":\"${{ github.event.pull_request.number || github.event.issue.number }}\"},\"report_incomplete\":{},\"submit_pull_request_review\":{\"footer\":\"if-body\",\"max\":1,\"target\":\"${{ github.event.pull_request.number || github.event.issue.number }}\"}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"footer\":false,\"hide_older_comments\":true,\"max\":1,\"target\":\"${{ github.event.pull_request.number || github.event.issue.number }}\"},\"add_labels\":{\"max\":1,\"target\":\"${{ github.event.pull_request.number || github.event.issue.number }}\"},\"create_pull_request_review_comment\":{\"max\":10,\"side\":\"RIGHT\",\"target\":\"${{ github.event.pull_request.number || github.event.issue.number }}\"},\"create_report_incomplete_issue\":{},\"dispatch_workflow\":{\"max\":1,\"workflow_files\":{\"format-auto-fix\":\".yml\"},\"workflows\":[\"format-auto-fix\"]},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"remove_labels\":{\"max\":1,\"target\":\"${{ github.event.pull_request.number || github.event.issue.number }}\"},\"report_incomplete\":{},\"submit_pull_request_review\":{\"footer\":\"if-body\",\"max\":1,\"target\":\"${{ github.event.pull_request.number || github.event.issue.number }}\"}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/mgmt-review.md b/.github/workflows/mgmt-review.md index 9fef599b2223..d85933b3b164 100644 --- a/.github/workflows/mgmt-review.md +++ b/.github/workflows/mgmt-review.md @@ -82,6 +82,8 @@ safe-outputs: remove-labels: max: 1 target: "${{ github.event.pull_request.number || github.event.issue.number }}" + dispatch-workflow: + - format-auto-fix messages: footer: "> ⚡ *Benchmarked by [{workflow_name}]({run_url})*" run-started: "⚡ [{workflow_name}]({run_url}) is profiling this PR for guidance and review..." @@ -210,7 +212,7 @@ Store a brief summary in `cache-memory` (PR number, package, outcome) so future 2. All CI check runs — list every check with `conclusion: failure` or `state: error` 3. ADO pipeline results — check `state`/`conclusion` fields; for failures, fetch ADO logs via the REST API to get specific error details - For checks still `pending` or `in_progress`, note them as "⏳ still running" — do NOT skip them. -- **Important**: Record each failure in a structured list before moving to Step 3. This list will be used to compose the final PR comment. +- **Important**: Record each failure in a structured list before moving to Step 3. This list will be used for both auto-fix attempts and the final comment. #### CI Check Name → Failure Mapping @@ -233,7 +235,7 @@ These are exact strings/patterns to search for in CI logs and PR status. They ar | `UnitTest FAILED` request url mismatch | Stale test recordings | You need to record new recordings per [test guide](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/Quickstart-on-how-to-write-tests.md#run-tests-in-record-mode). Or you could simply skip tests with maintainer approval. | No | | `UnitTest FAILED` missing browser recordings | Missing browser recordings | You need to record browser recordings per [test guide](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/Quickstart-on-how-to-write-tests.md#run-tests-in-record-mode). | No | | `Build FAILED` | Compilation failure | Fix compile errors | No | -| `Check-format FAILED` | Code not formatted | Run `cd && pnpm format` locally and push the result | No | +| `Check-format FAILED` | Code not formatted | Dispatch the `format-auto-fix` workflow to apply the formatting fix | dispatch-workflow | | `verify-links` broken URL | Broken markdown links | Add URL to `eng/ignore-links.txt` | No | | PR `Merging is blocking` pnpm-lock conflict | pnpm-lock.yaml conflict | Bot regenerates `pnpm-lock.yaml` and pushes the fix to the PR branch; if auto-fix fails, follow the [conflict guide](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/resolve-pnpm-lock-merge-conflict.md) | No | | `ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY` Broken lockfile | pnpm-lock.yaml conflict | Bot regenerates `pnpm-lock.yaml` and pushes the fix to the PR branch; if auto-fix fails, follow the [conflict guide](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/resolve-pnpm-lock-merge-conflict.md) | NO | @@ -244,7 +246,13 @@ Besides above cases also: - Check [CI troubleshooting](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/Troubleshoot-ci-failure.md) for other failures - Provide general guidance if merging conflict exists -### Step 3. Post a comment +### Step 3. Auto-fix failures if possible + +- If `Check-format FAILED` is detected: + 1. Dispatch the `format-auto-fix` workflow immediately via `dispatch-workflow`, passing the PR number as input `pr_number`. Use the actual PR number from context (pull_request event number or `item_number` input). +- All other failures require manual fixes by the contributor. + +### Step 4. Post a comment The comment must report **every** blocking item from your Step 2 list — not just the ones you attempted to auto-fix. This is the most important step. @@ -256,17 +264,14 @@ Compose a single GitHub PR comment (not a review) with: - **Header**: `## Next Steps to Merge` - **Message**: `Only failed checks and required actions are listed below:` - Include **all** currently failing/blocking checks from your Step 2 list: - - Not fixed: `- ❌ : . Action: . Review [ADO logs]().` - - For `Check-format FAILED` specifically, use this format: - ``` - - ❌ Check-format: code not formatted. Action: Run the following command locally, then commit and push the result: `cd && pnpm format`. Review [ADO logs](). - ``` + - Format auto-fix triggered: `- 🔧 : code not formatted — auto-fix workflow dispatched, will apply \`pnpm format\` and push.` + - Not auto-fixed: `- ❌ : . Action: . Review [ADO logs]().` - pnpm-lock conflict (manual): `- 🔄 pnpm-lock conflict: . Follow the [conflict guide](...).` - Still running: `- ⏳ : still running.` - **Note:** Always include the real ADO `target_url` link; never use placeholder URLs. - Keep concise (target <= 15 lines). If nothing blocks: `## PR is ready to merge`. -Post via `add-comment` exactly once. Use `hide-older-comments: true` to avoid duplicates. Include marker `` in the body. +Post via `add_comment` exactly once. Use `hide-older-comments: true` to avoid duplicates. Include marker `` in the body. ### Required Output Template @@ -277,6 +282,7 @@ Use this exact shape and keep it short. The comment MUST include ALL blocking it Only failed checks and required actions are listed below. - ❌ : . Action: . Review [ADO logs](). +- 🔧 : code not formatted — auto-fix workflow dispatched, will apply `pnpm format` and push. - 🔄 pnpm-lock conflict: merge conflict in pnpm-lock.yaml. Follow the [conflict guide](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/resolve-pnpm-lock-merge-conflict.md) to fix this issue. - ⏳ : still running. ```