@1aboveio/skills 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +13 -0
- package/.codex-plugin/resources.json +1827 -0
- package/README.md +22 -0
- package/package.json +9 -2
- package/skills/app-dev/airflow/SKILL.md +1 -1
- package/skills/app-dev/airflow/release-check.mjs +3 -1
- package/skills/app-dev/android-foundation/SKILL.md +92 -34
- package/skills/app-dev/android-foundation/assets/android-ci-job.yml +13 -7
- package/skills/app-dev/android-foundation/references/ci.md +23 -13
- package/skills/app-dev/android-foundation/references/delivery-focus.md +29 -0
- package/skills/app-dev/android-foundation/references/diagnosis.md +136 -0
- package/skills/app-dev/android-foundation/references/gotchas.md +20 -11
- package/skills/app-dev/android-foundation/references/setup.md +15 -6
- package/skills/app-dev/android-foundation/release-check.mjs +3 -1
- package/skills/app-dev/android-foundation/scripts/device-run.sh +148 -42
- package/skills/app-dev/app-debug/SKILL.md +1 -1
- package/skills/app-dev/app-debug/release-check.mjs +3 -1
- package/skills/app-dev/better-auth/SKILL.md +1 -1
- package/skills/app-dev/better-auth/release-check.mjs +3 -1
- package/skills/app-dev/data-catalog/SKILL.md +1 -1
- package/skills/app-dev/data-catalog/release-check.mjs +3 -1
- package/skills/app-dev/data-quality/SKILL.md +1 -1
- package/skills/app-dev/data-quality/release-check.mjs +3 -1
- package/skills/app-dev/ios-foundation/SKILL.md +1 -1
- package/skills/app-dev/ios-foundation/release-check.mjs +3 -1
- package/skills/app-dev/monorepo/SKILL.md +1 -1
- package/skills/app-dev/monorepo/release-check.mjs +3 -1
- package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
- package/skills/app-dev/nextjs-fullstack/release-check.mjs +3 -1
- package/skills/app-dev/prisma-setup/SKILL.md +1 -1
- package/skills/app-dev/prisma-setup/release-check.mjs +3 -1
- package/skills/app-dev/pyspark/SKILL.md +1 -1
- package/skills/app-dev/pyspark/release-check.mjs +3 -1
- package/skills/app-dev/python-backend/SKILL.md +1 -1
- package/skills/app-dev/python-backend/release-check.mjs +3 -1
- package/skills/app-dev/shadcn/SKILL.md +1 -1
- package/skills/app-dev/shadcn/release-check.mjs +3 -1
- package/skills/app-dev/zod-v4/SKILL.md +1 -1
- package/skills/app-dev/zod-v4/release-check.mjs +3 -1
- package/skills/cicd/cloud-build/SKILL.md +1 -1
- package/skills/cicd/cloud-build/release-check.mjs +3 -1
- package/skills/cicd/cloud-debug/SKILL.md +1 -1
- package/skills/cicd/cloud-debug/release-check.mjs +3 -1
- package/skills/cicd/cloud-deploy/SKILL.md +1 -1
- package/skills/cicd/cloud-deploy/release-check.mjs +3 -1
- package/skills/cicd/docker/SKILL.md +1 -1
- package/skills/cicd/docker/release-check.mjs +3 -1
- package/skills/cicd/github-actions/SKILL.md +1 -1
- package/skills/cicd/github-actions/release-check.mjs +3 -1
- package/skills/cicd/github-flow/SKILL.md +1 -1
- package/skills/cicd/github-flow/release-check.mjs +3 -1
- package/skills/cicd/google-cloud/SKILL.md +1 -1
- package/skills/cicd/google-cloud/release-check.mjs +3 -1
- package/skills/cicd/mergify/SKILL.md +1 -1
- package/skills/cicd/mergify/release-check.mjs +3 -1
- package/skills/cicd/podman/SKILL.md +1 -1
- package/skills/cicd/podman/release-check.mjs +3 -1
- package/skills/content/article-review/SKILL.md +1 -1
- package/skills/content/article-review/release-check.mjs +3 -1
- package/skills/content/article-to-image/SKILL.md +1 -1
- package/skills/content/article-to-image/release-check.mjs +3 -1
- package/skills/content/flowchart-svg/SKILL.md +1 -1
- package/skills/content/flowchart-svg/release-check.mjs +3 -1
- package/skills/content/image-gen/SKILL.md +1 -1
- package/skills/content/image-gen/release-check.mjs +3 -1
- package/skills/content/mermaid/SKILL.md +1 -1
- package/skills/content/mermaid/release-check.mjs +3 -1
- package/skills/content/wechat-article-writer/SKILL.md +1 -1
- package/skills/content/wechat-article-writer/release-check.mjs +3 -1
- package/skills/content/wechat-publisher/SKILL.md +1 -1
- package/skills/content/wechat-publisher/release-check.mjs +3 -1
- package/skills/content/wechat-search/SKILL.md +1 -1
- package/skills/content/wechat-search/release-check.mjs +3 -1
- package/skills/content/xai-search/SKILL.md +88 -13
- package/skills/content/xai-search/release-check.mjs +3 -1
- package/skills/content/xai-search/scripts/xai_search.sh +125 -149
- package/skills/core/distill-writing/SKILL.md +1 -1
- package/skills/core/distill-writing/release-check.mjs +3 -1
- package/skills/core/doc-to-md/SKILL.md +1 -1
- package/skills/core/doc-to-md/release-check.mjs +3 -1
- package/skills/core/editorial-review/SKILL.md +1 -1
- package/skills/core/editorial-review/release-check.mjs +3 -1
- package/skills/core/house-style/SKILL.md +1 -1
- package/skills/core/house-style/release-check.mjs +3 -1
- package/skills/core/plain-language/SKILL.md +1 -1
- package/skills/core/plain-language/release-check.mjs +3 -1
- package/skills/core/polish-cn/SKILL.md +1 -1
- package/skills/core/polish-cn/release-check.mjs +3 -1
- package/skills/core/reg-parser/SKILL.md +1 -1
- package/skills/core/reg-parser/release-check.mjs +3 -1
- package/skills/core/terminology/SKILL.md +1 -1
- package/skills/core/terminology/release-check.mjs +3 -1
- package/skills/core/write-doc/SKILL.md +1 -1
- package/skills/core/write-doc/release-check.mjs +3 -1
- package/skills/engineering/better-review/SKILL.md +1 -1
- package/skills/engineering/better-review/release-check.mjs +3 -1
- package/skills/engineering/contributing/SKILL.md +1 -1
- package/skills/engineering/contributing/release-check.mjs +3 -1
- package/skills/engineering/e2e-test/SKILL.md +1 -1
- package/skills/engineering/e2e-test/release-check.mjs +3 -1
- package/skills/engineering/engineering-runtime/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/coherence/workflow.json +13 -13
- package/skills/engineering/engineering-runtime/release-check.mjs +3 -1
- package/skills/engineering/engineering-runtime/scripts/installation-resolver.mjs +191 -0
- package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +51 -10
- package/skills/engineering/ensure-coverage/SKILL.md +1 -1
- package/skills/engineering/ensure-coverage/release-check.mjs +3 -1
- package/skills/engineering/golden-path/SKILL.md +1 -1
- package/skills/engineering/golden-path/release-check.mjs +3 -1
- package/skills/engineering/harness-runtime/SKILL.md +1 -1
- package/skills/engineering/harness-runtime/release-check.mjs +3 -1
- package/skills/engineering/resolve-issues/SKILL.md +11 -20
- package/skills/engineering/resolve-issues/references/delivery-progress.md +63 -0
- package/skills/engineering/resolve-issues/references/expire.md +24 -11
- package/skills/engineering/resolve-issues/references/implementation.md +9 -1
- package/skills/engineering/resolve-issues/references/preflight.md +7 -0
- package/skills/engineering/resolve-issues/references/shared-modules.md +27 -0
- package/skills/engineering/resolve-issues/references/task-plan.md +12 -5
- package/skills/engineering/resolve-issues/release-check.mjs +3 -1
- package/skills/engineering/resolve-issues/scripts/active-installation.mjs +32 -0
- package/skills/engineering/resolve-issues/scripts/install-hooks.mjs +3 -0
- package/skills/engineering/resolve-issues/scripts/rehydrate.mjs +17 -1
- package/skills/engineering/resolve-release/SKILL.md +1 -1
- package/skills/engineering/resolve-release/release-check.mjs +3 -1
- package/skills/engineering/rush-issues/SKILL.md +5 -2
- package/skills/engineering/rush-issues/release-check.mjs +3 -1
- package/skills/engineering/rush-release/SKILL.md +1 -1
- package/skills/engineering/rush-release/release-check.mjs +3 -1
- package/skills/engineering/test-quality/SKILL.md +1 -1
- package/skills/engineering/test-quality/release-check.mjs +3 -1
- package/skills/risk-compliance/compliance-audit/SKILL.md +1 -1
- package/skills/risk-compliance/compliance-audit/release-check.mjs +3 -1
- package/skills/risk-compliance/contract-compare/SKILL.md +1 -1
- package/skills/risk-compliance/contract-compare/release-check.mjs +3 -1
- package/skills/risk-compliance/contract-review/SKILL.md +1 -1
- package/skills/risk-compliance/contract-review/release-check.mjs +3 -1
- package/skills/risk-compliance/doc-authenticity/SKILL.md +1 -1
- package/skills/risk-compliance/doc-authenticity/release-check.mjs +3 -1
- package/skills/risk-compliance/fraud-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/fraud-analysis/release-check.mjs +3 -1
- package/skills/risk-compliance/payment-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/payment-analysis/release-check.mjs +3 -1
- package/skills/risk-compliance/similar-domain/SKILL.md +1 -1
- package/skills/risk-compliance/similar-domain/release-check.mjs +3 -1
- package/skills/risk-compliance/tld-detection/SKILL.md +1 -1
- package/skills/risk-compliance/tld-detection/release-check.mjs +3 -1
- package/skills/app-dev/android-foundation/scripts/device-run.test.mjs +0 -172
- package/skills/app-dev/data-catalog/scripts/data-catalog.test.mjs +0 -36
- package/skills/app-dev/data-quality/scripts/data-quality.test.mjs +0 -12
- package/skills/cicd/github-actions/scripts/ci-run-audit.test.mjs +0 -637
- package/skills/cicd/github-flow/scripts/flow.test.mjs +0 -114
- package/skills/cicd/github-flow/scripts/persist-conventions.test.mjs +0 -108
- package/skills/cicd/mergify/scripts/audit.test.mjs +0 -201
- package/skills/cicd/mergify/scripts/entrypoints.test.mjs +0 -106
- package/skills/cicd/mergify/scripts/pr-620-mergify-comments.fixture.json +0 -30
- package/skills/cicd/mergify/scripts/stack-push.test.mjs +0 -233
- package/skills/cicd/mergify/scripts/watch-pr-delivery.test.mjs +0 -1826
- package/skills/content/image-gen/scripts/credentials.test.mjs +0 -212
- package/skills/content/image-gen/scripts/options.test.mjs +0 -245
- package/skills/content/xai-search/tests/smoke_test.sh +0 -36
- package/skills/core/polish-cn/scripts/polish-cn.test.mjs +0 -244
- package/skills/core/reg-parser/scripts/reg-parser.test.mjs +0 -165
- package/skills/engineering/better-review/scripts/pr-context.test.mjs +0 -168
- package/skills/engineering/better-review/scripts/rereview-scope.test.mjs +0 -587
- package/skills/engineering/better-review/scripts/scan-diff.test.mjs +0 -414
- package/skills/engineering/better-review/scripts/vendor-sync.test.mjs +0 -96
- package/skills/engineering/contributing/scripts/persist-conventions.test.mjs +0 -118
- package/skills/engineering/e2e-test/scripts/presentation-checks.test.mjs +0 -106
- package/skills/engineering/e2e-test/scripts/presentation-perceivability.test.mjs +0 -117
- package/skills/engineering/e2e-test/scripts/presentation-reachability.test.mjs +0 -69
- package/skills/engineering/e2e-test/scripts/presentation-render-health.test.mjs +0 -98
- package/skills/engineering/e2e-test/scripts/presentation-sweep.test.mjs +0 -71
- package/skills/engineering/e2e-test/scripts/presentation-temporal.test.mjs +0 -59
- package/skills/engineering/e2e-test/scripts/project-route-manifest.test.mjs +0 -76
- package/skills/engineering/engineering-runtime/engineering-runtime.test.mjs +0 -68
- package/skills/engineering/engineering-runtime/scripts/invocation-policy.test.mjs +0 -95
- package/skills/engineering/engineering-runtime/scripts/main-module.test.mjs +0 -288
- package/skills/engineering/engineering-runtime/scripts/required-check-plan.test.mjs +0 -96
- package/skills/engineering/ensure-coverage/scripts/adapters/nextjs-inventory.test.mjs +0 -96
- package/skills/engineering/ensure-coverage/scripts/check-quarantine-expiry.test.mjs +0 -47
- package/skills/engineering/ensure-coverage/scripts/ci-audit.test.mjs +0 -86
- package/skills/engineering/ensure-coverage/scripts/coverage-ledger.test.mjs +0 -447
- package/skills/engineering/ensure-coverage/scripts/design-parity.test.mjs +0 -552
- package/skills/engineering/ensure-coverage/scripts/lint-tests.test.mjs +0 -289
- package/skills/engineering/ensure-coverage/scripts/mock-policy-config.test.mjs +0 -119
- package/skills/engineering/golden-path/scripts/golden-path.test.mjs +0 -1457
- package/skills/engineering/harness-runtime/discover-models.test.mjs +0 -363
- package/skills/engineering/harness-runtime/harness-runtime.test.mjs +0 -967
- package/skills/engineering/harness-runtime/model-catalog-seed.test.mjs +0 -829
- package/skills/engineering/harness-runtime/model-catalog.test.mjs +0 -366
- package/skills/engineering/resolve-issues/scripts/cleanup-runs.test.mjs +0 -280
- package/skills/engineering/resolve-issues/scripts/dropbox.test.mjs +0 -346
- package/skills/engineering/resolve-issues/scripts/findings-reuse.test.mjs +0 -88
- package/skills/engineering/resolve-issues/scripts/github-reviews.test.mjs +0 -264
- package/skills/engineering/resolve-issues/scripts/install-hooks.test.mjs +0 -79
- package/skills/engineering/resolve-issues/scripts/issue-tree.test.mjs +0 -107
- package/skills/engineering/resolve-issues/scripts/manual-invocation.test.mjs +0 -14
- package/skills/engineering/resolve-issues/scripts/next-actions-capacity.test.mjs +0 -149
- package/skills/engineering/resolve-issues/scripts/next-actions.test.mjs +0 -915
- package/skills/engineering/resolve-issues/scripts/plan-sync.test.mjs +0 -156
- package/skills/engineering/resolve-issues/scripts/preflight-models.test.mjs +0 -448
- package/skills/engineering/resolve-issues/scripts/ready-check.test.mjs +0 -217
- package/skills/engineering/resolve-issues/scripts/rehydrate.test.mjs +0 -91
- package/skills/engineering/resolve-issues/scripts/review-verdict.test.mjs +0 -1060
- package/skills/engineering/resolve-issues/scripts/size-check.test.mjs +0 -458
- package/skills/engineering/resolve-release/scripts/apply.test.mjs +0 -99
- package/skills/engineering/resolve-release/scripts/dark-flags.test.mjs +0 -100
- package/skills/engineering/resolve-release/scripts/flow.test.mjs +0 -340
- package/skills/engineering/resolve-release/scripts/green-head.test.mjs +0 -101
- package/skills/engineering/resolve-release/scripts/npm-version-absence.test.mjs +0 -426
- package/skills/engineering/resolve-release/scripts/plan-npm.test.mjs +0 -659
- package/skills/engineering/resolve-release/scripts/plan-toml-boundary.test.mjs +0 -317
- package/skills/engineering/resolve-release/scripts/plan-uv.test.mjs +0 -291
- package/skills/engineering/resolve-release/scripts/plan.test.mjs +0 -428
- package/skills/engineering/test-quality/scripts/lint-tests.test.mjs +0 -591
- package/skills/engineering/test-quality/scripts/mock-policy-config.test.mjs +0 -119
- package/skills/engineering/test-quality/scripts/persist-conventions.test.mjs +0 -104
- package/skills/risk-compliance/compliance-audit/scripts/compliance-audit.test.mjs +0 -14
- package/skills/risk-compliance/doc-authenticity/manual-invocation.test.mjs +0 -12
- package/skills/risk-compliance/doc-authenticity/scripts/validate_verdict.test.mjs +0 -212
- package/skills/risk-compliance/tld-detection/scripts/detect_ladders.test.mjs +0 -14
- package/skills/risk-compliance/tld-detection/scripts/detect_streams.test.mjs +0 -14
- package/skills/risk-compliance/tld-detection/tests/test_detect_ladders.py +0 -134
- package/skills/risk-compliance/tld-detection/tests/test_detect_streams.py +0 -520
|
@@ -1,428 +0,0 @@
|
|
|
1
|
-
import { test } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
4
|
-
import { createHash } from 'node:crypto'
|
|
5
|
-
import { tmpdir } from 'node:os'
|
|
6
|
-
import { join } from 'node:path'
|
|
7
|
-
import { spawnSync } from 'node:child_process'
|
|
8
|
-
import {
|
|
9
|
-
bumpFor,
|
|
10
|
-
classifySubject,
|
|
11
|
-
latestReleaseTag,
|
|
12
|
-
nextReleaseCandidateTag,
|
|
13
|
-
nextVersion,
|
|
14
|
-
parseSemver,
|
|
15
|
-
planRelease,
|
|
16
|
-
renderChangelog,
|
|
17
|
-
} from './plan.mjs'
|
|
18
|
-
|
|
19
|
-
function git(cwd, args) {
|
|
20
|
-
const result = spawnSync('git', args, { cwd, encoding: 'utf8' })
|
|
21
|
-
assert.equal(result.status, 0, result.stderr)
|
|
22
|
-
return result.stdout
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function repo() {
|
|
26
|
-
const cwd = mkdtempSync(join(tmpdir(), 'rush-release-plan-'))
|
|
27
|
-
git(cwd, ['init', '-b', 'main'])
|
|
28
|
-
git(cwd, ['config', 'user.email', 'dev@example.com'])
|
|
29
|
-
git(cwd, ['config', 'user.name', 'Dev'])
|
|
30
|
-
git(cwd, ['config', 'commit.gpgsign', 'false'])
|
|
31
|
-
writeFileSync(join(cwd, 'package.json'), `${JSON.stringify({ name: 'demo', version: '1.2.0' }, null, 2)}\n`)
|
|
32
|
-
git(cwd, ['add', 'package.json'])
|
|
33
|
-
git(cwd, ['commit', '-m', 'chore: init'])
|
|
34
|
-
git(cwd, ['tag', 'v1.2.0'])
|
|
35
|
-
return cwd
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
test('semver helpers', () => {
|
|
39
|
-
assert.deepEqual(parseSemver('v1.2.3'), { major: 1, minor: 2, patch: 3 })
|
|
40
|
-
assert.equal(parseSemver('v1.2.3-rc.1'), null)
|
|
41
|
-
assert.equal(latestReleaseTag(['v1.2.0', 'v1.10.0', 'v1.2.3-rc.1']).tag, 'v1.10.0')
|
|
42
|
-
assert.deepEqual(nextVersion({ major: 1, minor: 2, patch: 3 }, 'minor'), { major: 1, minor: 3, patch: 0 })
|
|
43
|
-
assert.equal(nextReleaseCandidateTag('v1.3.0', []), 'v1.3.0-rc.1')
|
|
44
|
-
assert.equal(nextReleaseCandidateTag('v1.3.0', ['v1.3.0-rc.1', 'v1.3.0-rc.3']), 'v1.3.0-rc.4')
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
test('conventional commits drive the bump, with 0.x breaking as minor', () => {
|
|
48
|
-
const feat = [classifySubject('feat(api): list invoices')]
|
|
49
|
-
const fix = [classifySubject('fix: null guard')]
|
|
50
|
-
const breaking = [classifySubject('feat!: drop v1')]
|
|
51
|
-
assert.equal(bumpFor(feat, { major: 1, minor: 2, patch: 0 }).bump, 'minor')
|
|
52
|
-
assert.equal(bumpFor(fix, { major: 1, minor: 2, patch: 0 }).bump, 'patch')
|
|
53
|
-
assert.equal(bumpFor(breaking, { major: 1, minor: 2, patch: 0 }).bump, 'major')
|
|
54
|
-
assert.equal(bumpFor(breaking, { major: 0, minor: 9, patch: 0 }).bump, 'minor')
|
|
55
|
-
assert.equal(bumpFor(breaking, { major: 0, minor: 9, patch: 0 }, { strict: true }).bump, 'major')
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
test('changelog groups notable commits and skips internals', () => {
|
|
59
|
-
const md = renderChangelog({
|
|
60
|
-
version: '1.3.0',
|
|
61
|
-
date: '2026-08-24',
|
|
62
|
-
changes: [
|
|
63
|
-
classifySubject('feat: add export'),
|
|
64
|
-
classifySubject('fix: timezone'),
|
|
65
|
-
classifySubject('chore: ignore me'),
|
|
66
|
-
],
|
|
67
|
-
})
|
|
68
|
-
assert.match(md, /## \[1\.3\.0\] - 2026-08-24/)
|
|
69
|
-
assert.match(md, /### Added\n- add export/)
|
|
70
|
-
assert.match(md, /### Fixed\n- timezone/)
|
|
71
|
-
assert.doesNotMatch(md, /ignore me/)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
test('planRelease reads git history from the frozen SHA', () => {
|
|
75
|
-
const cwd = repo()
|
|
76
|
-
writeFileSync(join(cwd, 'a.txt'), 'a\n')
|
|
77
|
-
git(cwd, ['add', 'a.txt'])
|
|
78
|
-
git(cwd, ['commit', '-m', 'feat: export csv'])
|
|
79
|
-
const sha = git(cwd, ['rev-parse', 'HEAD']).trim()
|
|
80
|
-
writeFileSync(join(cwd, 'package.json'), `${JSON.stringify({ name: 'demo', version: '9.0.0' }, null, 2)}\n`)
|
|
81
|
-
git(cwd, ['add', 'package.json'])
|
|
82
|
-
git(cwd, ['commit', '-m', 'feat!: should not count if SHA is frozen earlier'])
|
|
83
|
-
const plan = planRelease({ sha, cwd, now: new Date('2026-08-24T00:00:00Z') })
|
|
84
|
-
assert.equal(plan.baselineTag, 'v1.2.0')
|
|
85
|
-
assert.equal(plan.currentVersion, '1.2.0')
|
|
86
|
-
assert.equal(plan.bump, 'minor')
|
|
87
|
-
assert.equal(plan.nextVersion, '1.3.0')
|
|
88
|
-
assert.equal(plan.tag, 'v1.3.0')
|
|
89
|
-
assert.deepEqual(plan.versionFiles, ['package.json'])
|
|
90
|
-
assert.match(plan.changelogMarkdown, /export csv/)
|
|
91
|
-
assert.doesNotMatch(plan.changelogMarkdown, /should not count/)
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
test('Cloud Run plan reserves the next unused immutable candidate tag', () => {
|
|
95
|
-
const cwd = repo()
|
|
96
|
-
writeFileSync(join(cwd, 'a.txt'), 'a\n')
|
|
97
|
-
git(cwd, ['add', 'a.txt'])
|
|
98
|
-
git(cwd, ['commit', '-m', 'feat: export csv'])
|
|
99
|
-
const sha = git(cwd, ['rev-parse', 'HEAD']).trim()
|
|
100
|
-
git(cwd, ['tag', 'v1.3.0-rc.1'])
|
|
101
|
-
git(cwd, ['tag', 'v1.3.0-rc.2'])
|
|
102
|
-
|
|
103
|
-
const plan = planRelease({ sha, cloudRun: true, cwd, now: new Date('2026-08-24T00:00:00Z') })
|
|
104
|
-
|
|
105
|
-
assert.equal(plan.tag, 'v1.3.0')
|
|
106
|
-
assert.equal(plan.candidateTag, 'v1.3.0-rc.3')
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
test('planRelease ignores higher tags that are not reachable from the frozen SHA', () => {
|
|
110
|
-
const cwd = repo()
|
|
111
|
-
const candidate = git(cwd, ['rev-parse', 'HEAD']).trim()
|
|
112
|
-
git(cwd, ['switch', '-c', 'unrelated'])
|
|
113
|
-
writeFileSync(join(cwd, 'side.txt'), 'side\n')
|
|
114
|
-
git(cwd, ['add', 'side.txt'])
|
|
115
|
-
git(cwd, ['commit', '-m', 'feat: unrelated branch'])
|
|
116
|
-
git(cwd, ['tag', 'v9.0.0'])
|
|
117
|
-
git(cwd, ['switch', 'main'])
|
|
118
|
-
|
|
119
|
-
const plan = planRelease({ sha: candidate, cwd, now: new Date('2026-08-24T00:00:00Z') })
|
|
120
|
-
assert.equal(plan.baselineTag, 'v1.2.0')
|
|
121
|
-
assert.equal(plan.currentVersion, '1.2.0')
|
|
122
|
-
assert.equal(plan.nextVersion, '1.2.1')
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
test('planRelease uses the repository release identity when the private root package has no version', () => {
|
|
126
|
-
const cwd = mkdtempSync(join(tmpdir(), 'rush-release-distribution-plan-'))
|
|
127
|
-
git(cwd, ['init', '-b', 'main'])
|
|
128
|
-
git(cwd, ['config', 'user.email', 'dev@example.com'])
|
|
129
|
-
git(cwd, ['config', 'user.name', 'Dev'])
|
|
130
|
-
git(cwd, ['config', 'commit.gpgsign', 'false'])
|
|
131
|
-
mkdirSync(join(cwd, 'skills/distribution'), { recursive: true })
|
|
132
|
-
writeFileSync(join(cwd, 'package.json'), `${JSON.stringify({ name: 'private-root', private: true }, null, 2)}\n`)
|
|
133
|
-
writeFileSync(join(cwd, 'skills/distribution/manifest.json'), `${JSON.stringify({
|
|
134
|
-
releaseVersion: '0.19.0',
|
|
135
|
-
sources: [{
|
|
136
|
-
id: 'first-party',
|
|
137
|
-
renderTargets: [
|
|
138
|
-
{ id: 'private-checkout', revision: 'main' },
|
|
139
|
-
],
|
|
140
|
-
}],
|
|
141
|
-
}, null, 2)}\n`)
|
|
142
|
-
git(cwd, ['add', '.'])
|
|
143
|
-
git(cwd, ['commit', '-m', 'chore: release 0.19.0'])
|
|
144
|
-
git(cwd, ['tag', 'v0.19.0'])
|
|
145
|
-
writeFileSync(join(cwd, 'change.txt'), 'manual trigger only\n')
|
|
146
|
-
git(cwd, ['add', 'change.txt'])
|
|
147
|
-
git(cwd, ['commit', '-m', 'fix: make coverage skills manual only'])
|
|
148
|
-
|
|
149
|
-
const plan = planRelease({
|
|
150
|
-
sha: git(cwd, ['rev-parse', 'HEAD']).trim(),
|
|
151
|
-
cwd,
|
|
152
|
-
now: new Date('2026-08-30T00:00:00Z'),
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
assert.equal(plan.currentVersion, '0.19.0')
|
|
156
|
-
assert.equal(plan.nextVersion, '0.19.1')
|
|
157
|
-
assert.deepEqual(plan.versionFiles, ['skills/distribution/manifest.json'])
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
// rush-release never passes --dark-flags, so its plan must be exactly what it was before
|
|
161
|
-
// dark launch existed: no darkFlags key and an unchanged changelog.
|
|
162
|
-
test('a plan without dark flags carries no dark-launch trace', () => {
|
|
163
|
-
const cwd = repo()
|
|
164
|
-
git(cwd, ['commit', '--allow-empty', '-m', 'feat: export csv'])
|
|
165
|
-
const plan = planRelease({ sha: 'HEAD', cwd, now: new Date('2026-08-24T00:00:00Z') })
|
|
166
|
-
assert.equal('darkFlags' in plan, false)
|
|
167
|
-
assert.equal(plan.changelogMarkdown, '## [1.3.0] - 2026-08-24\n\n### Added\n- export csv\n')
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
test('dark flags add a Shipped dark changelog section and keep the bump', () => {
|
|
171
|
-
const cwd = repo()
|
|
172
|
-
git(cwd, ['commit', '--allow-empty', '-m', 'feat: bulk refunds'])
|
|
173
|
-
const darkFlags = [{ name: 'bulk-refunds', summary: 'Bulk refunds' }]
|
|
174
|
-
const plan = planRelease({ sha: 'HEAD', cwd, darkFlags, now: new Date('2026-08-24T00:00:00Z') })
|
|
175
|
-
assert.equal(plan.bump, 'minor')
|
|
176
|
-
assert.deepEqual(plan.darkFlags, darkFlags)
|
|
177
|
-
assert.equal(plan.changelogMarkdown,
|
|
178
|
-
'## [1.3.0] - 2026-08-24\n\n### Added\n- bulk refunds\n\n### Shipped dark\n- Bulk refunds (flag `bulk-refunds`, off)\n')
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
test('the CLI reads dark flags from a dark-flags.mjs list file and rejects malformed ones', () => {
|
|
182
|
-
const cwd = repo()
|
|
183
|
-
git(cwd, ['commit', '--allow-empty', '-m', 'fix: rounding'])
|
|
184
|
-
const good = join(cwd, 'flags.json')
|
|
185
|
-
writeFileSync(good, JSON.stringify({ flags: [{ name: 'new-checkout', summary: 'New checkout', default: false }], errors: [] }))
|
|
186
|
-
const script = join(import.meta.dirname, 'plan.mjs')
|
|
187
|
-
const ok = spawnSync('node', [script, '--sha', 'HEAD', '--json', '--dark-flags', good], { cwd, encoding: 'utf8' })
|
|
188
|
-
assert.equal(ok.status, 0, ok.stderr)
|
|
189
|
-
assert.deepEqual(JSON.parse(ok.stdout).darkFlags, [{ name: 'new-checkout', summary: 'New checkout' }])
|
|
190
|
-
const bad = join(cwd, 'bad.json')
|
|
191
|
-
writeFileSync(bad, JSON.stringify([{ name: 'no-summary' }]))
|
|
192
|
-
const refused = spawnSync('node', [script, '--sha', 'HEAD', '--dark-flags', bad], { cwd, encoding: 'utf8' })
|
|
193
|
-
assert.equal(refused.status, 2)
|
|
194
|
-
assert.match(refused.stderr, /name, summary/)
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
function preparedRepo(t, { productCut = false, badLockCut = false, python = false, badPackageCut = false } = {}) {
|
|
198
|
-
const cwd = repo()
|
|
199
|
-
const remote = mkdtempSync(join(tmpdir(), 'prepared-release-origin-'))
|
|
200
|
-
t.after(() => { rmSync(cwd, { recursive: true, force: true }); rmSync(remote, { recursive: true, force: true }) })
|
|
201
|
-
git(remote, ['init', '--bare'])
|
|
202
|
-
// Exercise git's real transport while keeping the canonical origin identity offline.
|
|
203
|
-
git(cwd, ['remote', 'add', 'origin', 'https://github.com/example/demo.git'])
|
|
204
|
-
git(cwd, ['config', `url.${remote}.insteadOf`, 'https://github.com/example/demo.git'])
|
|
205
|
-
const lock = (version) => ({ name: 'demo', version, lockfileVersion: 3, packages: { '': { name: 'demo', version } } })
|
|
206
|
-
writeFileSync(join(cwd, 'package-lock.json'), JSON.stringify(lock('1.2.0')))
|
|
207
|
-
writeFileSync(join(cwd, 'VERSION'), '1.2.0\n')
|
|
208
|
-
if (python) writeFileSync(join(cwd, 'pyproject.toml'), '[project]\nname = "demo"\nversion = "1.2.0"\n\n[tool.other]\nversion = "not-a-release"\n')
|
|
209
|
-
writeFileSync(join(cwd, 'CHANGELOG.md'), '# Changelog\n\n## [1.2.0] - 2026-08-01\n\n- Previous release.\n')
|
|
210
|
-
git(cwd, ['add', '.'])
|
|
211
|
-
git(cwd, ['commit', '-m', 'chore: existing identities'])
|
|
212
|
-
git(cwd, ['switch', '-c', 'old-metadata'])
|
|
213
|
-
writeFileSync(join(cwd, 'package.json'), JSON.stringify({ name: 'demo', version: '1.3.0', ...(badPackageCut ? { scripts: { build: 'execute-unreviewed-code' } } : {}) }))
|
|
214
|
-
const cutLock = lock('1.3.0')
|
|
215
|
-
if (badLockCut) cutLock.packages['node_modules/unsafe'] = { version: '9.0.0' }
|
|
216
|
-
writeFileSync(join(cwd, 'package-lock.json'), JSON.stringify(cutLock))
|
|
217
|
-
writeFileSync(join(cwd, 'VERSION'), '1.3.0\n')
|
|
218
|
-
if (python) writeFileSync(join(cwd, 'pyproject.toml'), '[project]\nname = "demo"\nversion = "1.3.0"\n\n[tool.other]\nversion = "not-a-release"\n')
|
|
219
|
-
const original = '## [1.3.0] - 2026-08-24\n\n### Added\n- Original notes.\n'
|
|
220
|
-
writeFileSync(join(cwd, 'CHANGELOG.md'), `# Changelog\n\n${original}\n## [1.2.0] - 2026-08-01\n\n- Previous release.\n`)
|
|
221
|
-
if (productCut) writeFileSync(join(cwd, 'product.js'), 'unreviewed code\n')
|
|
222
|
-
git(cwd, ['add', '.'])
|
|
223
|
-
git(cwd, ['commit', '-m', 'chore(release): 1.3.0'])
|
|
224
|
-
const cutSha = git(cwd, ['rev-parse', 'HEAD']).trim()
|
|
225
|
-
git(cwd, ['switch', 'main'])
|
|
226
|
-
git(cwd, ['merge', '--no-ff', 'old-metadata', '-m', 'Merge reviewed metadata'])
|
|
227
|
-
const mergeSha = git(cwd, ['rev-parse', 'HEAD']).trim()
|
|
228
|
-
git(cwd, ['tag', '-a', 'v1.3.0-rc.1', '-m', 'Failed candidate'])
|
|
229
|
-
git(cwd, ['push', 'origin', 'main', '--tags'])
|
|
230
|
-
const reviewed = '## [1.3.0] - 2026-08-24\n\n### Added\n- Reviewed corrected notes.\n\n### Fixed\n- Candidate failure.\n'
|
|
231
|
-
writeFileSync(join(cwd, 'CHANGELOG.md'), `# Changelog\n\n${reviewed}\n## [1.2.0] - 2026-08-01\n\n- Previous release.\n`)
|
|
232
|
-
writeFileSync(join(cwd, 'product.js'), 'reviewed fix\n')
|
|
233
|
-
git(cwd, ['add', '.'])
|
|
234
|
-
git(cwd, ['commit', '-m', 'fix: candidate failure and release notes'])
|
|
235
|
-
const sha = git(cwd, ['rev-parse', 'HEAD']).trim()
|
|
236
|
-
git(cwd, ['push', 'origin', 'main'])
|
|
237
|
-
const preparedMetadata = {
|
|
238
|
-
schema: 'prepared-release-metadata/v1', repository: 'example/demo', version: '1.3.0', metadataPr: 42,
|
|
239
|
-
cutSha, mergeSha,
|
|
240
|
-
previousAttempt: { receiptId: 'failed-attempt-1', receiptSha256: 'a'.repeat(64), candidateTag: 'v1.3.0-rc.1', candidateSha: mergeSha },
|
|
241
|
-
}
|
|
242
|
-
return { cwd, remote, sha, preparedMetadata, original, reviewed }
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function preparedPlan(fixture, opts = {}) {
|
|
246
|
-
return planRelease({ ...fixture, cloudRun: true, now: new Date('2026-08-25T00:00:00Z'), ...opts })
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const digest = (text) => createHash('sha256').update(text).digest('hex')
|
|
250
|
-
|
|
251
|
-
test('prepared reuse preserves version and binds reviewed target notes separately from historical metadata', (t) => {
|
|
252
|
-
const f = preparedRepo(t)
|
|
253
|
-
const oldReceipt = join(f.cwd, '.git', 'resolve-release-authorization.json')
|
|
254
|
-
writeFileSync(oldReceipt, 'historical receipt: must not be read or changed')
|
|
255
|
-
const before = git(f.cwd, ['status', '--porcelain'])
|
|
256
|
-
const plan = preparedPlan(f)
|
|
257
|
-
assert.equal(plan.nextVersion, '1.3.0')
|
|
258
|
-
assert.equal(plan.currentVersion, '1.3.0')
|
|
259
|
-
assert.equal(plan.bump, 'none')
|
|
260
|
-
assert.equal(plan.tag, 'v1.3.0')
|
|
261
|
-
assert.equal(plan.candidateTag, 'v1.3.0-rc.2')
|
|
262
|
-
assert.equal(plan.sha, f.sha)
|
|
263
|
-
assert.equal(plan.releaseSourceSha, f.sha)
|
|
264
|
-
assert.equal(plan.changelogMarkdown, f.reviewed)
|
|
265
|
-
assert.equal(plan.date, '2026-08-24')
|
|
266
|
-
assert.equal(plan.metadata.mode, 'reuse-prepared')
|
|
267
|
-
assert.equal(plan.metadata.cutSha, f.preparedMetadata.cutSha)
|
|
268
|
-
assert.equal(plan.metadata.mergeSha, f.preparedMetadata.mergeSha)
|
|
269
|
-
assert.equal(plan.metadata.preparedSectionSha256, digest(f.reviewed))
|
|
270
|
-
assert.equal(plan.metadata.originalSectionSha256, digest(f.original))
|
|
271
|
-
assert.deepEqual(plan.metadata.previousAttempt, f.preparedMetadata.previousAttempt)
|
|
272
|
-
assert.notEqual(plan.receiptId, f.preparedMetadata.previousAttempt.receiptId)
|
|
273
|
-
assert.notEqual(preparedPlan(f).receiptId, plan.receiptId)
|
|
274
|
-
assert.equal('permissions' in plan, false)
|
|
275
|
-
assert.equal(readFileSync(oldReceipt, 'utf8'), 'historical receipt: must not be read or changed')
|
|
276
|
-
assert.equal(git(f.cwd, ['status', '--porcelain']), before)
|
|
277
|
-
// Neither a dirty worktree nor a later commit can replace the frozen source or notes.
|
|
278
|
-
writeFileSync(join(f.cwd, 'VERSION'), '9.0.0\n')
|
|
279
|
-
git(f.cwd, ['add', 'VERSION'])
|
|
280
|
-
git(f.cwd, ['commit', '-m', 'feat!: unrelated later change'])
|
|
281
|
-
assert.equal(preparedPlan(f).metadata.preparedSectionSha256, digest(f.reviewed))
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
test('prepared reuse uses remote ordinals even with stale local tags and never recycles the historical ordinal', (t) => {
|
|
285
|
-
const f = preparedRepo(t)
|
|
286
|
-
git(f.cwd, ['tag', 'v1.3.0-rc.4'])
|
|
287
|
-
git(f.cwd, ['push', 'origin', 'refs/tags/v1.3.0-rc.4'])
|
|
288
|
-
git(f.cwd, ['tag', '-d', 'v1.3.0-rc.4', 'v1.3.0-rc.1'])
|
|
289
|
-
assert.equal(preparedPlan(f).candidateTag, 'v1.3.0-rc.5')
|
|
290
|
-
git(f.remote, ['update-ref', '-d', 'refs/tags/v1.3.0-rc.4'])
|
|
291
|
-
git(f.remote, ['update-ref', '-d', 'refs/tags/v1.3.0-rc.1'])
|
|
292
|
-
assert.equal(preparedPlan(f).candidateTag, 'v1.3.0-rc.2')
|
|
293
|
-
})
|
|
294
|
-
|
|
295
|
-
test('prepared reuse blocks a remote final tag, inspection errors and noncanonical or wrong repository identity', (t) => {
|
|
296
|
-
const f = preparedRepo(t)
|
|
297
|
-
git(f.cwd, ['tag', '-a', 'v1.3.0', '-m', 'Already used'])
|
|
298
|
-
git(f.cwd, ['push', 'origin', 'refs/tags/v1.3.0'])
|
|
299
|
-
git(f.cwd, ['tag', '-d', 'v1.3.0'])
|
|
300
|
-
assert.throws(() => preparedPlan(f), /final tag.*already exists/)
|
|
301
|
-
git(f.remote, ['update-ref', '-d', 'refs/tags/v1.3.0'])
|
|
302
|
-
git(f.cwd, ['config', '--unset', `url.${f.remote}.insteadOf`])
|
|
303
|
-
// Repoint only the transport rewrite; origin's canonical identity stays valid.
|
|
304
|
-
git(f.cwd, ['config', `url.${join(f.remote, 'missing')}.insteadOf`, 'https://github.com/example/demo.git'])
|
|
305
|
-
assert.throws(() => preparedPlan(f), /remote inspection failed/)
|
|
306
|
-
git(f.cwd, ['remote', 'set-url', 'origin', f.remote])
|
|
307
|
-
assert.throws(() => preparedPlan(f), /canonical GitHub origin/)
|
|
308
|
-
git(f.cwd, ['remote', 'set-url', 'origin', 'https://github.com/other/demo.git'])
|
|
309
|
-
assert.throws(() => preparedPlan(f), /repository.*does not match/)
|
|
310
|
-
})
|
|
311
|
-
|
|
312
|
-
test('prepared reuse refuses product changes hidden in the original metadata cut', (t) => {
|
|
313
|
-
for (const options of [{ productCut: true }, { badLockCut: true }, { badPackageCut: true }]) {
|
|
314
|
-
const f = preparedRepo(t, options)
|
|
315
|
-
assert.throws(() => preparedPlan(f), /metadata-only/)
|
|
316
|
-
}
|
|
317
|
-
})
|
|
318
|
-
|
|
319
|
-
test('prepared reuse validates schema, full identities, previous attempt binding and unchanged version identity', (t) => {
|
|
320
|
-
const f = preparedRepo(t)
|
|
321
|
-
const p = f.preparedMetadata
|
|
322
|
-
const cases = [
|
|
323
|
-
[{ ...p, schema: 'future/v2' }, /schema/],
|
|
324
|
-
[{ ...p, version: 'v1.3.0' }, /version/],
|
|
325
|
-
[{ ...p, version: '1.4.0', previousAttempt: { ...p.previousAttempt, candidateTag: 'v1.4.0-rc.1' } }, /version.*does not match/],
|
|
326
|
-
[{ ...p, metadataPr: 0 }, /metadataPr/],
|
|
327
|
-
[{ ...p, mergeSha: 'HEAD' }, /full.*SHA/],
|
|
328
|
-
[{ ...p, cutSha: p.cutSha.slice(0, 12) }, /full.*SHA/],
|
|
329
|
-
[{ ...p, extraPermission: true }, /unknown field/],
|
|
330
|
-
[{ ...p, previousAttempt: { ...p.previousAttempt, receiptSha256: '' } }, /receiptSha256/],
|
|
331
|
-
[{ ...p, previousAttempt: { ...p.previousAttempt, candidateSha: f.sha } }, /previous.*candidateSha/],
|
|
332
|
-
[{ ...p, previousAttempt: { ...p.previousAttempt, candidateTag: 'v1.4.0-rc.1' } }, /previous.*candidateTag/],
|
|
333
|
-
]
|
|
334
|
-
for (const [preparedMetadata, error] of cases) assert.throws(() => preparedPlan(f, { preparedMetadata }), error)
|
|
335
|
-
assert.throws(() => preparedPlan(f, { sha: 'HEAD' }), /full.*SHA/)
|
|
336
|
-
assert.throws(() => preparedPlan(f, { trunk: 'release' }), /trunk.*main/)
|
|
337
|
-
assert.throws(() => preparedPlan(f, { darkFlags: [{ name: 'flag', summary: 'Unreviewed notes' }] }), /dark flags/)
|
|
338
|
-
})
|
|
339
|
-
|
|
340
|
-
test('prepared reuse rejects unequal merge trees, unrelated provenance and missing commit objects', (t) => {
|
|
341
|
-
const f = preparedRepo(t)
|
|
342
|
-
const p = f.preparedMetadata
|
|
343
|
-
const unequal = { ...p, mergeSha: f.sha, previousAttempt: { ...p.previousAttempt, candidateSha: f.sha } }
|
|
344
|
-
assert.throws(() => preparedPlan(f, { preparedMetadata: unequal }), /cut and merge trees/)
|
|
345
|
-
git(f.cwd, ['switch', '--orphan', 'unrelated'])
|
|
346
|
-
git(f.cwd, ['add', '.'])
|
|
347
|
-
git(f.cwd, ['commit', '--allow-empty', '-m', 'unrelated root'])
|
|
348
|
-
const unrelated = git(f.cwd, ['rev-parse', 'HEAD']).trim()
|
|
349
|
-
assert.throws(() => preparedPlan(f, { sha: unrelated }), /merge.*ancestor/)
|
|
350
|
-
assert.throws(() => preparedPlan(f, { sha: 'f'.repeat(40) }), /failed|object|revision/i)
|
|
351
|
-
})
|
|
352
|
-
|
|
353
|
-
test('prepared reuse rejects target identity drift, duplicate or absent release notes and a rebound old RC', (t) => {
|
|
354
|
-
const f = preparedRepo(t)
|
|
355
|
-
const checkChange = (file, text, error) => {
|
|
356
|
-
git(f.cwd, ['reset', '--hard', f.sha])
|
|
357
|
-
writeFileSync(join(f.cwd, file), text)
|
|
358
|
-
git(f.cwd, ['add', file])
|
|
359
|
-
git(f.cwd, ['commit', '-m', 'fix: invalid prepared identity'])
|
|
360
|
-
const sha = git(f.cwd, ['rev-parse', 'HEAD']).trim()
|
|
361
|
-
assert.throws(() => preparedPlan(f, { sha }), error)
|
|
362
|
-
}
|
|
363
|
-
checkChange('VERSION', '1.4.0\n', /version.*does not match/)
|
|
364
|
-
checkChange('package-lock.json', JSON.stringify({ name: 'demo', version: '1.3.0', packages: { '': { version: '1.2.0' } } }), /root lock.*version/)
|
|
365
|
-
checkChange('package-lock.json', JSON.stringify({ name: 'foreign', version: '1.3.0', packages: { '': { version: '1.3.0' } } }), /root lock.*name/)
|
|
366
|
-
checkChange('CHANGELOG.md', `${f.reviewed}\n${f.reviewed}`, /exactly one/)
|
|
367
|
-
checkChange('CHANGELOG.md', '# Changelog\n', /exactly one/)
|
|
368
|
-
git(f.remote, ['update-ref', 'refs/tags/v1.3.0-rc.1', f.sha])
|
|
369
|
-
assert.throws(() => preparedPlan(f), /previous.*remote.*SHA/)
|
|
370
|
-
})
|
|
371
|
-
|
|
372
|
-
test('prepared reuse validates Python project identity without confusing tool versions, and supports artifact-only plans', (t) => {
|
|
373
|
-
const f = preparedRepo(t, { python: true })
|
|
374
|
-
const plan = preparedPlan(f, { cloudRun: false })
|
|
375
|
-
assert.equal(plan.candidateTag, null)
|
|
376
|
-
assert.equal(plan.bump, 'none')
|
|
377
|
-
assert.deepEqual(plan.versionFiles, ['package.json', 'package-lock.json', 'pyproject.toml', 'VERSION'])
|
|
378
|
-
writeFileSync(join(f.cwd, 'pyproject.toml'), '[project]\nversion = "1.4.0"\n')
|
|
379
|
-
git(f.cwd, ['add', '.'])
|
|
380
|
-
git(f.cwd, ['commit', '-m', 'fix: mismatched python identity'])
|
|
381
|
-
assert.throws(() => preparedPlan(f, { sha: git(f.cwd, ['rev-parse', 'HEAD']).trim() }), /pyproject.toml version.*does not match/)
|
|
382
|
-
})
|
|
383
|
-
|
|
384
|
-
test('prepared reuse refuses distribution cuts instead of trusting a broad generated-file allowlist', (t) => {
|
|
385
|
-
const f = preparedRepo(t)
|
|
386
|
-
mkdirSync(join(f.cwd, 'skills/distribution'), { recursive: true })
|
|
387
|
-
writeFileSync(join(f.cwd, 'skills/distribution/manifest.json'), JSON.stringify({ releaseVersion: '1.3.0' }))
|
|
388
|
-
git(f.cwd, ['add', '.'])
|
|
389
|
-
git(f.cwd, ['commit', '-m', 'chore: distribution identity'])
|
|
390
|
-
const cutSha = git(f.cwd, ['rev-parse', 'HEAD']).trim()
|
|
391
|
-
const preparedMetadata = { ...f.preparedMetadata, cutSha, mergeSha: cutSha,
|
|
392
|
-
previousAttempt: { ...f.preparedMetadata.previousAttempt, candidateSha: cutSha } }
|
|
393
|
-
assert.throws(() => preparedPlan(f, { sha: cutSha, preparedMetadata }), /distribution-aware.*unsupported/)
|
|
394
|
-
})
|
|
395
|
-
|
|
396
|
-
test('prepared reuse blocks an RC collision introduced between remote discovery and the exact absence check', (t) => {
|
|
397
|
-
const f = preparedRepo(t)
|
|
398
|
-
const uploadPack = join(f.cwd, '.git', 'upload-pack.sh')
|
|
399
|
-
writeFileSync(uploadPack, `#!/bin/sh\ngit-upload-pack "$@"\nstatus=$?\ngit --git-dir='${f.remote}' update-ref refs/tags/v1.3.0-rc.2 ${f.sha}\nexit "$status"\n`, { mode: 0o755 })
|
|
400
|
-
git(f.cwd, ['config', 'remote.origin.uploadpack', uploadPack])
|
|
401
|
-
assert.throws(() => preparedPlan(f), /proposed candidate tag already exists remotely/)
|
|
402
|
-
})
|
|
403
|
-
|
|
404
|
-
test('prepared reuse blocks an exact remote inspection failure after successful discovery', (t) => {
|
|
405
|
-
const f = preparedRepo(t)
|
|
406
|
-
const uploadPack = join(f.cwd, '.git', 'upload-pack.sh')
|
|
407
|
-
const marker = join(f.cwd, '.git', 'inspected')
|
|
408
|
-
writeFileSync(uploadPack, `#!/bin/sh\nif test -e '${marker}'; then exit 1; fi\ntouch '${marker}'\nexec git-upload-pack "$@"\n`, { mode: 0o755 })
|
|
409
|
-
git(f.cwd, ['config', 'remote.origin.uploadpack', uploadPack])
|
|
410
|
-
assert.throws(() => preparedPlan(f), /remote inspection failed/)
|
|
411
|
-
})
|
|
412
|
-
|
|
413
|
-
test('CLI loads prepared metadata without applying it and rejects missing, malformed and unknown options', (t) => {
|
|
414
|
-
const f = preparedRepo(t)
|
|
415
|
-
const file = join(f.cwd, '.git', 'prepared.json')
|
|
416
|
-
writeFileSync(file, JSON.stringify(f.preparedMetadata))
|
|
417
|
-
const script = join(import.meta.dirname, 'plan.mjs')
|
|
418
|
-
const run = (...args) => spawnSync(process.execPath, [script, '--sha', f.sha, '--json', ...args], { cwd: f.cwd, encoding: 'utf8' })
|
|
419
|
-
const result = run('--prepared-metadata', file, '--cloud-run')
|
|
420
|
-
assert.equal(result.status, 0, result.stderr)
|
|
421
|
-
assert.equal(JSON.parse(result.stdout).sha, f.sha)
|
|
422
|
-
assert.equal(JSON.parse(result.stdout).bump, 'none')
|
|
423
|
-
assert.equal(JSON.parse(result.stdout).metadata.preparedSectionSha256, digest(f.reviewed))
|
|
424
|
-
assert.equal(run('--prepared-metadata').status, 2)
|
|
425
|
-
writeFileSync(file, '{')
|
|
426
|
-
assert.notEqual(run('--prepared-metadata', file).status, 0)
|
|
427
|
-
assert.equal(run('--version', '9.0.0').status, 2)
|
|
428
|
-
})
|