From 68023b10f2c318cd468f2967f6bb286cf3ef1970 Mon Sep 17 00:00:00 2001 From: Muiris Woulfe Date: Tue, 1 Sep 2026 12:37:09 +0100 Subject: [PATCH 1/2] chore: initial commit --- .../New-GeneratedChangesPatch.ps1 | 35 +++++++++++ .github/workflows/build.yml | 58 +++++++++---------- .github/workflows/release-initiate.yml | 20 +++---- .github/workflows/release-publish.yml | 9 +-- package.json | 19 +++--- 5 files changed, 83 insertions(+), 58 deletions(-) create mode 100644 .github/workflow-scripts/New-GeneratedChangesPatch.ps1 diff --git a/.github/workflow-scripts/New-GeneratedChangesPatch.ps1 b/.github/workflow-scripts/New-GeneratedChangesPatch.ps1 new file mode 100644 index 00000000..b0baca92 --- /dev/null +++ b/.github/workflow-scripts/New-GeneratedChangesPatch.ps1 @@ -0,0 +1,35 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +$outputPath = 'generated/generated-changes.patch' + +git add -A +if ($LASTEXITCODE -ne 0) +{ + throw 'Could not stage generated changes.' +} + +git diff --cached --quiet +if ($LASTEXITCODE -eq 0) +{ + 'CHANGES_PRESENT=False' >> $Env:GITHUB_OUTPUT + return +} + +if ($LASTEXITCODE -ne 1) +{ + throw 'Could not test for generated changes.' +} + +$outputDirectory = Split-Path -Path $outputPath -Parent +$null = New-Item -ItemType Directory -Path $outputDirectory -Force +git diff --cached --binary --output=$outputPath +if ($LASTEXITCODE -ne 0) +{ + throw 'Could not create the generated changes patch.' +} + +'CHANGES_PRESENT=True' >> $Env:GITHUB_OUTPUT diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index df8732b7..d157554a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -97,7 +97,7 @@ jobs: run: npm run lint - name: npm – Build Package - run: npm run build:package + run: npm run build:package:no-install - name: Super-Linter uses: super-linter/super-linter@4ce20838b8ab83717e78138c5b3a1407148e0918 # v8.7.0 @@ -126,15 +126,7 @@ jobs: - name: Git – Create Changes Patch id: git-test-for-changes - run: |- - git add -A - $changesPresent = [bool](git diff --cached --name-only) - "CHANGES_PRESENT=$($changesPresent.ToString())" >> $Env:GITHUB_OUTPUT - if ($changesPresent) - { - New-Item -ItemType Directory -Path 'generated' | Out-Null - git diff --cached --binary --output='generated/generated-changes.patch' - } + run: .github/workflow-scripts/New-GeneratedChangesPatch.ps1 - if: github.event_name == 'pull_request' && steps.git-test-for-changes.outputs.CHANGES_PRESENT == 'True' name: Git – Upload Changes Patch @@ -178,17 +170,6 @@ jobs: - name: Git – Apply Changes Patch run: git apply --index '${{ runner.temp }}/generated-changes/generated-changes.patch' - - name: Git – Verify Pull Request Head - run: |- - $remoteHead = (git ls-remote origin "refs/heads/$env:HEAD_REF").Split()[0] - if ($remoteHead -ne $env:EXPECTED_HEAD) - { - throw "The pull request branch changed from '$env:EXPECTED_HEAD' to '$remoteHead'. Re-run the workflow before committing generated changes." - } - env: - EXPECTED_HEAD: ${{ github.event.pull_request.head.sha }} - HEAD_REF: ${{ github.head_ref }} - - name: App Token – Mint id: app-token uses: microsoft/PR-Metrics/.github/actions/mint-github-app-token@0959437740572519916a08b3609e67ffa435f8dd # v1.7.17 @@ -199,6 +180,8 @@ jobs: permissions: '{"contents":"write"}' - name: Git – Commit & Push (Signed) + # Do not move this into the checked-out pull request. This step receives a + # repository-write token and must not execute pull request code. run: |- $additions = [System.Collections.Generic.List[object]]::new() $deletions = [System.Collections.Generic.List[object]]::new() @@ -296,14 +279,11 @@ jobs: - name: npm – Lint run: npm run lint -- --no-fix - - name: npm – Clean - run: npm run clean - - name: npm – Build - run: npm run build + run: npm run build:no-install - name: npm – Test - run: npm run test + run: npm run test:no-install - name: Release – Create run: npx tfx-cli extension create --manifest-globs vss-extension.json --output-path ../ms-omex.PRMetrics.vsix @@ -345,6 +325,7 @@ jobs: permissions: contents: read pull-requests: write + statuses: write steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -360,7 +341,7 @@ jobs: GITHUB_TOKEN: ${{ github.token }} GITLEAKS_CONFIG_FILE: gitleaks.toml MARKDOWN_CONFIG_FILE: ../../.markdownlint.json - MULTI_STATUS: false + MULTI_STATUS: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} SAVE_SUPER_LINTER_SUMMARY: false SPELL_CODESPELL_CONFIG_FILE: .codespellrc # Disable linters that conflict with other linters or are not enforced. @@ -371,21 +352,18 @@ jobs: VALIDATE_PYTHON_BLACK: false VALIDATE_TYPESCRIPT_ES: false - dependabot: + dependabot-automation: if: >- github.event_name == 'pull_request' && github.event.pull_request.user.login == 'dependabot[bot]' && github.event.pull_request.head.repo.full_name == github.repository - name: Dependabot + name: Dependabot – Automation runs-on: ubuntu-latest environment: production permissions: contents: read id-token: write steps: - - name: gh – Log Version - run: gh --version - - name: App Token – Mint id: app-token uses: microsoft/PR-Metrics/.github/actions/mint-github-app-token@0959437740572519916a08b3609e67ffa435f8dd # v1.7.17 @@ -400,3 +378,19 @@ jobs: env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} PR_URL: ${{ github.event.pull_request.html_url }} + + dependabot: + if: always() + name: Dependabot + needs: dependabot-automation + runs-on: ubuntu-latest + permissions: {} + steps: + - name: Dependabot – Check Automation + run: |- + if ($env:AUTOMATION_RESULT -notin @('success', 'skipped')) + { + throw "Dependabot automation completed with result '$env:AUTOMATION_RESULT'." + } + env: + AUTOMATION_RESULT: ${{ needs.dependabot-automation.result }} diff --git a/.github/workflows/release-initiate.yml b/.github/workflows/release-initiate.yml index 62ba09e2..01afbbc2 100644 --- a/.github/workflows/release-initiate.yml +++ b/.github/workflows/release-initiate.yml @@ -5,6 +5,9 @@ name: Release – Initiate on: + pull_request: + branches: + - main workflow_dispatch: null permissions: {} @@ -42,10 +45,6 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - token: ${{ steps.app-token.outputs.token }} - - - name: gh – Log Version - run: gh --version - name: Version Number – Read run: |- @@ -71,9 +70,8 @@ jobs: - name: Git – Create Branch run: git checkout -b release/v$env:version - - name: Git – Add Changed Files - run: git add -A - + # stage-all-files replaces a separate git add. create-branch-on-remote + # pushes the new branch before the signed GraphQL commit is created. - name: Git – Commit & Push (Signed) uses: grafana/github-api-commit-action@b1d81091e8480dd11fcea8bc1f0ab977a0376ca5 # v1.0.0 with: @@ -82,6 +80,8 @@ jobs: stage-all-files: true token: ${{ steps.app-token.outputs.token }} + # The API commit updates the remote only. Sync it locally before generating + # dependency changes on top of the release-version commit. - name: Git – Sync Local with Remote run: |- git fetch origin release/v$env:version @@ -131,11 +131,5 @@ jobs: uses: grafana/github-api-commit-action@b1d81091e8480dd11fcea8bc1f0ab977a0376ca5 # v1.0.0 with: commit-message: "chore: update dependencies" - create-branch-on-remote: true stage-all-files: true token: ${{ steps.app-token.outputs.token }} - - - name: Git – Sync Local with Remote - run: |- - git fetch origin release/v$env:version - git reset --hard origin/release/v$env:version diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 62d05352..9106a8e1 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -10,6 +10,9 @@ on: - main paths: - .github/release-publish-trigger.txt + pull_request: + branches: + - main workflow_dispatch: null permissions: {} @@ -43,17 +46,11 @@ jobs: azure-subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} permissions: '{"contents":"write","discussions":"write"}' - - name: gh – Log Version - run: gh --version - - name: Install Node.js uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24.13.0 - - name: npm – Install Dependencies - run: npm ci - - name: npm – Build Release run: npm run build:release diff --git a/package.json b/package.json index dd681dd8..0c9eabcc 100644 --- a/package.json +++ b/package.json @@ -7,27 +7,32 @@ "main": "dist/index.mjs", "type": "module", "scripts": { - "build:initialization:debug": "npm ci && node scripts/fs-helpers.mjs rm debug && node scripts/fs-helpers.mjs cp src debug", - "build:initialization:release": "npm ci && node scripts/fs-helpers.mjs rm release && node scripts/fs-helpers.mjs cp src release", - "build:debug": "npm run build:initialization:debug && cd debug/task && tsc --sourceMap -p tsconfig.test.json", - "build:release": "npm run build:initialization:release && npm run build:release:compile && npm run build:release:clean && npm run build:release:package", + "build:initialization:debug": "node scripts/fs-helpers.mjs rm debug && node scripts/fs-helpers.mjs cp src debug", + "build:initialization:release": "node scripts/fs-helpers.mjs rm release && node scripts/fs-helpers.mjs cp src release", + "build:debug": "npm ci && npm run build:debug:no-install", + "build:debug:no-install": "npm run build:initialization:debug && cd debug/task && tsc --sourceMap -p tsconfig.test.json", + "build:release": "npm ci && npm run build:release:no-install", + "build:release:no-install": "npm run build:initialization:release && npm run build:release:compile && npm run build:release:clean && npm run build:release:package", "build:release:compile": "cd release/task && tsc && ncc build index.js --out . --minify", "build:release:clean": "npm run build:release:rename && npm run build:release:prune", "build:release:rename": "node scripts/fs-helpers.mjs -C release/task mv index.js index.mjs", "build:release:prune": "node scripts/fs-helpers.mjs -C release/task rm src tests typedocs index.ts package.json tsconfig.json tsconfig.test.json typedoc.json", "build:release:package": "node scripts/fs-helpers.mjs mkdir release/extension && node scripts/fs-helpers.mjs cpfile package.json release/extension/package.json", - "build:package": "npm ci && ncc build src/task/index.ts --out dist --minify && npm run build:package:clean", + "build:package": "npm ci && npm run build:package:no-install", + "build:package:no-install": "ncc build src/task/index.ts --out dist --minify && npm run build:package:clean", "build:package:clean": "npm run build:package:rename && npm run build:package:copy-resources", "build:package:rename": "node scripts/fs-helpers.mjs -C dist mv index.js index.mjs && node scripts/fs-helpers.mjs -C dist rm package.json", "build:package:copy-resources": "node scripts/fs-helpers.mjs cpfile src/task/Strings/resources.resjson/en-US/resources.resjson dist/resources.resjson", "build:docs": "cd src/task && typedoc", - "build": "npm run build:release && npm run build:package && npm run build:docs", + "build": "npm ci && npm run build:no-install", + "build:no-install": "npm run build:release:no-install && npm run build:package:no-install && npm run build:docs", "clean": "node scripts/fs-helpers.mjs rm debug release", "deploy": "npm run build:release && npm run deploy:clean && npm run deploy:upload", "deploy:clean": "(tfx build tasks delete --task-id 907d3b28-6b37-4ac7-ac75-9631ee53e512 --no-prompt || node -e \"\")", "deploy:upload": "tfx build tasks upload --task-path release/task --no-prompt", "lint": "eslint --fix **/*.ts", - "test": "npm run build:debug && cd debug/task && c8 --reporter=text --reporter=text-summary mocha tests/**/*.spec.js", + "test": "npm ci && npm run test:no-install", + "test:no-install": "npm run build:debug:no-install && cd debug/task && c8 --reporter=text --reporter=text-summary mocha tests/**/*.spec.js", "test:fast": "node scripts/fs-helpers.mjs rm debug && node scripts/fs-helpers.mjs cp src debug && cd debug/task && tsc --sourceMap -p tsconfig.test.json && c8 --reporter=text --reporter=text-summary mocha tests/**/*.spec.js", "update:dependencies": "npm update", "update:versions": "ncu -u --peer --reject @types/node" From b5c041ddc8e3bc85448fa9141be97f65c31ab19e Mon Sep 17 00:00:00 2001 From: Muiris Woulfe Date: Tue, 1 Sep 2026 13:50:04 +0100 Subject: [PATCH 2/2] Refining design --- .github/workflows/build.yml | 91 ++++++++++++++++---------- .github/workflows/release-initiate.yml | 3 - .github/workflows/release-publish.yml | 3 - 3 files changed, 57 insertions(+), 40 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d157554a..83fc8f87 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -138,39 +138,61 @@ jobs: retention-days: 1 commit-changes: - if: >- - github.event_name == 'pull_request' && - github.event.pull_request.head.repo.full_name == github.repository && - needs.update-code.outputs.changes-present == 'True' + if: always() name: Commit Changes needs: update-code runs-on: ubuntu-latest - environment: production + environment: >- + ${{ + github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository && + needs.update-code.result == 'success' && + needs.update-code.outputs.changes-present == 'True' && + 'production' || + '' + }} permissions: contents: read id-token: write + env: + RUN_AUTOMATION: >- + ${{ + github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository && + needs.update-code.result == 'success' && + needs.update-code.outputs.changes-present == 'True' + }} steps: - - name: Checkout + - name: Commit Changes – Check Applicability + run: >- + Write-Output -InputObject "Run automation: $env:RUN_AUTOMATION" + + - if: env.RUN_AUTOMATION == 'true' + name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false ref: ${{ github.event.pull_request.head.sha }} - - name: Git – Create Local Branch + - if: env.RUN_AUTOMATION == 'true' + name: Git – Create Local Branch run: git switch --create "$env:HEAD_REF" env: HEAD_REF: ${{ github.head_ref }} - - name: Git – Download Changes Patch + - if: env.RUN_AUTOMATION == 'true' + name: Git – Download Changes Patch uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: generated-changes path: ${{ runner.temp }}/generated-changes - - name: Git – Apply Changes Patch + - if: env.RUN_AUTOMATION == 'true' + name: Git – Apply Changes Patch run: git apply --index '${{ runner.temp }}/generated-changes/generated-changes.patch' - - name: App Token – Mint + - if: env.RUN_AUTOMATION == 'true' + name: App Token – Mint id: app-token uses: microsoft/PR-Metrics/.github/actions/mint-github-app-token@0959437740572519916a08b3609e67ffa435f8dd # v1.7.17 with: @@ -179,7 +201,8 @@ jobs: azure-subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} permissions: '{"contents":"write"}' - - name: Git – Commit & Push (Signed) + - if: env.RUN_AUTOMATION == 'true' + name: Git – Commit & Push (Signed) # Do not move this into the checked-out pull request. This step receives a # repository-write token and must not execute pull request code. run: |- @@ -353,18 +376,33 @@ jobs: VALIDATE_TYPESCRIPT_ES: false dependabot-automation: - if: >- - github.event_name == 'pull_request' && - github.event.pull_request.user.login == 'dependabot[bot]' && - github.event.pull_request.head.repo.full_name == github.repository name: Dependabot – Automation runs-on: ubuntu-latest - environment: production + environment: >- + ${{ + github.event_name == 'pull_request' && + github.event.pull_request.user.login == 'dependabot[bot]' && + github.event.pull_request.head.repo.full_name == github.repository && + 'production' || + '' + }} permissions: contents: read id-token: write + env: + RUN_AUTOMATION: >- + ${{ + github.event_name == 'pull_request' && + github.event.pull_request.user.login == 'dependabot[bot]' && + github.event.pull_request.head.repo.full_name == github.repository + }} steps: - - name: App Token – Mint + - name: Dependabot – Check Applicability + run: >- + Write-Output -InputObject "Run automation: $env:RUN_AUTOMATION" + + - if: env.RUN_AUTOMATION == 'true' + name: App Token – Mint id: app-token uses: microsoft/PR-Metrics/.github/actions/mint-github-app-token@0959437740572519916a08b3609e67ffa435f8dd # v1.7.17 with: @@ -373,24 +411,9 @@ jobs: azure-subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} permissions: '{"pull_requests":"write"}' - - name: Auto-Approve + - if: env.RUN_AUTOMATION == 'true' + name: Auto-Approve run: gh pr review --approve "$env:PR_URL" env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} PR_URL: ${{ github.event.pull_request.html_url }} - - dependabot: - if: always() - name: Dependabot - needs: dependabot-automation - runs-on: ubuntu-latest - permissions: {} - steps: - - name: Dependabot – Check Automation - run: |- - if ($env:AUTOMATION_RESULT -notin @('success', 'skipped')) - { - throw "Dependabot automation completed with result '$env:AUTOMATION_RESULT'." - } - env: - AUTOMATION_RESULT: ${{ needs.dependabot-automation.result }} diff --git a/.github/workflows/release-initiate.yml b/.github/workflows/release-initiate.yml index 01afbbc2..c374f085 100644 --- a/.github/workflows/release-initiate.yml +++ b/.github/workflows/release-initiate.yml @@ -5,9 +5,6 @@ name: Release – Initiate on: - pull_request: - branches: - - main workflow_dispatch: null permissions: {} diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 9106a8e1..132fbf74 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -10,9 +10,6 @@ on: - main paths: - .github/release-publish-trigger.txt - pull_request: - branches: - - main workflow_dispatch: null permissions: {}