From 4baf0b1ac112e75bb62d71fabe991080dbd9411a Mon Sep 17 00:00:00 2001 From: Paul Yuknewicz Date: Thu, 24 Sep 2026 01:26:17 -0700 Subject: [PATCH 1/2] Gate production marketplace pull requests on real release tags Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../production-marketplace-release.yml | 35 +++++++++++++++++++ docs/plugin-marketplace.md | 10 ++++++ 2 files changed, 45 insertions(+) create mode 100644 .github/workflows/production-marketplace-release.yml diff --git a/.github/workflows/production-marketplace-release.yml b/.github/workflows/production-marketplace-release.yml new file mode 100644 index 0000000..597882f --- /dev/null +++ b/.github/workflows/production-marketplace-release.yml @@ -0,0 +1,35 @@ +name: Production marketplace release gate + +on: + pull_request: + paths: + - .github/workflows/production-marketplace-release.yml + - .github/plugin/** + - canvases/azure-functions-hosted-skills/** + - canvases/azure-resources-query/** + - plugins/canvas-authoring/** + - docs/canvas-authoring/SHA256SUMS + - docs/plugin-marketplace.md + - docs/release-process.md + - scripts/verify-plugin-marketplace.mjs + - test/plugin-marketplace.test.mjs + - test/fixtures/marketplace.candidate.json + +permissions: + contents: read + +jobs: + production-marketplace-release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + - uses: actions/setup-node@v4 + with: + node-version: '22' + - name: Test marketplace policy + run: node --test test/plugin-marketplace.test.mjs + - name: Verify production marketplace against release tags + run: node scripts/verify-plugin-marketplace.mjs diff --git a/docs/plugin-marketplace.md b/docs/plugin-marketplace.md index 1fd255a..aa0e42b 100644 --- a/docs/plugin-marketplace.md +++ b/docs/plugin-marketplace.md @@ -87,6 +87,16 @@ does not prove the build's origin. tags exist.** Pre-tag tests with temporary *local-only* tags in a disposable clone are synthetic candidate qualification, not release verification; never push those refs or present the result as a published install. +The `Production marketplace release gate` pull-request workflow runs the +marketplace tests and then this unmodified strict validator with complete Git +history and fetched tags. It is expected to fail until the approved product +PRs merge and all three real production tags exist; rerun it after tag +publication. No candidate manifest or temporary tags satisfy this CI gate. +An administrator must make the `production-marketplace-release` job a required +status check for protected release branches; the workflow alone does not +enforce merging policy. Because the workflow is path-filtered, requiring it +for every PR on a branch would leave unrelated PRs pending: use an +always-running required check if branch-wide protection is needed. After publication, check the actual GitHub-hosted marketplace with fresh, isolated `HOME`, `COPILOT_HOME`, and `COPILOT_CACHE_HOME` directories. Confirm that each From 10867dc454338042cd2bb2d53a3f23c2806d6a35 Mon Sep 17 00:00:00 2001 From: Paul Yuknewicz Date: Thu, 24 Sep 2026 01:28:51 -0700 Subject: [PATCH 2/2] Keep marketplace required check present on every pull request Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../production-marketplace-release.yml | 48 ++++++++++++++----- docs/plugin-marketplace.md | 18 +++---- 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/.github/workflows/production-marketplace-release.yml b/.github/workflows/production-marketplace-release.yml index 597882f..a88380d 100644 --- a/.github/workflows/production-marketplace-release.yml +++ b/.github/workflows/production-marketplace-release.yml @@ -2,34 +2,58 @@ name: Production marketplace release gate on: pull_request: - paths: - - .github/workflows/production-marketplace-release.yml - - .github/plugin/** - - canvases/azure-functions-hosted-skills/** - - canvases/azure-resources-query/** - - plugins/canvas-authoring/** - - docs/canvas-authoring/SHA256SUMS - - docs/plugin-marketplace.md - - docs/release-process.md - - scripts/verify-plugin-marketplace.mjs - - test/plugin-marketplace.test.mjs - - test/fixtures/marketplace.candidate.json permissions: contents: read + pull-requests: read jobs: production-marketplace-release: runs-on: ubuntu-latest steps: + - name: Identify release-related changes + id: changes + uses: actions/github-script@v7 + with: + script: | + const files = await github.paginate(github.rest.pulls.listFiles, { + ...context.repo, + pull_number: context.issue.number, + per_page: 100, + }); + const paths = [ + ".github/workflows/production-marketplace-release.yml", + "docs/canvas-authoring/SHA256SUMS", + "docs/plugin-marketplace.md", + "docs/release-process.md", + "scripts/verify-plugin-marketplace.mjs", + "test/plugin-marketplace.test.mjs", + "test/fixtures/marketplace.candidate.json", + ]; + const prefixes = [ + ".github/plugin/", + "canvases/azure-functions-hosted-skills/", + "canvases/azure-resources-query/", + "plugins/canvas-authoring/", + ]; + const relevant = (path) => + paths.includes(path) || prefixes.some((prefix) => path.startsWith(prefix)); + const verify = files.length >= 3000 || files.some(({ filename, previous_filename }) => + relevant(filename) || (previous_filename && relevant(previous_filename))); + core.setOutput("verify", verify ? "true" : "false"); + core.info(verify ? "Run strict marketplace verification." : "Unrelated changes; skip marketplace verification."); - uses: actions/checkout@v4 + if: steps.changes.outputs.verify == 'true' with: fetch-depth: 0 fetch-tags: true - uses: actions/setup-node@v4 + if: steps.changes.outputs.verify == 'true' with: node-version: '22' - name: Test marketplace policy + if: steps.changes.outputs.verify == 'true' run: node --test test/plugin-marketplace.test.mjs - name: Verify production marketplace against release tags + if: steps.changes.outputs.verify == 'true' run: node scripts/verify-plugin-marketplace.mjs diff --git a/docs/plugin-marketplace.md b/docs/plugin-marketplace.md index aa0e42b..d0cb40c 100644 --- a/docs/plugin-marketplace.md +++ b/docs/plugin-marketplace.md @@ -87,16 +87,16 @@ does not prove the build's origin. tags exist.** Pre-tag tests with temporary *local-only* tags in a disposable clone are synthetic candidate qualification, not release verification; never push those refs or present the result as a published install. -The `Production marketplace release gate` pull-request workflow runs the -marketplace tests and then this unmodified strict validator with complete Git -history and fetched tags. It is expected to fail until the approved product -PRs merge and all three real production tags exist; rerun it after tag +The `production-marketplace-release` pull-request check is always present. +For marketplace, product, verifier, and related release-file changes, it runs +the marketplace tests and then this unmodified strict validator with complete +Git history and fetched tags. An unrelated PR skips checkout and verification +but still reports a successful check; a failed changed-files lookup cannot +skip the gate. Relevant PRs are expected to fail until the approved product +PRs merge and all three real production tags exist; rerun after tag publication. No candidate manifest or temporary tags satisfy this CI gate. -An administrator must make the `production-marketplace-release` job a required -status check for protected release branches; the workflow alone does not -enforce merging policy. Because the workflow is path-filtered, requiring it -for every PR on a branch would leave unrelated PRs pending: use an -always-running required check if branch-wide protection is needed. +An administrator must require `production-marketplace-release` on protected +release branches; the workflow alone does not enforce merging policy. After publication, check the actual GitHub-hosted marketplace with fresh, isolated `HOME`, `COPILOT_HOME`, and `COPILOT_CACHE_HOME` directories. Confirm that each