code-foundry 0.31.0 → 0.31.2
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/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/src/commands/release.mjs +31 -9
- package/src/lib/release-policy.mjs +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.31.2](https://github.com/0xPlayerOne/code-foundry/compare/v0.31.1...v0.31.2) (2026-07-29)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **release:** treat identical branch trees as aligned ([32b739e](https://github.com/0xPlayerOne/code-foundry/commit/32b739e8e663dca05fd89005b26c6457ec265c1c))
|
|
9
|
+
|
|
10
|
+
## [0.31.1](https://github.com/0xPlayerOne/code-foundry/compare/v0.31.0...v0.31.1) (2026-07-29)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **release:** build clean staging sync branches ([03a7c19](https://github.com/0xPlayerOne/code-foundry/commit/03a7c191d7a5a6b95cce639e6ed9201b374f1010))
|
|
16
|
+
|
|
3
17
|
## [0.31.0](https://github.com/0xPlayerOne/code-foundry/compare/v0.30.4...v0.31.0) (2026-07-29)
|
|
4
18
|
|
|
5
19
|
|
package/package.json
CHANGED
package/src/commands/release.mjs
CHANGED
|
@@ -42,18 +42,12 @@ export function reconcileRelease(root, options) {
|
|
|
42
42
|
if (!process.env.GITHUB_REPOSITORY) throw new Error('GITHUB_REPOSITORY is required for synchronization PRs.')
|
|
43
43
|
const token = process.env.GH_TOKEN || process.env.GITHUB_TOKEN
|
|
44
44
|
if (!token) throw new Error('GH_TOKEN or GITHUB_TOKEN is required for synchronization PRs.')
|
|
45
|
-
const existing = ghJson(target, ['pr', 'list', '--repo', process.env.GITHUB_REPOSITORY, '--state', 'open', '--base', head, '--
|
|
45
|
+
const existing = ghJson(target, ['pr', 'list', '--repo', process.env.GITHUB_REPOSITORY, '--state', 'open', '--base', head, '--search', `chore(release): synchronize ${base} -> ${head} in:title`, '--json', 'number,url,headRefName'])
|
|
46
46
|
if (Array.isArray(existing) && existing.length) {
|
|
47
47
|
return { ...plan, synchronizationPullRequest: existing[0].url ?? existing[0].number }
|
|
48
48
|
}
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
'--base', head, '--head', base,
|
|
52
|
-
'--title', `chore(release): synchronize ${base} -> ${head}`,
|
|
53
|
-
'--body', `Synchronize ${head} with ${base} after Release Please publication.\n\nOnly approved release metadata differs between the branch tips; Code Foundry verified the content before opening this PR.`,
|
|
54
|
-
], { cwd: resolve(root), encoding: 'utf8', env: { ...process.env, GH_TOKEN: token } })
|
|
55
|
-
if (result.status !== 0) throw new Error(`Failed to create synchronization PR: ${result.stderr.trim()}`)
|
|
56
|
-
return { ...plan, synchronizationPullRequest: result.stdout.trim() }
|
|
49
|
+
const synchronizationPullRequest = createSynchronizationPullRequest(target, process.env.GITHUB_REPOSITORY, base, head, mainSha, stagingSha, mainChangedPaths, token)
|
|
50
|
+
return { ...plan, synchronizationPullRequest }
|
|
57
51
|
}
|
|
58
52
|
if (plan.action !== 'fast-forward' || !options.github || options.dryRun) return plan
|
|
59
53
|
if (!process.env.GITHUB_REPOSITORY) throw new Error('GITHUB_REPOSITORY is required for --github reconciliation.')
|
|
@@ -67,6 +61,34 @@ export function reconcileRelease(root, options) {
|
|
|
67
61
|
return plan
|
|
68
62
|
}
|
|
69
63
|
|
|
64
|
+
/** @param {string} root @param {string} repository @param {string} base @param {string} head @param {string} mainSha @param {string} stagingSha @param {string[]} changedPaths @param {string} token */
|
|
65
|
+
function createSynchronizationPullRequest(root, repository, base, head, mainSha, stagingSha, changedPaths, token) {
|
|
66
|
+
const branch = `codex/release-sync-${head}-${mainSha.slice(0, 12)}`
|
|
67
|
+
const remote = spawnSync('git', ['ls-remote', '--exit-code', '--heads', 'origin', `refs/heads/${branch}`], { cwd: root, encoding: 'utf8' })
|
|
68
|
+
if (remote.status !== 0) {
|
|
69
|
+
const checkout = spawnSync('git', ['switch', '--create', branch, `origin/${head}`], { cwd: root, encoding: 'utf8' })
|
|
70
|
+
if (checkout.status !== 0) throw new Error(`Failed to create synchronization branch: ${checkout.stderr.trim()}`)
|
|
71
|
+
const patch = spawnSync('git', ['diff', '--binary', stagingSha, mainSha, '--', ...changedPaths], { cwd: root, encoding: 'utf8' })
|
|
72
|
+
if (patch.status !== 0) throw new Error(`Failed to prepare synchronization patch: ${patch.stderr.trim()}`)
|
|
73
|
+
const apply = spawnSync('git', ['apply', '--whitespace=nowarn'], { cwd: root, input: patch.stdout, encoding: 'utf8' })
|
|
74
|
+
if (apply.status !== 0) throw new Error(`Synchronization patch did not apply cleanly: ${apply.stderr.trim()}`)
|
|
75
|
+
const add = spawnSync('git', ['add', '--', ...changedPaths], { cwd: root, encoding: 'utf8' })
|
|
76
|
+
if (add.status !== 0) throw new Error(`Failed to stage synchronization metadata: ${add.stderr.trim()}`)
|
|
77
|
+
const commit = spawnSync('git', ['commit', '-m', `chore(release): synchronize ${base} -> ${head}`], { cwd: root, encoding: 'utf8' })
|
|
78
|
+
if (commit.status !== 0) throw new Error(`Failed to commit synchronization metadata: ${commit.stderr.trim()}`)
|
|
79
|
+
const push = spawnSync('git', ['push', '--set-upstream', 'origin', branch], { cwd: root, encoding: 'utf8', env: { ...process.env, GH_TOKEN: token } })
|
|
80
|
+
if (push.status !== 0) throw new Error(`Failed to publish synchronization branch: ${push.stderr.trim()}`)
|
|
81
|
+
}
|
|
82
|
+
const result = spawnSync('gh', [
|
|
83
|
+
'pr', 'create', '--repo', repository,
|
|
84
|
+
'--base', head, '--head', branch,
|
|
85
|
+
'--title', `chore(release): synchronize ${base} -> ${head}`,
|
|
86
|
+
'--body', `Synchronize ${head} with ${base} after Release Please publication.\n\nCode Foundry verified that only approved release metadata differs between the branch tips, then applied that metadata to an isolated synchronization branch.`,
|
|
87
|
+
], { cwd: resolve(root), encoding: 'utf8', env: { ...process.env, GH_TOKEN: token } })
|
|
88
|
+
if (result.status !== 0) throw new Error(`Failed to create synchronization PR: ${result.stderr.trim()}`)
|
|
89
|
+
return result.stdout.trim()
|
|
90
|
+
}
|
|
91
|
+
|
|
70
92
|
/**
|
|
71
93
|
* Dispatch a configured post-release workflow at most once for a tag.
|
|
72
94
|
* @param {string} root
|
|
@@ -165,6 +165,9 @@ export function classifyReconciliation(input) {
|
|
|
165
165
|
if (mainSha === mergeBaseSha) {
|
|
166
166
|
return { action: 'none', reason: 'staging contains commits that are not on main; no release-only reconciliation is needed.' }
|
|
167
167
|
}
|
|
168
|
+
if (!mainChangedPaths.length && !stagingChangedPaths.length) {
|
|
169
|
+
return { action: 'aligned', reason: 'Branches have different history but identical content.' }
|
|
170
|
+
}
|
|
168
171
|
const unexpectedMain = unexpectedReleasePaths(mainChangedPaths, allowed)
|
|
169
172
|
const unexpectedStaging = unexpectedReleasePaths(stagingChangedPaths, allowed)
|
|
170
173
|
if (unexpectedMain.length || unexpectedStaging.length) {
|