From fde97d6b71886984e1d7ff6af53855d25f87a4f1 Mon Sep 17 00:00:00 2001 From: Mike Grier Date: Sat, 19 Sep 2026 14:30:56 -0700 Subject: [PATCH 1/2] feat: automate calendar-versioned probe binary releases Manage both binary-only probe packages through release-please with UTC calendar versions for direct and dependency-triggered updates. Preserve placement's tag prefix and executable assets; package every platform probe for x64 and ARM64 with default features, checksums and build identity. Only tag-push runs upload binaries or mint attestations. Preserve generated release notes and keep both packages off crates.io. Extend publication-route checks, exercise the real release-please Rust/Cargo implementations and ZIP contents, and align the distribution docs with the new routes. Release scope: both probe bumps are intentional. The shared automation is the feature for both packages, not a docs-only ride-along; no registry library path is touched. Sweep: corrected checkout-only, manual-version and release- scope claims across both probes' docs, manifest comments, rustdoc and tools. Validated npm ci/tests, both platform archive architectures, probe suites and doctest phases, cargo fmt/clippy, default debug/release checks, publication routes, workflow references, scope-checker tests and encoding. Hosted upload and attestation require the eventual tag-push run; no release is created here. Completed item: PB-1: Automate both probe binary releases through CI and release-please. --- .github/release/binary-packages.json | 12 + .github/release/calver.cjs | 26 + .github/release/calver.test.cjs | 29 + .github/release/check-publication.cjs | 74 + .github/release/check-publication.test.cjs | 30 + .github/release/package-lock.json | 1892 +++++++++++++++++ .github/release/package-probes.cjs | 87 + .github/release/package-probes.test.cjs | 42 + .github/release/package.json | 15 + .github/release/release.cjs | 60 + .github/release/release.test.cjs | 137 ++ .github/workflows/ci.yml | 15 + .github/workflows/release-placement-probe.yml | 17 +- .github/workflows/release-platform-probes.yml | 88 + .github/workflows/release-please.yml | 15 +- .gitignore | 1 + .release-please-manifest.json | 1 + COMPLETED-CHECKLIST.md | 25 + COMPLETED-PLANS.md | 1 + DESIGN-NOTES.md | 30 + DESIGN-RATIONALE.md | 28 + DEVELOPMENT.md | 54 +- .../windows-placement-probe/DESIGN-NOTES.md | 14 +- crates/windows-placement-probe/README.md | 8 +- crates/windows-platform-probes/CHECKLIST.md | 8 +- crates/windows-platform-probes/Cargo.toml | 7 +- .../windows-platform-probes/DESIGN-NOTES.md | 20 +- crates/windows-platform-probes/README.md | 29 +- crates/windows-platform-probes/src/lib.rs | 8 +- release-please-config.json | 10 + tools/check-commit-scope.ps1 | 13 +- tools/check-publishable.ps1 | 13 +- 32 files changed, 2745 insertions(+), 64 deletions(-) create mode 100644 .github/release/binary-packages.json create mode 100644 .github/release/calver.cjs create mode 100644 .github/release/calver.test.cjs create mode 100644 .github/release/check-publication.cjs create mode 100644 .github/release/check-publication.test.cjs create mode 100644 .github/release/package-lock.json create mode 100644 .github/release/package-probes.cjs create mode 100644 .github/release/package-probes.test.cjs create mode 100644 .github/release/package.json create mode 100644 .github/release/release.cjs create mode 100644 .github/release/release.test.cjs create mode 100644 .github/workflows/release-platform-probes.yml diff --git a/.github/release/binary-packages.json b/.github/release/binary-packages.json new file mode 100644 index 000000000..b5cdd9a02 --- /dev/null +++ b/.github/release/binary-packages.json @@ -0,0 +1,12 @@ +{ + "crates/windows-placement-probe": { + "package": "windows-placement-probe", + "component": "placement-probe", + "workflow": "release-placement-probe.yml" + }, + "crates/windows-platform-probes": { + "package": "windows-platform-probes", + "component": "windows-platform-probes", + "workflow": "release-platform-probes.yml" + } +} \ No newline at end of file diff --git a/.github/release/calver.cjs b/.github/release/calver.cjs new file mode 100644 index 000000000..7099e5673 --- /dev/null +++ b/.github/release/calver.cjs @@ -0,0 +1,26 @@ +// Copyright (c) Mike Grier. +'use strict'; + +function nextCalendarVersion(previous, date = new Date()) { + if (!(date instanceof Date) || !Number.isFinite(date.valueOf())) throw new Error('Invalid release date'); + const match = /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/.exec(previous); + if (!match) throw new Error('Calendar version must have three numeric components'); + const [year, day, counter] = match.slice(1).map(Number); + if (![year, day, counter].every(Number.isSafeInteger)) throw new Error('Version exceeds safe integer range'); + if (year !== 0) { + const month = Math.floor(day / 100); + const monthDay = day % 100; + const parsed = new Date(Date.UTC(year, month - 1, monthDay)); + if (year < 1000 || month < 1 || month > 12 || parsed.getUTCFullYear() !== year || parsed.getUTCMonth() + 1 !== month || parsed.getUTCDate() !== monthDay) { + throw new Error('Invalid calendar date in previous version'); + } + } + const todayYear = date.getUTCFullYear(); + const todayDay = (date.getUTCMonth() + 1) * 100 + date.getUTCDate(); + if (todayYear < 1000) throw new Error('Release year must be at least 1000'); + if (todayYear > year || (todayYear === year && todayDay > day)) return `${todayYear}.${todayDay}.0`; + if (!Number.isSafeInteger(counter + 1)) throw new Error('Calendar counter exhausted'); + return `${year}.${day}.${counter + 1}`; +} + +module.exports = { nextCalendarVersion }; \ No newline at end of file diff --git a/.github/release/calver.test.cjs b/.github/release/calver.test.cjs new file mode 100644 index 000000000..bc07f1e7d --- /dev/null +++ b/.github/release/calver.test.cjs @@ -0,0 +1,29 @@ +// Copyright (c) Mike Grier. +'use strict'; +const { test } = require('node:test'); +const assert = require('node:assert/strict'); +const { nextCalendarVersion } = require('./calver.cjs'); + +test('calendar releases roll over UTC dates and increment within a day', () => { + for (const [previous, now, expected] of [ + ['2026.902.0', '2026-09-19T00:00:00Z', '2026.919.0'], + ['2026.919.0', '2026-09-19T23:59:59Z', '2026.919.1'], + ['2026.919.8', '2026-09-19T00:00:00Z', '2026.919.9'], + ['2026.1231.3', '2027-01-01T00:00:00Z', '2027.101.0'], + ['2026.131.0', '2026-02-01T00:00:00Z', '2026.201.0'], + ['2028.228.0', '2028-02-29T00:00:00Z', '2028.229.0'], + ['2028.229.0', '2028-03-01T00:00:00Z', '2028.301.0'], + ['2026.920.0', '2026-09-19T23:59:59Z', '2026.920.1'], + ['2027.101.0', '2026-09-19T00:00:00Z', '2027.101.1'], + ['0.0.1', '2026-09-19T00:00:00Z', '2026.919.0'], + ['2026.919.0', '2026-09-19T22:00:00-07:00', '2026.920.0'], + ]) assert.equal(nextCalendarVersion(previous, new Date(now)), expected); +}); + +test('invalid dates, prereleases and numeric overflow fail before publication', () => { + for (const previous of ['1.2', '2026.0902.0', '2026.229.0', '2026.230.0', '2026.1301.0', '2026.900.0', '2026.902.-1', '2026.902.0-beta', '2026.902.9007199254740992']) { + assert.throws(() => nextCalendarVersion(previous, new Date('2026-09-19T00:00:00Z'))); + } + assert.throws(() => nextCalendarVersion('2026.919.9007199254740991', new Date('2026-09-19T00:00:00Z'))); + assert.throws(() => nextCalendarVersion('2026.902.0', new Date('invalid'))); +}); \ No newline at end of file diff --git a/.github/release/check-publication.cjs b/.github/release/check-publication.cjs new file mode 100644 index 000000000..227eb193c --- /dev/null +++ b/.github/release/check-publication.cjs @@ -0,0 +1,74 @@ +// Copyright (c) Mike Grier. +'use strict'; +const fs = require('node:fs'); +const path = require('node:path'); +const assert = require('node:assert/strict'); +const yaml = require('yaml'); +const toml = require('smol-toml'); +const packages = require('./binary-packages.json'); + +function load(root) { + const read = name => fs.readFileSync(path.join(root, name), 'utf8'); + return { + config: JSON.parse(read('release-please-config.json')), + versions: JSON.parse(read('.release-please-manifest.json')), + releasePlease: yaml.parse(read('.github/workflows/release-please.yml')), + registry: yaml.parse(read('.github/workflows/publish-crate.yml')), + probes: Object.entries(packages).map(([directory, spec]) => ({ directory, spec, + manifest: toml.parse(read(`${directory}/Cargo.toml`)), + workflow: yaml.parse(read(`.github/workflows/${spec.workflow}`)), + })), + }; +} + +function check(data) { + const steps = data.releasePlease.jobs['release-please'].steps; + const wrapper = steps.find(step => step.run === 'node .github/release/release.cjs'); + assert(wrapper, 'release-please must load the calendar extension'); + assert.equal(wrapper.env.RELEASE_PLEASE_TOKEN, '${{ secrets.RELEASE_PLEASE_TOKEN }}', 'release tags need the PAT to trigger binary workflows'); + assert(steps.some(step => step.run === 'npm ci --prefix .github/release'), 'release tooling must use the lockfile'); + assert.deepEqual(data.releasePlease.on.push.branches, ['main']); + for (const { directory, spec, manifest, workflow } of data.probes) { + const config = data.config.packages[directory]; + assert(config, `${directory} missing from release-please`); + assert.equal(config.component, spec.component); + assert.equal(config['package-name'], manifest.package.name); + assert.equal(config.versioning, 'probe-calver'); + assert.equal(data.versions[directory], manifest.package.version, 'manifest/package baseline mismatch'); + assert.equal(manifest.package.publish, false, 'probe packages must stay off crates.io'); + assert(workflow.on.push.tags.includes(`${spec.component}-v*`), 'missing probe tag trigger'); + assert(!data.registry.on.push.tags.includes(`${spec.component}-v*`), 'binary tag must not publish to crates.io'); + assert.deepEqual(workflow.jobs.build.strategy.matrix.target, ['x86_64-pc-windows-msvc', 'aarch64-pc-windows-msvc']); + assert.equal(workflow.permissions.contents, 'read'); + assert.equal(workflow.jobs.release.permissions.contents, 'write'); + assert.equal(workflow.jobs.release.permissions['id-token'], 'write'); + assert.equal(workflow.jobs.release.permissions.attestations, 'write'); + const guard = `github.event_name == 'push' && startsWith(github.ref, 'refs/tags/${spec.component}-v')`; + assert.equal(workflow.jobs.release.if, guard, 'release must exclude PR and manual tag dispatch'); + const uploads = workflow.jobs.build.steps.filter(step => step.uses?.startsWith('actions/upload-artifact@')); + assert(uploads.length, 'missing binary artifact transfer'); + for (const upload of uploads) { + assert.equal(upload.if, guard, 'PR/manual builds must not distribute stamped artifacts'); + assert.equal(upload.with['if-no-files-found'], 'error'); + } + const releaseSteps = workflow.jobs.release.steps; + const attest = releaseSteps.findIndex(step => step.uses?.startsWith('actions/attest-build-provenance@')); + const publish = releaseSteps.findIndex(step => step.run?.includes('gh release upload')); + assert(attest >= 0 && publish > attest, 'attest before publishing bytes'); + assert(!releaseSteps.some(step => step.run?.includes('gh release edit')), 'do not overwrite release-please changelogs'); + if (spec.package === 'windows-platform-probes') { + const build = workflow.jobs.build.steps.find(step => step.name === 'Build and verify archive'); + assert(build?.run.includes('package-probes.cjs'), 'use metadata-derived packaging'); + assert.equal(build.env.IS_RELEASE, `\${{ ${guard} }}`); + assert(!build.run.includes('--all-features'), 'test-only renderer oracle must remain disabled'); + } + } +} + +module.exports = { load, check }; +if (require.main === module) { + let result; + try { check(load(process.argv[2] || path.resolve(__dirname, '../..'))); result = { status: 'success', message: 'Binary release routes and publication guards verified' }; } + catch (error) { process.exitCode = 1; result = { status: 'error', error: error.message }; } + process.stdout.write(JSON.stringify(result) + '\n'); +} \ No newline at end of file diff --git a/.github/release/check-publication.test.cjs b/.github/release/check-publication.test.cjs new file mode 100644 index 000000000..3ab3f5ed9 --- /dev/null +++ b/.github/release/check-publication.test.cjs @@ -0,0 +1,30 @@ +// Copyright (c) Mike Grier. +'use strict'; +const { test } = require('node:test'); +const assert = require('node:assert/strict'); +const path = require('node:path'); +const { load, check } = require('./check-publication.cjs'); + +test('actual manifests and workflows have executable binary release routes', () => check(load(path.resolve(__dirname, '../..')))); + +test('publication guards reject missing routes, unsafe triggers and oracle builds', () => { + for (let defect = 0; defect < 12; defect++) { + const data = load(path.resolve(__dirname, '../..')); + const probe = data.probes[defect % 2]; + switch (defect) { + case 0: delete data.config.packages[probe.directory]; break; + case 1: probe.workflow.on.push.tags = []; break; + case 2: probe.workflow.jobs.release.if = 'true'; break; + case 3: delete probe.workflow.jobs.build.steps.find(step => step.uses?.startsWith('actions/upload-artifact@')).if; break; + case 4: probe.manifest.package.publish = true; break; + case 5: data.registry.on.push.tags.push(`${probe.spec.component}-v*`); break; + case 6: probe.workflow.jobs.release.steps.reverse(); break; + case 7: probe.workflow.jobs.build.steps.find(step => step.name === 'Build and verify archive').run += '\ncargo build --all-features'; break; + case 8: data.versions[probe.directory] = '0.0.0'; break; + case 9: probe.workflow.jobs.build.strategy.matrix.target.pop(); break; + case 10: data.releasePlease.jobs['release-please'].steps.find(step => step.id === 'release').env.RELEASE_PLEASE_TOKEN = '${{ github.token }}'; break; + case 11: data.config.packages[probe.directory].versioning = 'default'; break; + } + assert.throws(() => check(data), `defect ${defect} survived`); + } +}); \ No newline at end of file diff --git a/.github/release/package-lock.json b/.github/release/package-lock.json new file mode 100644 index 000000000..e761dec72 --- /dev/null +++ b/.github/release/package-lock.json @@ -0,0 +1,1892 @@ +{ + "name": "windows-probe-release-automation", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "windows-probe-release-automation", + "version": "0.0.0", + "dependencies": { + "@actions/core": "3.0.1", + "fflate": "0.8.3", + "release-please": "17.11.2", + "smol-toml": "1.8.0", + "yaml": "2.9.1" + } + }, + "node_modules/@actions/core": { + "version": "3.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@actions/core/-/core-3.0.1.tgz", + "integrity": "sha1-D02LFFJ+5R4NsGHu3CSiBsn5jCM=", + "license": "MIT", + "dependencies": { + "@actions/exec": "^3.0.0", + "@actions/http-client": "^4.0.0" + } + }, + "node_modules/@actions/exec": { + "version": "3.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@actions/exec/-/exec-3.0.0.tgz", + "integrity": "sha1-jDRk0g8KpAaHB3VwIdfjwBp+4gM=", + "license": "MIT", + "dependencies": { + "@actions/io": "^3.0.2" + } + }, + "node_modules/@actions/http-client": { + "version": "4.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@actions/http-client/-/http-client-4.0.1.tgz", + "integrity": "sha1-IqI6diW6EybZuhVAEsqDAaJ/iKM=", + "license": "MIT", + "dependencies": { + "tunnel": "^0.0.6", + "undici": "^6.23.0" + } + }, + "node_modules/@actions/io": { + "version": "3.0.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@actions/io/-/io-3.0.2.tgz", + "integrity": "sha1-b4myehWdEJg22YPvooOZfCO5IoQ=", + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha1-8vu/6ofESiFZDsUVt3iywm2IZuc=", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha1-vYcITO0MeW7Ea9pJLeboPSnon8I=", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@conventional-commits/parser": { + "version": "0.4.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@conventional-commits/parser/-/parser-0.4.1.tgz", + "integrity": "sha1-ITcXslt/+FJg0pVGbCgiJv/glPI=", + "license": "ISC", + "dependencies": { + "unist-util-visit": "^2.0.3", + "unist-util-visit-parents": "^3.1.1" + } + }, + "node_modules/@google-automations/git-file-utils": { + "version": "3.2.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@google-automations/git-file-utils/-/git-file-utils-3.2.0.tgz", + "integrity": "sha1-2Kzc4pzUw740iyfMaT8ot/RbVc4=", + "license": "Apache-2.0", + "dependencies": { + "@octokit/rest": "^20.1.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": ">= 22" + } + }, + "node_modules/@iarna/toml": { + "version": "3.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@iarna/toml/-/toml-3.0.0.tgz", + "integrity": "sha1-zN5Skv6dNIu+k/6Q1Xn8RCtysLM=", + "license": "ISC" + }, + "node_modules/@jsep-plugin/assignment": { + "version": "1.3.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", + "integrity": "sha1-/PxUF6BJM/fO7nhuirSYqjziskI=", + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@jsep-plugin/regex": { + "version": "1.0.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@jsep-plugin/regex/-/regex-1.0.4.tgz", + "integrity": "sha1-yy/EIyIPpxxgkyO5un99NEp1X8w=", + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha1-QNID6oJ7nxf0KinGr7k7d0XvgMc=", + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "5.2.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@octokit/core/-/core-5.2.2.tgz", + "integrity": "sha1-JSgFcy3ptOjk9ljTS4DEybJTR2E=", + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.4.1", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "9.0.6", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@octokit/endpoint/-/endpoint-9.0.6.tgz", + "integrity": "sha1-EU2RIQj+aS2LE5z+f8CEbf0RtsA=", + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "7.1.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@octokit/graphql/-/graphql-7.1.1.tgz", + "integrity": "sha1-ednz0Mlqj9E9ZBhv5cM2BtSLecw=", + "license": "MIT", + "dependencies": { + "@octokit/request": "^8.4.1", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha1-PVXDLqwNONoacIOpw7DMp3kk99M=", + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "11.4.4-cjs.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.4.4-cjs.2.tgz", + "integrity": "sha1-l5oQ1Xe856OT6OZZU4h+QrCgUAA=", + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.7.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "4.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz", + "integrity": "sha1-mKPKluCxBzgGZHCBEYZMuWVR+Vg=", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "13.3.2-cjs.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.3.2-cjs.1.tgz", + "integrity": "sha1-0KFC/0HY94krbM70WXkEn1Hsqo0=", + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.8.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^5" + } + }, + "node_modules/@octokit/request": { + "version": "8.4.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@octokit/request/-/request-8.4.1.tgz", + "integrity": "sha1-cVoBXM+ZMIeXfqQ2XER5H8RXJIY=", + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^9.0.6", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "5.1.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@octokit/request-error/-/request-error-5.1.1.tgz", + "integrity": "sha1-uSGPnBFm5ou00MibY47cYskzSAU=", + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest": { + "version": "20.1.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@octokit/rest/-/rest-20.1.2.tgz", + "integrity": "sha1-HXTQxyreDWT3xUFkSNXIhfXjzMQ=", + "license": "MIT", + "dependencies": { + "@octokit/core": "^5.0.2", + "@octokit/plugin-paginate-rest": "11.4.4-cjs.2", + "@octokit/plugin-request-log": "^4.0.0", + "@octokit/plugin-rest-endpoint-methods": "13.3.2-cjs.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha1-PnxrGcAjbCcGVuTqZmFIwrUf0aM=", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha1-7BB1XocUl7zYPv6SfkPsRujAdH4=", + "license": "MIT" + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha1-VuLMJsOXwDj6sOOpF6EtXFkJ6QE=", + "license": "MIT" + }, + "node_modules/@types/npm-package-arg": { + "version": "6.1.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/npm-package-arg/-/npm-package-arg-6.1.4.tgz", + "integrity": "sha1-ESt0phy4YyMT9gAhJ4KEAVbgIo4=", + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha1-Ea9XsSfjJId3SEH3pOVOqxZtA8Q=", + "license": "MIT" + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.15", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@xmldom/xmldom/-/xmldom-0.8.15.tgz", + "integrity": "sha1-cev4Bynk6VIh1Rmsmx4erqd4SQc=", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha1-48121MVI7oldPD/Y3B9sW5Ay56g=", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha1-CCyyyJyf6GWaMRpTvWpNxTAdswQ=", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha1-JG9Q88p4oyQPbJl+ipvR6sSeSzg=", + "license": "Python-2.0" + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", + "license": "MIT" + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha1-Dn82wE2EeOeli9vtgM7fl3eF8oA=", + "license": "MIT", + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha1-v7EGYv7tgZaixi58aOF3IMJ0F5o=", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha1-xR6AnIGk41QIRCK5smutiCScUXw=", + "license": "Apache-2.0" + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha1-fHJDiAm1+lur9UGZofHCgaaYT88=", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha1-48mzFWnhBoEd8kL3FXJaH0xJQyA=", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha1-XnVda6UaoiPsfT1S8ld4IQ+dw8A=", + "license": "MIT", + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha1-qsTit3NKdAhnrrFr8CqtVWoeegE=", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha1-DASwddsCy/5g3I5s8vVIaxo2CKo=", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=", + "license": "MIT" + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha1-+2XnXtvd/S5WhVTotbBf/3pR/LM=", + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "6.1.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-6.1.0.tgz", + "integrity": "sha1-O60F9O6mTkI9PZD8UMF9LIzxdlI=", + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "6.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/conventional-changelog-writer/-/conventional-changelog-writer-6.0.1.tgz", + "integrity": "sha1-2NO7Xh9iMMrtlp3MdiscNoqPewE=", + "license": "MIT", + "dependencies": { + "conventional-commits-filter": "^3.0.0", + "dateformat": "^3.0.3", + "handlebars": "^4.7.7", + "json-stringify-safe": "^5.0.1", + "meow": "^8.1.2", + "semver": "^7.0.0", + "split": "^1.0.1" + }, + "bin": { + "conventional-changelog-writer": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-commits-filter": { + "version": "3.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/conventional-commits-filter/-/conventional-commits-filter-3.0.0.tgz", + "integrity": "sha1-vxETJmFR3WTEnNJp4+t9cdcBXuI=", + "license": "MIT", + "dependencies": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha1-Abbo0WNje7LdbJgspO1lhjaCeG4=", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha1-zcyPm2l3cZ/fvR3nrsJKv3Vrneo=", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/dateformat": { + "version": "3.0.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha1-puN0maTZqc+F71hyBE1ikByYia4=", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha1-BKLVI7LxjYDQFYpDuJXVbf+NGdg=", + "license": "MIT", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha1-Y2jL20Cr8zc7UlrIfkomDDpwCRk=", + "license": "ISC" + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha1-WSSF67v2s7GrK+F1yDk9BMoNV+Y=", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/diff/-/diff-8.0.4.tgz", + "integrity": "sha1-T1uvMYi5skMRF7li6yC6Mw+t9pY=", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha1-5BuALh7t+fbK4YPOXmIteJ19jlM=", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha1-XEXo6GmVJiYzHXqrMm0B2vZdWJ0=", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha1-zDhff3UfHR/GUMITdIBCVFOMfTE=", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha1-7b/itmiwwdl8JLrw8QYrEyIhvHg=", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha1-kMzOcIzZzYLMTcjD3dmr3VWyDog=", + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha1-6Bj9ac5cz8tARZT4QpY79TFkzDc=", + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/entities/-/entities-4.5.0.tgz", + "integrity": "sha1-XSaOpecRPsdMTQM7eepaNaSI+0g=", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha1-s6jYu2+S7swWKePifTyGB6ijJBQ=", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha1-BfdaJdq5jk+x3NXhRywFRtUFfI8=", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha1-ARo/aYVroYnf+n3I/M6Z0qh5A+U=", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fflate": { + "version": "0.8.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha1-vCfY6zA0PU1RKrsDSAICzmXYJfw=", + "license": "MIT" + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/figures/-/figures-3.2.0.tgz", + "integrity": "sha1-YlwYvSk8YE3EqN2y/r8MiDQXRq8=", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha1-l6/n1s3AvFkoWEt8jXsW6KmqXRk=", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha1-LALYZNl/PqbIgwxGTL0Rq26rehw=", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha1-T5RBKoLbMvNuOwuXQfipf+sDH34=", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha1-bxOQgqtY3E5aDlHv59ta6JDVag8=", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha1-HG7aXBaFxjlCdm15u0Cudzzs2IM=", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha1-jGLYy5C+sqrV0KW2dYGtmFTD8AM=", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/he/-/he-1.2.0.tgz", + "integrity": "sha1-hK5l+n6vsWX922FWauFLrwVmTw8=", + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha1-gnuChn6f8cjQxNnVOIA5fSyG0iQ=", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha1-mosfJGhmwChQlIZYX2K48sGMJw4=", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha1-2o3+rH2hMLBcK6S1nJts1mYRprk=", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha1-Yk+PRJfWGbLZdoUx1Y9BIoVNclE=", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha1-PgdFCoCA684/vwysSU9NKrMk4II=", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha1-8Rb4Bk/pCz94RKOJl8C3UFEmnx0=", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha1-Rz+wXZc3BeP9liBUUBjKjiLvSYI=", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk=", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha1-jkT7FKJkPFlya7FXh7XxUSyz0/s=", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsep": { + "version": "1.4.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/jsep/-/jsep-1.4.0.tgz", + "integrity": "sha1-Gf7Mv6Udinn3JIC0uOQM4uFxUvA=", + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha1-fEeAWpQxmSjgV3dAXcEuH3pO4C0=", + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "license": "ISC" + }, + "node_modules/jsonpath-plus": { + "version": "10.4.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/jsonpath-plus/-/jsonpath-plus-10.4.0.tgz", + "integrity": "sha1-c89UXCMa/aIUUhULeipY5I4QlwI=", + "license": "MIT", + "dependencies": { + "@jsep-plugin/assignment": "^1.3.0", + "@jsep-plugin/regex": "^1.0.4", + "jsep": "^1.4.0" + }, + "bin": { + "jsonpath": "bin/jsonpath-cli.js", + "jsonpath-plus": "bin/jsonpath-cli.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha1-7KKE910pZQeTCdwK2SVauy68FjI=", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha1-Gvujlq/WdqbUJQTQpno6frn2KqA=", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha1-bW/mVw69lqr5D8rR2vo7JWbbOpQ=", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha1-kwT5Buk/qucIgNoQKp8d8OqLsFo=", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow": { + "version": "8.1.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/meow/-/meow-8.1.2.tgz", + "integrity": "sha1-vL5FvaDuFynTUMA8/8g5WjbE6Jc=", + "license": "MIT", + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha1-20vBUaSiz07r+a3V23VQjbbMhB8=", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha1-pj9oFnOzBXH76LwlaGrnRu76mGk=", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha1-/ZVrvgt3JB6fFaxdzLHGOAYJaO8=", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha1-waRk52kzAuCCoHXO4MBXdBrEdyw=", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha1-wGVXE8U6ii69d/+iR9NCxA8BBhk=", + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/modify-values": { + "version": "1.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha1-s5OfpgVUZHTj4+PGPWS9Q7TuYCI=", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ms/-/ms-2.1.3.tgz", + "integrity": "sha1-V0yBOM4dK1hh8LRFedut1gxmFbI=", + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha1-tKr7k+OustgXTKU88WOrfXMIMF8=", + "license": "MIT" + }, + "node_modules/node-html-parser": { + "version": "6.1.13", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/node-html-parser/-/node-html-parser-6.1.13.tgz", + "integrity": "sha1-od95m4PfXGdD/NknQLoUaCCDt+Q=", + "license": "MIT", + "dependencies": { + "css-select": "^5.1.0", + "he": "1.2.0" + } + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha1-28w+LaWVCaCYNCKITNFy7v36Ul4=", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha1-yeq0KO/842zWuSySS9sADvHx7R0=", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha1-PdM8ZHohT9//2DWTPrCG2g3CHbE=", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha1-o0KLtwiLOmApL2aRkni3wpetTwc=", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha1-yyhoVA4xPWHeWPr741zpAE1VQOY=", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-github-repo-url": { + "version": "1.4.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz", + "integrity": "sha1-nn2LslKmy2ukJZUGC3v23z28H1A=", + "license": "MIT" + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha1-x2/Gbe5UIxyWKyK8yKcs8vmXU80=", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha1-UTvb4tO5XXdi6METfvoZXGxhtbM=", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha1-+8EUtgykKzDZ2vWFjkvWi77bZzU=", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha1-PTIa8+q5ObCDyPkpodEs2oHCa2s=", + "license": "ISC" + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha1-W4h48ROlgheEjGSCAmxz4bpXcn8=", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha1-e/KVQ4yloz5WzTDgU7NO5yUMk8w=", + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha1-86YTV1hFlzOuK5VjgFbhhU5+9Qc=", + "license": "MIT", + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha1-CeJJ696FHTseSNJ8EFREZn8XuD0=", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha1-3/wL+aIcAiCQkPKqaUKeFBTa8/k=", + "license": "ISC" + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha1-5m2xg4sgDB38IzIl0SyzZSDiNKg=", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/semver/-/semver-5.7.2.tgz", + "integrity": "sha1-SNVdtzfDKHzUg14X+hP+rOHEHvg=", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha1-jSojcNPfiG61yQraHFv2GIrPg4s=", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/redent/-/redent-3.0.0.tgz", + "integrity": "sha1-5Ve3mYMWu1PJ8fVvpiY1LGljBZ8=", + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/release-please": { + "version": "17.11.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/release-please/-/release-please-17.11.2.tgz", + "integrity": "sha1-grXeqPL1DhgAHL0h3laafB94E1g=", + "license": "Apache-2.0", + "dependencies": { + "@conventional-commits/parser": "^0.4.1", + "@google-automations/git-file-utils": "^3.2.0", + "@iarna/toml": "^3.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.3.1", + "@octokit/request-error": "^5.1.0", + "@octokit/rest": "^20.1.1", + "@types/npm-package-arg": "^6.1.0", + "@xmldom/xmldom": "^0.8.4", + "async-retry": "^1.3.3", + "chalk": "^4.0.0", + "conventional-changelog-conventionalcommits": "^6.0.0", + "conventional-changelog-writer": "^6.0.0", + "conventional-commits-filter": "^3.0.0", + "detect-indent": "^6.1.0", + "diff": "^8.0.3", + "figures": "^3.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "js-yaml": "^4.3.1", + "jsonpath-plus": "^10.0.0", + "node-html-parser": "^6.0.0", + "parse-github-repo-url": "^1.4.1", + "semver": "^7.5.3", + "type-fest": "^3.0.0", + "typescript": "^4.6.4", + "unist-util-visit": "^2.0.3", + "unist-util-visit-parents": "^3.1.1", + "xpath": "^0.0.34", + "yaml": "^2.2.2", + "yargs": "^17.0.0" + }, + "bin": { + "release-please": "build/src/bin/release-please.js" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha1-9bKmgIl8acI4oTzRaxVnH4tzVJ8=", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/retry/-/retry-0.13.1.tgz", + "integrity": "sha1-GFsVh6z2eRnWOzVzSeA1N7JIRlg=", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/semver/-/semver-7.8.5.tgz", + "integrity": "sha1-ObZGA33VDBT7RR5+TKxY7YuGP2k=", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/smol-toml": { + "version": "1.8.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/smol-toml/-/smol-toml-1.8.0.tgz", + "integrity": "sha1-3F4JNoJfH+83vfLFaQgfcJ2KAJ8=", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha1-T1qwZo8AWeNPnADc4zF4ShLeTpw=", + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha1-XWB9J/yAb2bXtkp2ZlD6iQ8E7WY=", + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha1-z3D1BILu/cmOPOCmgz5KU87rpnk=", + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha1-sGnmh7EpGjLxJok+12onp0XuITM=", + "license": "CC0-1.0" + }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/split/-/split-1.0.1.tgz", + "integrity": "sha1-YFvZvjA6pZ+zX5Ip++oN3snqB9k=", + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha1-JpxxF9J7Ba0uU2gwqOyJXvnG0BA=", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha1-nibGPTD1NEPpSJSVshBdN7Z6hdk=", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha1-wy4c7pQLazQyx3G8LFS8znPNMAE=", + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha1-btpL00SjyUrqN21MwxvHcxEDngk=", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "license": "MIT" + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha1-Jgpdli2LdSQlsy86fbDcrNF2wUQ=", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha1-cvExSzSlsZLbASMk3yzFh8pH+Sw=", + "license": "MIT", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha1-u3RMHwZ4vqdUOi0ewk6D5o6MhwY=", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha1-CVl5+bzA0J2jJNWNA86Pg3TL5lo=", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha1-gjFem7xvKyWIiFis0f/4RBA1t38=", + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici": { + "version": "6.28.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/undici/-/undici-6.28.1.tgz", + "integrity": "sha1-qp0sRK7zhAvvgCFvKlDzN1Q6P70=", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/unist-util-is": { + "version": "4.1.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha1-l25fRip6Xec9lLcGusG5BnG1d5c=", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "2.0.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha1-w3A4kxRt9HIDu4qXla9H17lxIIw=", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha1-ZabOaY94prD1aqDojxOAGIbNrvY=", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha1-FfIPVdo8kwxXvdvxc0xmVNX9Nao=", + "license": "ISC" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha1-/JH2uce6FchX9MssXe/uw51PQQo=", + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha1-Z+FFz/UQpqaYS98RUpEdadLrnkM=", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "license": "ISC" + }, + "node_modules/xpath": { + "version": "0.0.34", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/xpath/-/xpath-0.0.34.tgz", + "integrity": "sha1-p2klXogW4JOOHgAF8rqnJ5vovhI=", + "license": "MIT", + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha1-f0k00PfKjFb5UxSTndzS3ZHOHVU=", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.9.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha1-d53/5ryv7FlqcXLpgyiaWIZH+qo=", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha1-LrfcOwKJcY/ClfNidThFxBoMlO4=", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha1-kJa87r+ZDSG7MfqVFuDt4pSnfTU=", + "license": "ISC", + "engines": { + "node": ">=12" + } + } + } +} diff --git a/.github/release/package-probes.cjs b/.github/release/package-probes.cjs new file mode 100644 index 000000000..7457331c4 --- /dev/null +++ b/.github/release/package-probes.cjs @@ -0,0 +1,87 @@ +// Copyright (c) Mike Grier. +'use strict'; +const fs = require('node:fs'); +const path = require('node:path'); +const { execFileSync } = require('node:child_process'); +const { createHash } = require('node:crypto'); +const { zipSync } = require('fflate'); +const packages = require('./binary-packages.json'); + +const machines = { 'x86_64-pc-windows-msvc': 0x8664, 'aarch64-pc-windows-msvc': 0xaa64 }; + +function checkPe(bytes, target) { + if (!machines[target] || bytes.length < 64 || bytes.toString('ascii', 0, 2) !== 'MZ') throw new Error('Invalid executable or target'); + const offset = bytes.readUInt32LE(60); + if (offset > bytes.length - 6 || bytes.readUInt32LE(offset) !== 0x4550 || bytes.readUInt16LE(offset + 4) !== machines[target]) { + throw new Error('PE architecture does not match target'); + } +} + +function selectExecutables(pkg, records) { + const expected = pkg.targets.filter(target => target.kind.includes('bin')).map(target => target.name).sort(); + if (!expected.length) throw new Error('Package declares no binaries'); + const found = new Map(); + for (const record of records) { + if (record.reason !== 'compiler-artifact' || record.package_id !== pkg.id || !record.executable || !record.target.kind.includes('bin')) continue; + if (record.features.includes('oracle-in-renderer')) throw new Error('Shipping build enables the test-only renderer oracle'); + if (found.has(record.target.name)) throw new Error('Duplicate executable artifact'); + found.set(record.target.name, record.executable); + } + if (JSON.stringify([...found.keys()].sort()) !== JSON.stringify(expected)) throw new Error('Compiled executable census differs from Cargo targets'); + return found; +} + +function archive(pkg, records, { target, revision, component, tag, read = fs.readFileSync }) { + if (tag && tag !== `${component}-v${pkg.version}`) throw new Error('Release tag does not match package version'); + if (!/^[0-9a-f]{40}$/.test(revision)) throw new Error('Expected full build commit'); + const files = {}; + for (const [name, executable] of selectExecutables(pkg, records)) { + if (!/^[a-zA-Z0-9_-]+$/.test(name)) throw new Error('Invalid binary filename'); + const bytes = read(executable); + checkPe(bytes, target); + files[`${name}.exe`] = bytes; + } + const info = { package: pkg.name, version: pkg.version, target, git_revision: revision, + features: 'default', binaries: Object.keys(files).sort() }; + files['BUILD-INFO.json'] = Buffer.from(JSON.stringify(info, null, 2) + '\n'); + return { files, info }; +} + +function main(args) { + const [packageName, target, output, tag] = args; + const entry = Object.entries(packages).find(([, spec]) => spec.package === packageName); + if (!entry || !machines[target] || !output) throw new Error('usage: package-probes [tag]'); + const root = path.resolve(__dirname, '../..'); + const destination = path.resolve(output); + if (!destination.startsWith(path.join(root, '.scratch') + path.sep)) throw new Error('Output must be under .scratch'); + if (fs.existsSync(destination)) throw new Error('Output directory already exists'); + const run = (program, argv, extra = {}) => execFileSync(program, argv, { cwd: root, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024, ...extra }); + const metadata = JSON.parse(run('cargo', ['metadata', '--locked', '--no-deps', '--format-version', '1'])); + const pkg = metadata.packages.find(pkg => pkg.name === packageName); + if (!pkg) throw new Error('Package is not in workspace'); + if (tag && tag !== `${entry[1].component}-v${pkg.version}`) throw new Error('Release tag does not match package version'); + const revision = run('git', ['--no-pager', 'rev-parse', 'HEAD']).trim(); + if (tag && run('git', ['--no-pager', 'status', '--porcelain']).trim()) throw new Error('Tagged publication requires a clean checkout'); + const built = run('cargo', ['build', '--release', '--locked', '--target', target, '-p', packageName, '--bins', '--message-format=json'], { + env: { ...process.env, PLACEMENT_PROBE_COMMIT: revision, PLACEMENT_PROBE_SOURCE: 'ci' }, + }); + const records = built.split(/\r?\n/).filter(Boolean).map(line => JSON.parse(line)); + if (!records.some(record => record.reason === 'build-finished' && record.success)) throw new Error('Cargo did not finish successfully'); + const { files, info } = archive(pkg, records, { target, revision, component: entry[1].component, tag }); + files['README.md'] = fs.readFileSync(path.join(root, entry[0], 'README.md')); + files['LICENSE'] = fs.readFileSync(path.join(root, 'LICENSE')); + const name = `${packageName}-${target.split('-')[0]}.zip`; + const bytes = zipSync(files); + fs.mkdirSync(destination, { recursive: true }); + fs.writeFileSync(path.join(destination, name), bytes, { flag: 'wx' }); + fs.writeFileSync(path.join(destination, `${name}.sha256`), `${createHash('sha256').update(bytes).digest('hex')} ${name}\n`, { flag: 'wx' }); + return { archive: name, ...info }; +} + +module.exports = { archive, selectExecutables, checkPe, main }; +if (require.main === module) { + let result; + try { result = { status: 'success', ...main(process.argv.slice(2)) }; } + catch (error) { process.exitCode = 1; result = { status: 'error', error: error.message }; } + process.stdout.write(JSON.stringify(result) + '\n'); +} \ No newline at end of file diff --git a/.github/release/package-probes.test.cjs b/.github/release/package-probes.test.cjs new file mode 100644 index 000000000..34d5ad154 --- /dev/null +++ b/.github/release/package-probes.test.cjs @@ -0,0 +1,42 @@ +// Copyright (c) Mike Grier. +'use strict'; +const { test } = require('node:test'); +const assert = require('node:assert/strict'); +const { zipSync, unzipSync } = require('fflate'); +const { archive, checkPe, selectExecutables } = require('./package-probes.cjs'); + +function pe(machine) { + const bytes = Buffer.alloc(128); + bytes.write('MZ'); bytes.writeUInt32LE(64, 60); bytes.writeUInt32LE(0x4550, 64); bytes.writeUInt16LE(machine, 68); + return bytes; +} +function fixture(count) { + const pkg = { id: 'path+probe', name: 'probe', version: '2026.919.0', targets: Array.from({ length: count }, (_, index) => ({ name: `probe-${index}`, kind: ['bin'] })) }; + const records = pkg.targets.map(target => ({ reason: 'compiler-artifact', package_id: pkg.id, target, executable: `${target.name}.exe`, features: [] })); + return { pkg, records }; +} + +test('archives exactly the declared binaries for both architectures and growing target sets', () => { + for (const count of [1, 2, 3, 4, 5, 8, 10, 16, 17, 32]) { + for (const [target, machine] of [['x86_64-pc-windows-msvc', 0x8664], ['aarch64-pc-windows-msvc', 0xaa64]]) { + const { pkg, records } = fixture(count); + const result = archive(pkg, records, { target, revision: 'a'.repeat(40), component: 'probe', tag: 'probe-v2026.919.0', read: () => pe(machine) }); + const extracted = unzipSync(zipSync(result.files)); + assert.equal(Object.keys(extracted).length, count + 1); + assert.equal(JSON.parse(Buffer.from(extracted['BUILD-INFO.json']).toString()).binaries.length, count); + for (const name of result.info.binaries) checkPe(Buffer.from(extracted[name]), target); + } + } +}); + +test('missing, extra, duplicate, wrong architecture and oracle-enabled binaries fail', () => { + const { pkg, records } = fixture(2); + assert.throws(() => selectExecutables(pkg, records.slice(1)), /census/); + assert.throws(() => selectExecutables(pkg, [...records, records[0]]), /Duplicate/); + assert.throws(() => selectExecutables(pkg, [...records, { ...records[0], target: { kind: ['bin'], name: 'extra' } }]), /census/); + assert.throws(() => selectExecutables(pkg, [{ ...records[0], features: ['oracle-in-renderer'] }, records[1]]), /oracle/); + assert.throws(() => checkPe(pe(0x8664), 'aarch64-pc-windows-msvc'), /architecture/); + assert.throws(() => checkPe(Buffer.alloc(5), 'x86_64-pc-windows-msvc'), /Invalid/); + assert.throws(() => archive(pkg, records, { target: 'x86_64-pc-windows-msvc', revision: 'a'.repeat(40), component: 'probe', tag: 'probe-v0.0.1' }), /tag/); + assert.equal(selectExecutables(pkg, [...records, { reason: 'compiler-artifact', package_id: 'dependency', target: { kind: ['bin'], name: 'other' }, executable: 'other.exe' }]).size, 2); +}); \ No newline at end of file diff --git a/.github/release/package.json b/.github/release/package.json new file mode 100644 index 000000000..66d8af0ec --- /dev/null +++ b/.github/release/package.json @@ -0,0 +1,15 @@ +{ + "name": "windows-probe-release-automation", + "version": "0.0.0", + "private": true, + "scripts": { + "test": "node --test *.test.cjs" + }, + "dependencies": { + "@actions/core": "3.0.1", + "fflate": "0.8.3", + "release-please": "17.11.2", + "smol-toml": "1.8.0", + "yaml": "2.9.1" + } +} diff --git a/.github/release/release.cjs b/.github/release/release.cjs new file mode 100644 index 000000000..16543e967 --- /dev/null +++ b/.github/release/release.cjs @@ -0,0 +1,60 @@ +// Copyright (c) Mike Grier. +'use strict'; +const { GitHub, Manifest, registerVersioningStrategy, registerPlugin } = require('release-please'); +const { DefaultVersioningStrategy } = require('release-please/build/src/versioning-strategies/default.js'); +const { CargoWorkspace } = require('release-please/build/src/plugins/cargo-workspace.js'); +const { Version } = require('release-please/build/src/version.js'); +const { nextCalendarVersion } = require('./calver.cjs'); + +function registerCalendarVersions(date = new Date()) { + class CalendarStrategy extends DefaultVersioningStrategy { + determineReleaseType() { + return { bump: previous => Version.parse(nextCalendarVersion(previous.toString(), date)) }; + } + } + class CalendarWorkspace extends CargoWorkspace { + bumpVersion(pkg) { + if (this.repositoryConfig[pkg.path]?.versioning === 'probe-calver') { + return Version.parse(nextCalendarVersion(pkg.version, date)); + } + return super.bumpVersion(pkg); + } + } + registerVersioningStrategy('probe-calver', options => new CalendarStrategy(options)); + registerPlugin('cargo-workspace', options => new CalendarWorkspace( + options.github, options.targetBranch, options.repositoryConfig, + { ...options, ...options.type, merge: options.type.merge ?? !options.separatePullRequests }, + )); +} + +async function releaseAndPropose(github, load = () => Manifest.fromManifest(github, github.repository.defaultBranch)) { + const releases = (await (await load()).createReleases()).filter(Boolean); + const prs = (await (await load()).createPullRequests()).filter(Boolean); + return { releases, prs }; +} + +async function main(core) { + if (process.env.GITHUB_EVENT_NAME !== 'push' || process.env.GITHUB_REF !== 'refs/heads/main') { + throw new Error('Release automation may only run on a push to main'); + } + const token = process.env.RELEASE_PLEASE_TOKEN; + if (!token) throw new Error('RELEASE_PLEASE_TOKEN is required to trigger downstream tag workflows'); + const [owner, repo] = (process.env.GITHUB_REPOSITORY || '').split('/'); + if (!owner || !repo) throw new Error('GITHUB_REPOSITORY is required'); + registerCalendarVersions(); + const github = await GitHub.create({ owner, repo, token, defaultBranch: 'main' }); + const { releases, prs } = await releaseAndPropose(github); + core.setOutput('release_created', releases.length > 0); + core.setOutput('releases_created', releases.length > 0); + core.setOutput('paths_released', JSON.stringify(releases.map(release => release.path || '.'))); + core.setOutput('pr', prs[0] || ''); + core.setOutput('prs', JSON.stringify(prs)); + for (const release of releases) { + const prefix = release.path && release.path !== '.' ? `${release.path}--` : ''; + core.setOutput(`${prefix}release_created`, true); + core.setOutput(`${prefix}tag_name`, release.tagName); + } +} + +module.exports = { registerCalendarVersions, releaseAndPropose }; +if (require.main === module) import('@actions/core').then(core => main(core).catch(error => core.setFailed(error.message))); \ No newline at end of file diff --git a/.github/release/release.test.cjs b/.github/release/release.test.cjs new file mode 100644 index 000000000..86b085108 --- /dev/null +++ b/.github/release/release.test.cjs @@ -0,0 +1,137 @@ +// Copyright (c) Mike Grier. +'use strict'; +const { test } = require('node:test'); +const assert = require('node:assert/strict'); +const { registerCalendarVersions, releaseAndPropose } = require('./release.cjs'); +const { buildVersioningStrategy } = require('release-please/build/src/factories/versioning-strategy-factory.js'); +const { buildPlugin } = require('release-please/build/src/factories/plugin-factory.js'); +const { Version } = require('release-please/build/src/version.js'); +const { nextCalendarVersion } = require('./calver.cjs'); + +test('release-please uses calendar versions for probes and unchanged semver for libraries', () => { + registerCalendarVersions(new Date('2026-09-19T00:00:00Z')); + const github = { repository: { owner: 'test', repo: 'test' } }; + const commits = [{ type: 'feat', scope: null, bareMessage: 'test', message: 'feat: test', notes: [], references: [], breaking: false }]; + const calendar = buildVersioningStrategy({ type: 'probe-calver', github }); + assert.equal(calendar.bump(Version.parse('2026.902.0'), commits).toString(), '2026.919.0'); + assert.equal(calendar.bump(Version.parse('2026.919.0'), commits).toString(), '2026.919.1'); + const normal = buildVersioningStrategy({ type: 'default', github }); + assert.equal(normal.bump(Version.parse('1.2.3'), commits).toString(), '1.3.0'); + const plugin = buildPlugin({ type: 'cargo-workspace', github, targetBranch: 'main', repositoryConfig: { + 'crates/probe': { releaseType: 'rust', versioning: 'probe-calver' }, + 'crates/library': { releaseType: 'rust' }, + } }); + assert.equal(plugin.bumpVersion({ path: 'crates/probe', version: '2026.902.0' }).toString(), '2026.919.0'); + assert.equal(plugin.bumpVersion({ path: 'crates/library', version: '1.2.3' }).toString(), '1.2.4'); + const separate = buildPlugin({ type: { type: 'cargo-workspace', merge: false }, github, targetBranch: 'main', repositoryConfig: {} }); + assert.equal(separate.merge, false); +}); + +test('release phase precedes fresh proposal loading and errors are not swallowed', async () => { + const events = []; + const load = async () => { + events.push('load'); + return { createReleases: async () => { events.push('release'); return [undefined, { path: 'crate', tagName: 'tag' }]; }, + createPullRequests: async () => { events.push('pr'); return [undefined, { number: 1 }]; } }; + }; + const result = await releaseAndPropose({}, load); + assert.deepEqual(events, ['load', 'release', 'load', 'pr']); + assert.equal(result.releases.length, 1); + assert.equal(result.prs.length, 1); + await assert.rejects(releaseAndPropose({}, async () => { throw new Error('API failed'); }), /API failed/); +}); + +test('real Rust strategy updates binary-only manifests without changing distribution or tag names', async () => { + const fs = require('node:fs'); + const path = require('node:path'); + const toml = require('smol-toml'); + const { buildStrategy } = require('release-please/build/src/factory.js'); + const { parseConventionalCommits } = require('release-please/build/src/commit.js'); + const { TagName } = require('release-please/build/src/util/tag-name.js'); + const root = path.resolve(__dirname, '../..'); + const settings = JSON.parse(fs.readFileSync(path.join(root, 'release-please-config.json'), 'utf8')); + const versions = JSON.parse(fs.readFileSync(path.join(root, '.release-please-manifest.json'), 'utf8')); + const packages = require('./binary-packages.json'); + registerCalendarVersions(new Date('2026-09-19T00:00:00Z')); + const github = { repository: { owner: 'example', repo: 'probes', defaultBranch: 'main' }, + getFileContentsOnBranch: async filename => ({ parsedContent: fs.readFileSync(path.join(root, filename), 'utf8') }) }; + for (const [directory, spec] of Object.entries(packages)) { + const strategy = await buildStrategy({ github, targetBranch: 'main', path: directory, releaseType: 'rust', + component: spec.component, packageName: spec.package, includeComponentInTag: true, + versioning: settings.packages[directory].versioning }); + const previous = { tag: new TagName(Version.parse(versions[directory]), spec.component), sha: 'a'.repeat(40), notes: '' }; + const commits = parseConventionalCommits([{ sha: 'b'.repeat(40), message: 'feat: ship probe binaries', files: [`${directory}/Cargo.toml`] }]); + assert.equal(await strategy.buildReleasePullRequest([], previous), undefined); + assert.equal(await strategy.buildReleasePullRequest(parseConventionalCommits([{ sha: 'c'.repeat(40), message: 'chore: metadata only', files: [] }]), previous), undefined); + const candidate = await strategy.buildReleasePullRequest(commits, previous); + assert(candidate, 'publish=false binary crate must get a release PR'); + const expected = nextCalendarVersion(versions[directory], new Date('2026-09-19T00:00:00Z')); + assert.equal(candidate.version.toString(), expected); + const update = candidate.updates.find(update => update.path === `${directory}/Cargo.toml`); + assert(update); + const manifest = toml.parse(update.updater.updateContent(fs.readFileSync(path.join(root, update.path), 'utf8'))); + assert.equal(manifest.package.version, expected); + assert.equal(manifest.package.publish, false); + assert.equal((await strategy.getComponent()), spec.component); + assert.equal(new TagName(candidate.version, await strategy.getComponent()).toString(), `${spec.component}-v${expected}`); + } +}); + +test('release entry point rejects pull requests and manual dispatch before API work', () => { + const { spawnSync } = require('node:child_process'); + const path = require('node:path'); + for (const event of ['pull_request', 'workflow_dispatch']) { + const result = spawnSync(process.execPath, [path.join(__dirname, 'release.cjs')], { + encoding: 'utf8', env: { ...process.env, GITHUB_EVENT_NAME: event, GITHUB_REF: 'refs/heads/main', RELEASE_PLEASE_TOKEN: '' }, + }); + assert.notEqual(result.status, 0); + assert.match(result.stdout + result.stderr, /only run on a push to main/); + } +}); + +test('a library release propagates calendar bumps to binary dependents and the root lockfile', async () => { + const fs = require('node:fs'); + const path = require('node:path'); + const toml = require('smol-toml'); + const { buildStrategy } = require('release-please/build/src/factory.js'); + const { parseConventionalCommits } = require('release-please/build/src/commit.js'); + const { TagName } = require('release-please/build/src/util/tag-name.js'); + const root = path.resolve(__dirname, '../..'); + const read = filename => fs.readFileSync(path.join(root, filename), 'utf8'); + const packages = require('./binary-packages.json'); + const library = 'crates/windows-topology-sys'; + const members = [library, ...Object.keys(packages)]; + const settings = JSON.parse(read('release-please-config.json')); + const versions = JSON.parse(read('.release-please-manifest.json')); + registerCalendarVersions(new Date('2026-09-19T00:00:00Z')); + const github = { repository: { owner: 'example', repo: 'probes', defaultBranch: 'main' }, + findFilesByGlobAndRef: async member => [member], + getFileContentsOnBranch: async filename => ({ parsedContent: filename === 'Cargo.toml' + ? `[workspace]\nmembers = ${JSON.stringify(members)}\n` : read(filename) }) }; + const repositoryConfig = {}; + const strategies = {}; + const releases = {}; + for (const directory of members) { + const config = settings.packages[directory]; + repositoryConfig[directory] = { releaseType: 'rust', component: config.component || config['package-name'], + packageName: config['package-name'], versioning: config.versioning || 'default', includeComponentInTag: true }; + strategies[directory] = await buildStrategy({ ...repositoryConfig[directory], github, targetBranch: 'main', path: directory }); + releases[directory] = { tag: new TagName(Version.parse(versions[directory]), repositoryConfig[directory].component), sha: 'a'.repeat(40), notes: '' }; + } + const plugin = buildPlugin({ type: 'cargo-workspace', github, targetBranch: 'main', repositoryConfig }); + await plugin.preconfigure(strategies, {}, releases); + assert.deepEqual(await plugin.run([]), []); + const commits = parseConventionalCommits([{ sha: 'b'.repeat(40), message: 'fix: topology discovery', files: [`${library}/src/lib.rs`] }]); + const pullRequest = await strategies[library].buildReleasePullRequest(commits, releases[library]); + const candidates = await plugin.run([{ path: library, config: repositoryConfig[library], pullRequest }]); + assert.equal(candidates.length, 1); + const updates = candidates[0].pullRequest.updates; + const updated = filename => updates.filter(update => update.path === filename).reduce((content, update) => update.updater.updateContent(content), read(filename)); + for (const [directory, spec] of Object.entries(packages)) { + const expected = nextCalendarVersion(versions[directory], new Date('2026-09-19T00:00:00Z')); + assert.equal(toml.parse(updated(`${directory}/Cargo.toml`)).package.version, expected); + assert.equal(JSON.parse(updated('.release-please-manifest.json'))[directory], expected); + assert.equal(toml.parse(updated('Cargo.lock')).package.find(pkg => pkg.name === spec.package).version, expected); + assert(candidates[0].pullRequest.body.releaseData.some(release => release.component === spec.component)); + } +}); \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 786565a2c..3d399375e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,17 @@ env: # `wtf-string` is portable, so it is additionally built, tested, and linted on # Linux and macOS (the `portable` job) to keep its non-Windows support verified. jobs: + probe-release-tests: + name: probe release policy and packaging tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v6 + with: + node-version: '24' + - run: npm ci --prefix .github/release + - run: npm test --prefix .github/release + encoding: name: encoding sanity check runs-on: windows-latest @@ -45,6 +56,10 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v7 + - uses: actions/setup-node@v6 + with: + node-version: '24' + - run: npm ci --prefix .github/release - name: Run check-publishable.ps1 shell: pwsh run: ./tools/check-publishable.ps1 diff --git a/.github/workflows/release-placement-probe.yml b/.github/workflows/release-placement-probe.yml index c228449fd..9ad45952b 100644 --- a/.github/workflows/release-placement-probe.yml +++ b/.github/workflows/release-placement-probe.yml @@ -12,10 +12,7 @@ on: - 'placement-probe-v*' # **A pull request touching the tool builds and verifies it, without # releasing.** This is what makes the release path exercised rather than - # hoped for, and it matters most for the `aarch64` artifact: `ci.yml` - # cross-compiles only `thumbv7em` for `wtf-string`, so nothing else in this - # repository would ever build the ARM64 target before a tag turned a failure - # into a broken release. + # hoped for, including the cross-compiled `aarch64` artifact. # # It also covers a trap in the alternative. `workflow_dispatch` is only # offered for workflows that already exist on the **default branch**, so a @@ -34,8 +31,7 @@ on: # true because of that. pull_request: # **The dependencies are in this filter for a reason specific to ARM64.** - # This is the only workflow in the repository that builds - # `aarch64-pc-windows-msvc`, and `windows-placement-probe` compiles both + # This workflow builds `aarch64-pc-windows-msvc`, and the tool compiles both # path dependencies into itself. Filtering on this crate's own directory # alone meant a change to either dependency was exercised on x64 by `ci.yml` # and on no other architecture at all, so an architecture-specific @@ -50,6 +46,8 @@ on: - 'Cargo.toml' - 'Cargo.lock' - 'rust-toolchain.toml' + - '.github/release/**' + - 'release-please-config.json' # Kept for a re-run after merge, when the file does live on the default # branch. Build-and-verify-only -- but **that is enforced below rather than # inherent**, and an earlier revision of this comment claimed otherwise. @@ -245,6 +243,7 @@ jobs: steps: - uses: actions/download-artifact@v8 with: + pattern: placement-probe-* path: artifacts merge-multiple: true @@ -319,11 +318,7 @@ jobs: GH_TOKEN: ${{ github.token }} run: | if gh release view "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then - echo "Release ${GITHUB_REF_NAME} already exists; updating its notes and assets." - gh release edit "${GITHUB_REF_NAME}" \ - --repo "${GITHUB_REPOSITORY}" \ - --title "windows-placement-probe ${GITHUB_REF_NAME##*-v}" \ - --notes-file notes.md + echo "Release ${GITHUB_REF_NAME} already exists; preserving its release-please notes." else gh release create "${GITHUB_REF_NAME}" \ --repo "${GITHUB_REPOSITORY}" \ diff --git a/.github/workflows/release-platform-probes.yml b/.github/workflows/release-platform-probes.yml new file mode 100644 index 000000000..bf2e1b9d1 --- /dev/null +++ b/.github/workflows/release-platform-probes.yml @@ -0,0 +1,88 @@ +# Copyright (c) Mike Grier. +name: release-platform-probes + +on: + push: + tags: + - 'windows-platform-probes-v*' + pull_request: + paths: + - 'crates/**' + - '.github/release/**' + - '.github/workflows/release-platform-probes.yml' + - 'release-please-config.json' + - 'Cargo.toml' + - 'Cargo.lock' + - 'rust-toolchain.toml' + workflow_dispatch: + +permissions: + contents: read + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true' + +jobs: + build: + name: platform probes ${{ matrix.target }} + runs-on: windows-latest + env: + RUSTUP_TOOLCHAIN: stable + strategy: + fail-fast: false + matrix: + target: + - x86_64-pc-windows-msvc + - aarch64-pc-windows-msvc + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v6 + with: + node-version: '24' + - uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + - run: npm ci --prefix .github/release + - name: Build and verify archive + shell: pwsh + env: + PROBE_TARGET: ${{ matrix.target }} + RELEASE_TAG: ${{ github.ref_name }} + IS_RELEASE: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/windows-platform-probes-v') }} + run: | + $arguments = @('.github/release/package-probes.cjs', 'windows-platform-probes', $env:PROBE_TARGET, '.scratch/probe-dist') + if ($env:IS_RELEASE -eq 'true') { $arguments += $env:RELEASE_TAG } + node @arguments + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + - uses: actions/upload-artifact@v7 + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/windows-platform-probes-v') + with: + name: platform-probes-${{ matrix.target }} + path: .scratch/probe-dist/* + if-no-files-found: error + + release: + needs: build + runs-on: ubuntu-latest + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/windows-platform-probes-v') + permissions: + contents: write + id-token: write + attestations: write + steps: + - uses: actions/download-artifact@v8 + with: + pattern: platform-probes-* + path: artifacts + merge-multiple: true + - name: Attest the archives + uses: actions/attest-build-provenance@v4 + with: + subject-path: artifacts/windows-platform-probes-*.zip + - name: Attach to the release + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release view "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" + gh release upload "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" \ + --clobber artifacts/windows-platform-probes-*.zip artifacts/windows-platform-probes-*.sha256 \ No newline at end of file diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 3ebaa995c..a0ad2df4a 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -23,9 +23,14 @@ jobs: release_created: ${{ steps.release.outputs.release_created }} pr: ${{ steps.release.outputs.pr }} steps: - - uses: googleapis/release-please-action@v5 - id: release + - uses: actions/checkout@v7 + - uses: actions/setup-node@v6 with: - token: ${{ secrets.RELEASE_PLEASE_TOKEN }} - config-file: release-please-config.json - manifest-file: .release-please-manifest.json + node-version: '24' + - run: npm ci --prefix .github/release + - run: npm test --prefix .github/release + - name: Create releases and release PRs + id: release + env: + RELEASE_PLEASE_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN }} + run: node .github/release/release.cjs diff --git a/.gitignore b/.gitignore index f1a2d501e..2c6c0747b 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ target # Scratch / diagnostic output (git-ignored per repo instructions) .scratch/ +.github/release/node_modules/ # windows-placement-probe writes its backup record into the working directory, # so running the tool from inside a checkout leaves one of these behind. Ignored diff --git a/.release-please-manifest.json b/.release-please-manifest.json index cba4295b3..a9d33913f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,5 @@ { + "crates/windows-placement-probe": "2026.902.0", "crates/windows-file-enumeration-sys": "0.1.1", "crates/windows-file-watcher": "0.2.0", "crates/windows-file-watcher-example-test-harness": "0.1.3", diff --git a/COMPLETED-CHECKLIST.md b/COMPLETED-CHECKLIST.md index 0e836c631..bfc4e595e 100644 --- a/COMPLETED-CHECKLIST.md +++ b/COMPLETED-CHECKLIST.md @@ -3855,3 +3855,28 @@ M26.5 only to fix one technical premise inside it. Migrating roughly 400 lines o bookkeeping through it would bury the change it exists to make. The migration is mechanical, is its own commit, and needs the group headings dated per the archive format -- date-only on the `## Moved` line, with any precise timestamp reserved for an anchored item heading. + +## Moved 2026-09-19 14:29:03 -07:00 -- PB: automated probe binary releases + +### PB-1 -- Automate both probe binary releases through CI and release-please. *(completed 2026-09-19 14:29:03 UTC-07:00)* + +- [x] Preserve calendar versions and placement-probe's existing tag/asset + provenance. Keep both packages off crates.io. Build x64 and ARM64 + default-feature binaries, package every declared platform-probe executable, + attach attestations to release assets, and keep PR/manual validation unable + to publish. Distinguish binary-only releases in publication checks; test + calendar/dependency bumps, workflow guards, complete packaging and version/tag + consistency. Update the distribution docs and preserve ordinary library + release behavior. + +Decision: [DESIGN-NOTES.md](DESIGN-NOTES.md#d-probe-releases) -> `D-PB`. +Implementation and operating commands: +[DEVELOPMENT.md](DEVELOPMENT.md#release-process). + +Verified locally: locked npm installation and release-tooling tests, real Rust +release candidates and dependency-triggered lockfile updates, both platform +archive architectures, both probe test suites and doctest phases, Rust formatting +and Clippy, default-workspace debug/release checks, publication routes, workflow +references and text encoding. The debug build reported non-failing incremental +cache access notes. Hosted upload permissions and attestation issuance await the +tag-push workflow after merge; no release or tag was created locally. diff --git a/COMPLETED-PLANS.md b/COMPLETED-PLANS.md index cca65272d..5c89bc90b 100644 --- a/COMPLETED-PLANS.md +++ b/COMPLETED-PLANS.md @@ -25,3 +25,4 @@ and in [crates/windows-threadpool-sys/COMPLETED-CHECKLIST.md](crates/windows-thr | [CHECKLIST-review-baseline.md](COMPLETED-CHECKLIST.md#checklist-review-baseline) (deleted on completion; the link opens its archived entry) | 2026-08-28 | M1 automated-reviewer language baseline: closed the gap that let an automated review of PR #46 raise seven false "`size_of` is not in scope, this will not compile" findings. The reviewer was reasoning correctly from evidence stacked against it -- the edition and MSRV are structurally invisible in a diff (the toolchain pin never appears, the root manifest's `[workspace.package]` table fell six lines outside the only hunk, and the new crate manifests carry `edition.workspace = true`, a pointer to a table in no hunk), while the workspace's own pre-1.80 `size_of` sites supplied genuine in-repo precedent for the wrong reading. Created the `.github/instructions/global.rust.instructions.md` the root instructions had been citing for a file that did not exist (RB-1), stated the baseline first in `.github/copilot-instructions.md` where a reviewer reads it (RB-2), normalised seven contradicting call sites across three crates (RB-3), and added `tools/check-baseline.ps1` plus a CI job asserting twelve restatements across six files against the manifests, sabotage-verified in five ways (RB-4). Validated empirically: re-running the same reviewer on the same PR went from 7 comments to 0, with the `size_of` claim absent. Feature-scoped file deleted on completion. | [DESIGN-NOTES.md](DESIGN-NOTES.md#restatement-drift); [DESIGN-RATIONALE.md](DESIGN-RATIONALE.md) | | [crates/windows-impersonation-token-sys/CHECKLIST-mutant-tests.md](crates/windows-impersonation-token-sys/COMPLETED-CHECKLIST.md#mt-1) (deleted on completion; the link opens its archived entry) | 2026-09-01 | Added unit tests for both actionable mutation-test survivors in `windows-impersonation-token-sys` and recorded why the disjoint access-mask operator mutation is behaviorally equivalent. | N/A | | [crates/windows-platform-probes/CHECKLIST.md](crates/windows-platform-probes/CHECKLIST.md) | 2026-09-09 | M1: every probe streams its report as it measures, so a run killed partway keeps what it had already established; the buffering `catch_unwind` pair is gone with the buffer. M2: the report's parts are checked against *each other*, the defect class that a 180/0 mutation score and 28 rounds of per-artifact review both passed over, because each part was locally correct and the contradiction lived between two of them. An oracle reads the rendered artifact and relates only claims already visible in it; the topology banner is built from the read the body describes; both cost probes derive prose and NDJSON from one source; `GetFullPathNameW` is described accurately in the crate that owns it; and every CI probe step is gated on the build so diagnostics survive a failing test. | [crates/windows-platform-probes/DESIGN-NOTES.md](crates/windows-platform-probes/DESIGN-NOTES.md#d-correspondence-failures) | +| [CHECKLIST.md](COMPLETED-CHECKLIST.md#pb-1) -> `PB-1` | 2026-09-19 14:29:03 -07:00 | Automated calendar-versioned probe binary releases, architecture-checked platform ZIPs, guarded attestation/upload jobs, and release-route checks. Local gates pass; hosted publication follows merge and the release PR. | [DESIGN-NOTES.md](DESIGN-NOTES.md#d-probe-releases) | diff --git a/DESIGN-NOTES.md b/DESIGN-NOTES.md index 185cb6f09..de50c2736 100644 --- a/DESIGN-NOTES.md +++ b/DESIGN-NOTES.md @@ -1,5 +1,35 @@ # Design notes +## D-PB: probe binary releases use release-please + +Both probe packages are release-managed binary distributions, not registry +packages. `publish = false` and path-only workspace dependencies remain in place. +The route registry is +[binary-packages.json](.github/release/binary-packages.json); build and upload +contracts are checked against the parsed manifests and workflows in CI. + +Versions use UTC `YYYY.MMDD.N` at proposal time. On the same or an earlier +clock date, increment the previous counter; on a later date, reset it to zero. +The platform package's legacy `0.x.y` baseline starts with today's date. The +wrapper extends both direct and dependency-triggered probe bumps while retaining +release-please's Rust manifest, lockfile, changelog and library-version behavior. +The record schema remains separately versioned. + +Keep the placement executable assets and tag prefix. Platform probes ship one +ZIP per Windows architecture, containing all binary targets derived from Cargo +metadata and compiler artifacts, plus documentation and build identity. Verify +the PE machine of each executable and reject the renderer's test-only oracle +feature. Both paths build x64 and ARM64 with default features. + +Only tag-push runs upload build artifacts and attach attested assets to releases. +PR and manual runs are build-only, including dispatches against a tag. Preserve +the release-please changelog when attaching assets. Release-please uses the +repository token that can trigger downstream workflows, not `GITHUB_TOKEN`. + +Implementation: [COMPLETED-CHECKLIST.md](COMPLETED-CHECKLIST.md#pb-1) -> `PB-1`. +Rationale: [DESIGN-RATIONALE.md](DESIGN-RATIONALE.md#probe-release-automation). +Operations: [DEVELOPMENT.md](DEVELOPMENT.md#release-process). + ## Founding theme This repository exists to enable asynchronous Windows code in Rust that uses native diff --git a/DESIGN-RATIONALE.md b/DESIGN-RATIONALE.md index 5b35c885a..3b38d8de8 100644 --- a/DESIGN-RATIONALE.md +++ b/DESIGN-RATIONALE.md @@ -5,6 +5,34 @@ This file records why the cross-component decisions in [design-sessions/DESIGN-SESSION-2026-08-27-async-file-enumeration.md](design-sessions/DESIGN-SESSION-2026-08-27-async-file-enumeration.md). Tier 1 remains authoritative. +## Probe release automation + +For [DESIGN-NOTES.md](DESIGN-NOTES.md#d-probe-releases) -> `D-PB`. + +The placement workflow already built and attested release executables, but +release-please did not manage its package. Platform probes had neither a managed +release route nor binary packaging. The earlier "never distributed" decision +would require every outside measurement to begin with a source checkout. + +The engineer chose calendar versions for both probes. Replacing placement's +version with semver would change a deliberate build-identity convention. A +pinned release-please wrapper provides that version policy while keeping the +existing Rust release implementation. The Cargo workspace plugin has its own +dependency-only bump path, so extending only the direct version strategy would +leave probe versions subject to ordinary patch bumps on that path. + +A metadata-derived archive avoids a second list of executable names. Checking +both the target list and emitted artifact list detects omitted builds as well +as extra binaries; checking PE headers also observes the architecture of the +bytes that will be attached. Packaging does not execute the platform experiments: +some mutate process-wide state or deliberately wait indefinitely. + +The placement workflow's existing publication guards apply to the new route +too. GitHub attestations authenticate the artifact bytes; environment-derived +build labels do not. Local tests exercise real Rust release updates and ZIP +round trips; hosted token permissions and attestation issuance are exercised +only by the eventual release workflow. + ## Why captured impersonation is its own crate The enumeration open must occur asynchronously, but directory access is determined diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index b9f7181c6..2c0913e08 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -56,10 +56,58 @@ override, since it is meant to run the pinned version. ## Release process `release-please` owns version changes, tags, and changelog updates. Each crate -is versioned and released independently; a `-v` tag (for example +is versioned and released independently. For registry crates, a `-v` tag (for example `windows-overlapped-io-sys-v0.1.0`) triggers the crates.io publish workflow after verifying that the tag matches that crate's package version. +The probes are binary-only releases, never crates.io packages. Their routes are +declared in [.github/release/binary-packages.json](.github/release/binary-packages.json): + +| Package | Release tag | Assets | +|---|---|---| +| `windows-placement-probe` | `placement-probe-v` | `placement-probe-x86_64.exe`, `placement-probe-aarch64.exe` | +| `windows-platform-probes` | `windows-platform-probes-v` | `windows-platform-probes-x86_64.zip`, `windows-platform-probes-aarch64.zip`, SHA-256 sidecars | + +Both use UTC calendar versions, `YYYY.MMDD.N`, assigned when release-please +proposes an update. The date need not be the date the release PR is merged. +Same-day updates increment `N`; a clock behind the previous release increments +its counter rather than decreasing the version. The platform package's legacy +`0.0.1` baseline transitions on its first automated release. Record schema +versions remain independent of these package versions. + +[release.cjs](.github/release/release.cjs) extends the pinned release-please +versioning and Cargo workspace plugin for these packages; library versioning +continues to use semver. A release PR updates manifests, lockfile and changelogs. +Merging it creates the releases and tags. Tag pushes then build with default +features, verify versions, and attach attested binaries without replacing the +release-please notes. Platform ZIPs contain every Cargo binary target plus the +README, license and build identity. The test-only `oracle-in-renderer` feature +is rejected by the packager. + +PR and `workflow_dispatch` runs validate builds only, even when a dispatch +names a tag. They upload no binary artifacts and mint no attestations. Retry +a failed publication by re-running its original tag-push workflow. Platform +publication requires the release-please release to exist; it does not create +one from a manual tag. + +Release-tooling checks require Node 24 and the committed npm lockfile: + +```text +npm ci --prefix .github/release +npm test --prefix .github/release +pwsh -File tools/check-publishable.ps1 +``` + +The two binary workflows also build both Windows architectures on relevant PRs. +Local archive verification uses the same builder, for example: + +```text +node .github/release/package-probes.cjs windows-platform-probes x86_64-pc-windows-msvc .scratch/probe-dist +``` + +The destination must be a new directory under `.scratch/`. Local builds do not +publish or attest anything; the embedded `ci` stamp alone is not authentication. + Some crates depend on workspace siblings by version as well as by path, so `cargo publish`'s build verification resolves those dependencies from crates.io rather than the workspace checkout: `windows-threadpool-sys` depends on @@ -81,5 +129,7 @@ check is a no-op for a crate with no workspace-sibling dependencies Publishing requires these repository secrets: - `RELEASE_PLEASE_TOKEN` for release pull requests, tags, and follow-up workflow - runs. + runs. It must have repository contents and pull-request write permission; + the default `GITHUB_TOKEN` cannot replace it because its tag pushes do not + trigger the publishing workflows. - `CARGO_REGISTRY_TOKEN` with crates.io publish permission. diff --git a/crates/windows-placement-probe/DESIGN-NOTES.md b/crates/windows-placement-probe/DESIGN-NOTES.md index 73f541c84..86ec96eb1 100644 --- a/crates/windows-placement-probe/DESIGN-NOTES.md +++ b/crates/windows-placement-probe/DESIGN-NOTES.md @@ -121,14 +121,12 @@ discussion thread is from last week or from last year. `0.1.0` answered neither, and would have gone on answering neither indefinitely, because nothing would ever have forced it to move. -**Nothing automated fights this.** Release-please does not manage this crate -- -it is absent from both `release-please-config.json`'s `packages` map and -`.release-please-manifest.json`, and carries no `x-release-please-version` -marker -- so the version is maintained by hand, which is the only way a date -could be correct anyway. The release workflow's tag check needed no change: it -parses `${GITHUB_REF_NAME##*-v}`, which yields `2026.902.0` from -`placement-probe-v2026.902.0`, and still rejects a stale tag. Verified against -the workflow's own logic rather than assumed. +**Release-please now assigns this calendar version**, replacing hand-maintained +bumps. The date is the UTC proposal date, which can precede the release-PR merge. +The shared automation rule is in the root +[DESIGN-NOTES.md](../../DESIGN-NOTES.md#d-probe-releases) -> `D-PB`. +The tag prefix remains `placement-probe-v`, and the release workflow compares +its version against the built executable before publication. ## The schema freezes at the first release, not before diff --git a/crates/windows-placement-probe/README.md b/crates/windows-placement-probe/README.md index c883f0643..e5114eaa4 100644 --- a/crates/windows-placement-probe/README.md +++ b/crates/windows-placement-probe/README.md @@ -27,7 +27,7 @@ here can. ## Running it Download the binary for your architecture from the -[latest release](https://github.com/MikeGrier/windows-threadpool-sys/releases), +[placement-probe releases](https://github.com/MikeGrier/windows-threadpool-sys/releases?q=placement-probe-v), then: ```text @@ -35,6 +35,12 @@ placement-probe --preview see exactly what it collects, measure nothing placement-probe measure, and print a result to paste ``` +Release-please manages the calendar version and CI attaches the x64 and ARM64 +executables. The downloaded filenames include their architecture; rename the +chosen executable to `placement-probe.exe` for the commands shown here, or use +its full filename. See the repository's +[release process](../../DEVELOPMENT.md#release-process). + By default a result carries the machine's **shape and its timings and nothing else**. `--include-metadata` adds the context described below, and is worth reading about before you decide either way. diff --git a/crates/windows-platform-probes/CHECKLIST.md b/crates/windows-platform-probes/CHECKLIST.md index 2aa865987..03db8e2b9 100644 --- a/crates/windows-platform-probes/CHECKLIST.md +++ b/crates/windows-platform-probes/CHECKLIST.md @@ -240,10 +240,10 @@ correctness in the archive. both directions. **One correction to this item as written.** The blocker recorded when queuing was **wrong** -- - `windows-placement-probe` is `publish = false` and absent from - [.release-please-manifest.json](../../.release-please-manifest.json), and - [check-commit-scope.ps1](../../tools/check-commit-scope.ps1) says in terms that such a crate - "cannot be poisoned, because it is never released -- so it is not a finding." The `x-probe-*` row + `windows-placement-probe` was absent from release-please configuration at the + time. It is now a managed binary release under the root + [DESIGN-NOTES.md](../../DESIGN-NOTES.md#d-probe-releases) -> `D-PB`; `publish = false` + prohibits crates.io publication, not binary releases. The `x-probe-*` row it called for is deferred to `M4.10`, which turned out to be a larger question than these two probes. diff --git a/crates/windows-platform-probes/Cargo.toml b/crates/windows-platform-probes/Cargo.toml index 487a58ca4..a25aa8680 100644 --- a/crates/windows-platform-probes/Cargo.toml +++ b/crates/windows-platform-probes/Cargo.toml @@ -130,10 +130,9 @@ required-features = ["oracle-in-renderer"] # carries the gate, which is also the clearest statement of what that module is. serde_json = { version = "1.0", optional = true } # **Every workspace dependency below is path-only, with no `version`**, because -# this crate is never distributed at all -- not to a registry, and not as a -# released binary either, unlike `windows-placement-probe` next door. These -# probes are a development instrument, run from a checkout, and `publish = false` -# above is the whole story. +# released binaries are built from the workspace checkout, not cargo-packaged +# for a registry. `publish = false` prevents registry publication; it does not +# prevent the release workflow from distributing the compiled experiments. # # A `version` beside a `path` is consulted only when the depending crate is # packaged, but cargo still requires the path crate's own version to satisfy it diff --git a/crates/windows-platform-probes/DESIGN-NOTES.md b/crates/windows-platform-probes/DESIGN-NOTES.md index 37758b9f2..06c363a57 100644 --- a/crates/windows-platform-probes/DESIGN-NOTES.md +++ b/crates/windows-platform-probes/DESIGN-NOTES.md @@ -273,8 +273,9 @@ restores it afterwards has only narrowed the window, not acquired the right. This crate does it anyway, and the tension is resolved by what this crate *is*. It is an experiment for discovering platform behaviour, not a component: the only way to learn whether the thread mode is a view of the process mode is to move the -process mode and look. `publish = false` and version `0.0.0` are the enforcement --- nothing ships this, and nothing outside the workspace can depend on it. The +process mode and look. `publish = false` prevents registry publication, while +standalone binaries are distributed under the root +[DESIGN-NOTES.md](../../DESIGN-NOTES.md#d-probe-releases) -> `D-PB`. The call site says the same thing in its own rustdoc, because a reader arriving at the function will not have read this file. @@ -360,17 +361,16 @@ they agree. A probe that grew a hard-coded expectation would be back on the wrong side of the rule above. ## This crate is never distributed, and its dependencies carry no versions +**Superseded in its binary-distribution rule by [D-PB](../../DESIGN-NOTES.md#d-probe-releases); path-only dependencies remain.** -Not to a registry, and not as a released binary either -- unlike -`windows-placement-probe`, which ships a CI-built binary to people running it on -hardware this workspace does not own. These probes are a development -instrument, run from a checkout by someone who has the checkout. `publish = -false` is the whole story, and it is permanent rather than "not yet". +The earlier checkout-only distribution decision is replaced by CI-built +binary releases. The experiments remain outside the production-library surface, +and `publish = false` still prohibits registry publication. -**The consequence is that every workspace dependency here is path-only.** A +**Every workspace dependency here remains path-only.** A `version` beside a `path` exists to tell a registry what to resolve when the -depending crate is packaged. Nothing packages this crate, so those pins named a -version no one would ever consult -- while still having to be correct, because +depending crate is packaged. Binary builds use the workspace checkout rather +than registry packaging. Such pins would still have to be correct, because cargo requires the path crate's own version to satisfy the pin **at every build**, not merely at publication. diff --git a/crates/windows-platform-probes/README.md b/crates/windows-platform-probes/README.md index bd93f7d48..a48a3c38a 100644 --- a/crates/windows-platform-probes/README.md +++ b/crates/windows-platform-probes/README.md @@ -4,8 +4,8 @@ Executable probes for the Windows behaviour this workspace's designs rest on -- supported, documented APIs, in the specific cases their documentation does not describe. -**Windows only. Not published** -- this crate exists to keep measurements honest, -not to be depended on. +**Windows only. Binary releases, not a crates.io library** -- this crate exists +to keep measurements honest, not to be depended on. **An experiment, not a component.** These probes measure platform behaviour and are not for production use: that scope is what lets one do things a shipping @@ -62,7 +62,30 @@ would make the test a check of the copy rather than of the platform. Every tier is **compiled** by an ordinary workspace build. That is the floor: a probe that no longer compiles has already rotted. -## Running +## Downloads + +Download the x64 or ARM64 ZIP from the +[platform-probe releases](https://github.com/MikeGrier/windows-threadpool-sys/releases?q=windows-platform-probes-v). +The archive contains the probe executables, this README, the license, and a +build-identity JSON file naming the version, architecture and source commit. +The SHA-256 sidecar detects a changed download; the GitHub attestation verifies +the archive's provenance. For the x64 archive: + +```text +gh attestation verify windows-platform-probes-x86_64.zip --repo MikeGrier/windows-threadpool-sys +``` + +ARM64 uses the corresponding `aarch64` filename. These are not Authenticode-signed +executables. Extract the archive and run the particular experiment whose +behavior you intend to measure; some experiments require privileges, change +process-wide state or intentionally hang. The binary-only tier below is not +a batch test suite. + +Release-please assigns UTC calendar versions and CI builds the default features; +the test-only renderer oracle is excluded. See the repository's +[release process](../../DEVELOPMENT.md#release-process). + +## Running From Source ```text cargo test --package windows-platform-probes --features oracle-in-renderer # asserted tier + required-feature integration target diff --git a/crates/windows-platform-probes/src/lib.rs b/crates/windows-platform-probes/src/lib.rs index f5b78634d..3a86cd958 100644 --- a/crates/windows-platform-probes/src/lib.rs +++ b/crates/windows-platform-probes/src/lib.rs @@ -37,10 +37,10 @@ //! # Experiments, not components //! //! Everything here is an **experiment for discovering what Windows does**, and -//! none of it is for production use. The crate is `publish = false` at version -//! `0.0.0`; nothing ships it and nothing outside this workspace can depend on -//! it. That is not modesty about maturity -- it is the boundary that lets a -//! probe do things a component must not. +//! none of it is for production use. The crate is `publish = false`: it is not +//! a registry library. CI distributes standalone experiment binaries, not a +//! production component. That distinction lets a probe do things a component +//! must not. //! //! The clearest case is process-wide state. A probe may set it, because some //! questions cannot be answered without moving it and looking: diff --git a/release-please-config.json b/release-please-config.json index 23467714a..fc19ecb93 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -11,6 +11,16 @@ "cargo-workspace" ], "packages": { + "crates/windows-placement-probe": { + "package-name": "windows-placement-probe", + "component": "placement-probe", + "versioning": "probe-calver" + }, + "crates/windows-platform-probes": { + "package-name": "windows-platform-probes", + "component": "windows-platform-probes", + "versioning": "probe-calver" + }, "crates/windows-file-enumeration-sys": { "package-name": "windows-file-enumeration-sys", "component": "windows-file-enumeration-sys" diff --git a/tools/check-commit-scope.ps1 b/tools/check-commit-scope.ps1 index 1f6f853b5..90db65d10 100644 --- a/tools/check-commit-scope.ps1 +++ b/tools/check-commit-scope.ps1 @@ -58,18 +58,15 @@ Push-Location $repo try { # The crates release-please actually versions -- read from the CONFIG, which is -# the authority for which packages it manages. A `publish = false` crate cannot -# be poisoned, because it is never released, and a crate release-please is not -# configured for is not released whatever its manifest says. +# the authority for which packages it manages. This includes binary-only +# `publish = false` packages; only packages absent from the config are excluded. # # **The manifest is the wrong source, and reading it made this script wrong.** # `.release-please-manifest.json` records the current version of each managed # package, but nothing prunes an entry when a package leaves the config: this -# repository's manifest still carries `crates/windows-platform-probes`, which -# `release-please-config.json` does not manage and whose `Cargo.toml` says -# `publish = false`. Reading the manifest therefore treated that crate as -# released and flagged a commit for mislabelling a changelog entry that could -# never be written. +# repository's manifest previously carried windows-platform-probes while the +# config did not manage it. Reading the manifest therefore treated it as +# released too early. Both probes are now configured binary releases. $configPath = Join-Path $repo 'release-please-config.json' if (-not (Test-Path $configPath)) { throw "No release-please-config.json at $configPath" } $config = Get-Content $configPath -Raw | ConvertFrom-Json diff --git a/tools/check-publishable.ps1 b/tools/check-publishable.ps1 index e09def0ba..1663db0b9 100644 --- a/tools/check-publishable.ps1 +++ b/tools/check-publishable.ps1 @@ -14,10 +14,9 @@ That happened to windows-waitable-queues, and was found by review rather than by any check. This script is the check. - A crate may be deliberately absent from release-please -- windows-placement- - probe ships as a downloadable binary and is not on crates.io yet -- so the - comparison is one-directional: everything release-please manages must be - publishable. The reverse is allowed. + Explicit binary-only routes are checked by the Node release-tooling checker. + Every other managed crate must have a crates.io route. A crate may be + deliberately absent from release-please; the comparison is one-directional. #> [CmdletBinding()] param( @@ -52,6 +51,11 @@ function Write-Report { $configPath = Join-Path $RepositoryRoot 'release-please-config.json' $workflowPath = Join-Path $RepositoryRoot '.github/workflows/publish-crate.yml' +$binaryChecker = Join-Path $RepositoryRoot '.github/release/check-publication.cjs' +node $binaryChecker $RepositoryRoot +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +$binaryPaths = ([IO.File]::ReadAllText((Join-Path $RepositoryRoot '.github/release/binary-packages.json')) | ConvertFrom-Json).PSObject.Properties.Name + foreach ($required in @($configPath, $workflowPath)) { if (-not (Test-Path $required)) { throw "cannot check publication: $required is missing" @@ -59,6 +63,7 @@ foreach ($required in @($configPath, $workflowPath)) { } $managed = (Get-Content $configPath -Raw | ConvertFrom-Json).packages.PSObject.Properties.Name | + Where-Object { $binaryPaths -notcontains $_ } | ForEach-Object { Split-Path $_ -Leaf } | Sort-Object From 97fe94098272975125e1606871b2caa0a625c3c6 Mon Sep 17 00:00:00 2001 From: Mike Grier Date: Sat, 19 Sep 2026 17:56:02 -0400 Subject: [PATCH 2/2] Refine output validation and versioning format Update error handling for output directory validation and clarify versioning format in documentation. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/release/package-probes.cjs | 6 +++++- DESIGN-NOTES.md | 2 +- DEVELOPMENT.md | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/release/package-probes.cjs b/.github/release/package-probes.cjs index 7457331c4..fd369c2a1 100644 --- a/.github/release/package-probes.cjs +++ b/.github/release/package-probes.cjs @@ -53,7 +53,11 @@ function main(args) { if (!entry || !machines[target] || !output) throw new Error('usage: package-probes [tag]'); const root = path.resolve(__dirname, '../..'); const destination = path.resolve(output); - if (!destination.startsWith(path.join(root, '.scratch') + path.sep)) throw new Error('Output must be under .scratch'); + const scratchRoot = path.join(root, '.scratch') + path.sep; + const withinScratch = process.platform === 'win32' + ? destination.toLowerCase().startsWith(scratchRoot.toLowerCase()) + : destination.startsWith(scratchRoot); + if (!withinScratch) throw new Error('Output must be under .scratch'); if (fs.existsSync(destination)) throw new Error('Output directory already exists'); const run = (program, argv, extra = {}) => execFileSync(program, argv, { cwd: root, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024, ...extra }); const metadata = JSON.parse(run('cargo', ['metadata', '--locked', '--no-deps', '--format-version', '1'])); diff --git a/DESIGN-NOTES.md b/DESIGN-NOTES.md index de50c2736..375aa6611 100644 --- a/DESIGN-NOTES.md +++ b/DESIGN-NOTES.md @@ -8,7 +8,7 @@ The route registry is [binary-packages.json](.github/release/binary-packages.json); build and upload contracts are checked against the parsed manifests and workflows in CI. -Versions use UTC `YYYY.MMDD.N` at proposal time. On the same or an earlier +Versions use UTC `YYYY.(MMDD as an integer without zero padding).N` at proposal time. On the same or an earlier clock date, increment the previous counter; on a later date, reset it to zero. The platform package's legacy `0.x.y` baseline starts with today's date. The wrapper extends both direct and dependency-triggered probe bumps while retaining diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 2c0913e08..9df2c89e9 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -68,7 +68,7 @@ declared in [.github/release/binary-packages.json](.github/release/binary-packag | `windows-placement-probe` | `placement-probe-v` | `placement-probe-x86_64.exe`, `placement-probe-aarch64.exe` | | `windows-platform-probes` | `windows-platform-probes-v` | `windows-platform-probes-x86_64.zip`, `windows-platform-probes-aarch64.zip`, SHA-256 sidecars | -Both use UTC calendar versions, `YYYY.MMDD.N`, assigned when release-please +Both use UTC calendar versions, `YYYY.(MMDD as an integer without zero padding).N`, assigned when release-please proposes an update. The date need not be the date the release PR is merged. Same-day updates increment `N`; a clock behind the previous release increments its counter rather than decreasing the version. The platform package's legacy