All Extensions fails to install - End of central directory record signature not found #2118
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: API Proposal Version Check | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| - 'release/*' | |
| paths: | |
| - 'src/vscode-dts/vscode.proposed.*.d.ts' | |
| issue_comment: | |
| types: [created] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| actions: write | |
| concurrency: | |
| group: api-proposal-${{ github.event.pull_request.number || github.event.issue.number }} | |
| cancel-in-progress: true | |
| jobs: | |
| check-version-changes: | |
| name: Check API Proposal Version Changes | |
| # Run on PR events, or on issue_comment if it's on a PR and contains the override command | |
| if: | | |
| github.event_name == 'pull_request' || | |
| (github.event_name == 'issue_comment' && | |
| github.event.issue.pull_request && | |
| contains(github.event.comment.body, '/api-proposal-change-required') && | |
| (github.event.comment.author_association == 'OWNER' || | |
| github.event.comment.author_association == 'MEMBER' || | |
| github.event.comment.author_association == 'COLLABORATOR')) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Get PR info | |
| id: pr_info | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| let prNumber, headSha, baseSha; | |
| if (context.eventName === 'pull_request') { | |
| prNumber = context.payload.pull_request.number; | |
| headSha = context.payload.pull_request.head.sha; | |
| baseSha = context.payload.pull_request.base.sha; | |
| } else { | |
| // issue_comment event - need to fetch PR details | |
| prNumber = context.payload.issue.number; | |
| const { data: pr } = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: prNumber | |
| }); | |
| headSha = pr.head.sha; | |
| baseSha = pr.base.sha; | |
| } | |
| core.setOutput('number', prNumber); | |
| core.setOutput('head_sha', headSha); | |
| core.setOutput('base_sha', baseSha); | |
| - name: Check for override comment | |
| id: check_override | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| const prNumber = ${{ steps.pr_info.outputs.number }}; | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber | |
| }); | |
| // Only accept overrides from trusted users (repo members/collaborators) | |
| const trustedAssociations = ['OWNER', 'MEMBER', 'COLLABORATOR']; | |
| let overrideComment = null; | |
| const untrustedOverrides = []; | |
| comments.forEach((comment, index) => { | |
| const hasOverrideText = comment.body.includes('/api-proposal-change-required'); | |
| const isTrusted = trustedAssociations.includes(comment.author_association); | |
| console.log(`Comment ${index + 1}:`); | |
| console.log(` Author: ${comment.user.login}`); | |
| console.log(` Author association: ${comment.author_association}`); | |
| console.log(` Created at: ${comment.created_at}`); | |
| console.log(` Contains override command: ${hasOverrideText}`); | |
| console.log(` Author is trusted: ${isTrusted}`); | |
| console.log(` Would be valid override: ${hasOverrideText && isTrusted}`); | |
| if (hasOverrideText) { | |
| if (isTrusted && !overrideComment) { | |
| overrideComment = comment; | |
| } else if (!isTrusted) { | |
| untrustedOverrides.push(comment); | |
| } | |
| } | |
| }); | |
| if (overrideComment) { | |
| console.log(`✅ Override comment FOUND`); | |
| console.log(` Comment ID: ${overrideComment.id}`); | |
| console.log(` Author: ${overrideComment.user.login}`); | |
| console.log(` Association: ${overrideComment.author_association}`); | |
| console.log(` Created at: ${overrideComment.created_at}`); | |
| core.setOutput('override_found', 'true'); | |
| core.setOutput('override_user', overrideComment.user.login); | |
| } else { | |
| if (untrustedOverrides.length > 0) { | |
| console.log(`⚠️ Found ${untrustedOverrides.length} override comment(s) from UNTRUSTED user(s):`); | |
| untrustedOverrides.forEach((comment, index) => { | |
| console.log(` Untrusted override ${index + 1}:`); | |
| console.log(` Author: ${comment.user.login}`); | |
| console.log(` Association: ${comment.author_association}`); | |
| console.log(` Created at: ${comment.created_at}`); | |
| console.log(` Comment ID: ${comment.id}`); | |
| }); | |
| console.log(` Trusted associations are: ${trustedAssociations.join(', ')}`); | |
| } | |
| console.log('❌ No valid override comment found'); | |
| core.setOutput('override_found', 'false'); | |
| } | |
| # If triggered by the override comment, re-run the failed workflow to update its status | |
| # Only allow trusted users to trigger re-runs to prevent spam | |
| - name: Re-run failed workflow on override | |
| if: | | |
| steps.check_override.outputs.override_found == 'true' && | |
| github.event_name == 'issue_comment' && | |
| (github.event.comment.author_association == 'OWNER' || | |
| github.event.comment.author_association == 'MEMBER' || | |
| github.event.comment.author_association == 'COLLABORATOR') | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| const headSha = '${{ steps.pr_info.outputs.head_sha }}'; | |
| console.log(`Override comment found by ${{ steps.check_override.outputs.override_user }}`); | |
| console.log('API proposal version change has been acknowledged.'); | |
| // Find the failed workflow run for this PR's head SHA | |
| const { data: runs } = await github.rest.actions.listWorkflowRuns({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| workflow_id: 'api-proposal-version-check.yml', | |
| head_sha: headSha, | |
| status: 'completed', | |
| per_page: 10 | |
| }); | |
| // Find the most recent failed run | |
| const failedRun = runs.workflow_runs.find(run => | |
| run.conclusion === 'failure' && run.event === 'pull_request' | |
| ); | |
| if (failedRun) { | |
| console.log(`Re-running failed workflow run ${failedRun.id}`); | |
| await github.rest.actions.reRunWorkflow({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: failedRun.id | |
| }); | |
| console.log('Workflow re-run triggered successfully'); | |
| } else { | |
| console.log('No failed pull_request workflow run found to re-run'); | |
| // The check will pass on this run since override exists | |
| } | |
| - name: Pass on override comment | |
| if: steps.check_override.outputs.override_found == 'true' | |
| run: | | |
| echo "Override comment found by ${{ steps.check_override.outputs.override_user }}" | |
| echo "API proposal version change has been acknowledged." | |
| # Only continue checking if no override found | |
| - name: Checkout repository | |
| if: steps.check_override.outputs.override_found != 'true' | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check for version changes | |
| if: steps.check_override.outputs.override_found != 'true' | |
| id: version_check | |
| env: | |
| HEAD_SHA: ${{ steps.pr_info.outputs.head_sha }} | |
| BASE_SHA: ${{ steps.pr_info.outputs.base_sha }} | |
| run: | | |
| set -e | |
| # Use merge-base to get accurate diff of what the PR actually changes | |
| MERGE_BASE=$(git merge-base "$BASE_SHA" "$HEAD_SHA") | |
| echo "Merge base: $MERGE_BASE" | |
| # Get the list of changed proposed API files (diff against merge-base) | |
| CHANGED_FILES=$(git diff --name-only "$MERGE_BASE" "$HEAD_SHA" -- 'src/vscode-dts/vscode.proposed.*.d.ts' || true) | |
| if [ -z "$CHANGED_FILES" ]; then | |
| echo "No proposed API files changed" | |
| echo "version_changed=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| echo "Changed proposed API files:" | |
| echo "$CHANGED_FILES" | |
| VERSION_CHANGED="false" | |
| CHANGED_LIST="" | |
| for FILE in $CHANGED_FILES; do | |
| # Check if file exists in head | |
| if ! git cat-file -e "$HEAD_SHA:$FILE" 2>/dev/null; then | |
| echo "File $FILE was deleted, skipping version check" | |
| continue | |
| fi | |
| # Get version from head (current PR) | |
| HEAD_VERSION=$(git show "$HEAD_SHA:$FILE" | grep -E '^// version: [0-9]+' | sed 's/.*version: //' || echo "") | |
| # Get version from merge-base (what the PR is based on) | |
| BASE_VERSION=$(git show "$MERGE_BASE:$FILE" 2>/dev/null | grep -E '^// version: [0-9]+' | sed 's/.*version: //' || echo "") | |
| echo "File: $FILE" | |
| echo " Base version: ${BASE_VERSION:-'(none)'}" | |
| echo " Head version: ${HEAD_VERSION:-'(none)'}" | |
| # Check if version was added or changed | |
| if [ -n "$HEAD_VERSION" ] && [ "$HEAD_VERSION" != "$BASE_VERSION" ]; then | |
| echo " -> Version changed!" | |
| VERSION_CHANGED="true" | |
| FILENAME=$(basename "$FILE") | |
| if [ -n "$CHANGED_LIST" ]; then | |
| CHANGED_LIST="$CHANGED_LIST, $FILENAME" | |
| else | |
| CHANGED_LIST="$FILENAME" | |
| fi | |
| fi | |
| done | |
| echo "version_changed=$VERSION_CHANGED" >> $GITHUB_OUTPUT | |
| echo "changed_files=$CHANGED_LIST" >> $GITHUB_OUTPUT | |
| - name: Post warning comment | |
| if: steps.check_override.outputs.override_found != 'true' && steps.version_check.outputs.version_changed == 'true' | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| const prNumber = ${{ steps.pr_info.outputs.number }}; | |
| const changedFiles = '${{ steps.version_check.outputs.changed_files }}'; | |
| // Check if we already posted a warning comment | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber | |
| }); | |
| const marker = '<!-- api-proposal-version-warning -->'; | |
| const existingComment = comments.find(comment => | |
| comment.body.includes(marker) | |
| ); | |
| const body = `${marker} | |
| ## ⚠️ API Proposal Version Change Detected | |
| The following proposed API files have version changes: **${changedFiles}** | |
| API proposal version changes should only be used when maintaining compatibility is not possible. Consider keeping the version as is and maintaining backward compatibility. | |
| **Any version changes must be adopted by the consuming extensions before the next insiders for the extension to work.** | |
| --- | |
| If the version change is required, comment \`/api-proposal-change-required\` to unblock this check and acknowledge that you will update any critical consuming extensions (Copilot Chat).`; | |
| if (existingComment) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existingComment.id, | |
| body: body | |
| }); | |
| console.log('Updated existing warning comment'); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| body: body | |
| }); | |
| console.log('Posted new warning comment'); | |
| } | |
| - name: Fail if version changed without override | |
| if: steps.check_override.outputs.override_found != 'true' && steps.version_check.outputs.version_changed == 'true' | |
| run: | | |
| echo "::error::API proposal version changed in: ${{ steps.version_check.outputs.changed_files }}" | |
| echo "To unblock, comment '/api-proposal-change-required' on the PR." | |
| exit 1 |