code-foundry 0.37.3 → 0.37.5
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/sync.mjs +14 -5
- package/src/lib/release-policy.mjs +60 -3
- package/src/runtime.mjs +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.37.5](https://github.com/0xPlayerOne/code-foundry/compare/v0.37.4...v0.37.5) (2026-08-23)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **sync:** omit Cargo Dependabot outside Rust repos ([#398](https://github.com/0xPlayerOne/code-foundry/issues/398)) ([395c4f3](https://github.com/0xPlayerOne/code-foundry/commit/395c4f34887bbb3e9f67378325b70c21d467adf0))
|
|
9
|
+
|
|
10
|
+
## [0.37.4](https://github.com/0xPlayerOne/code-foundry/compare/v0.37.3...v0.37.4) (2026-08-23)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* release-workspace-state ([#395](https://github.com/0xPlayerOne/code-foundry/issues/395)) ([a708bc0](https://github.com/0xPlayerOne/code-foundry/commit/a708bc07272b9751b22a457771cc386601bbe3d8))
|
|
16
|
+
|
|
3
17
|
## [0.37.3](https://github.com/0xPlayerOne/code-foundry/compare/v0.37.2...v0.37.3) (2026-08-23)
|
|
4
18
|
|
|
5
19
|
|
package/package.json
CHANGED
package/src/commands/sync.mjs
CHANGED
|
@@ -146,7 +146,7 @@ export function syncRepository(options) {
|
|
|
146
146
|
content = Buffer.from(renderWorkflow(content.toString('utf8'), config, runtimeRepository, runtimeRef, rustCodeql))
|
|
147
147
|
}
|
|
148
148
|
if (file === '.github/dependabot.yml') {
|
|
149
|
-
content = Buffer.from(renderDependabot(content.toString('utf8'), config))
|
|
149
|
+
content = Buffer.from(renderDependabot(content.toString('utf8'), config, languages))
|
|
150
150
|
}
|
|
151
151
|
if (['AGENTS.md', '.github/CONTRIBUTING.md', '.github/SECURITY.md'].includes(file)) {
|
|
152
152
|
content = Buffer.from(renderContributionDocs(content.toString('utf8'), file, config))
|
|
@@ -393,14 +393,23 @@ function renderWorkflow(content, config, repository, ref, rustCodeql) {
|
|
|
393
393
|
|
|
394
394
|
/**
|
|
395
395
|
* Dependabot updates land on the repository's integration branch. Direct
|
|
396
|
-
* repositories have no staging branch, so every update targets main.
|
|
396
|
+
* repositories have no staging branch, so every update targets main. Cargo
|
|
397
|
+
* updates are emitted only when Rust is part of the configured language set.
|
|
397
398
|
* @param {string} content
|
|
398
399
|
* @param {Record<string,string>} config
|
|
400
|
+
* @param {string} languages
|
|
399
401
|
* @returns {string}
|
|
400
402
|
*/
|
|
401
|
-
function renderDependabot(content, config) {
|
|
402
|
-
|
|
403
|
-
|
|
403
|
+
function renderDependabot(content, config, languages) {
|
|
404
|
+
let rendered = content
|
|
405
|
+
if (!includesValue(languages, 'rust')) {
|
|
406
|
+
rendered = rendered
|
|
407
|
+
.split(/(?=^ - package-ecosystem: )/m)
|
|
408
|
+
.filter((block) => !block.startsWith(' - package-ecosystem: cargo\n'))
|
|
409
|
+
.join('')
|
|
410
|
+
}
|
|
411
|
+
if (isStagingRelease(config.git_workflow)) return rendered
|
|
412
|
+
return rendered.replaceAll('target-branch: staging', 'target-branch: main')
|
|
404
413
|
}
|
|
405
414
|
|
|
406
415
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
import { existsSync, readFileSync } from 'node:fs'
|
|
4
|
-
import { join } from 'node:path'
|
|
4
|
+
import { dirname, join } from 'node:path'
|
|
5
5
|
import { isReleasePleaseHead } from './validation-policy.mjs'
|
|
6
6
|
|
|
7
7
|
const DEFAULT_RELEASE_FILES = new Set([
|
|
@@ -71,6 +71,62 @@ export function unexpectedReleasePaths(paths, allowed) {
|
|
|
71
71
|
.filter((path) => !allowed.has(path))
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Verify Cargo package versions remain reproducible after Release Please
|
|
76
|
+
* updates a manifest. Release Please's Node updater can change Cargo.toml
|
|
77
|
+
* while leaving a configured generic Cargo.lock extra-file untouched.
|
|
78
|
+
* @param {string} root
|
|
79
|
+
* @param {Record<string, unknown>} config
|
|
80
|
+
* @returns {string[]}
|
|
81
|
+
*/
|
|
82
|
+
export function validateCargoLockVersions(root, config = {}) {
|
|
83
|
+
const paths = new Set(['Cargo.lock'])
|
|
84
|
+
/** @param {unknown} entries @param {string} [prefix] */
|
|
85
|
+
const addEntries = (entries, prefix = '') => {
|
|
86
|
+
if (!Array.isArray(entries)) return
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
const value = typeof entry === 'string' ? entry : entry && typeof entry === 'object' && 'path' in entry ? entry.path : ''
|
|
89
|
+
if (typeof value !== 'string' || !value.endsWith('Cargo.lock')) continue
|
|
90
|
+
const clean = value.replace(/^\.\//, '').replace(/\\/g, '/')
|
|
91
|
+
paths.add(prefix ? `${prefix}/${clean}` : clean)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
addEntries(config['extra-files'])
|
|
95
|
+
const packages = config.packages
|
|
96
|
+
if (packages && typeof packages === 'object' && !Array.isArray(packages)) {
|
|
97
|
+
for (const [directory, value] of Object.entries(packages)) {
|
|
98
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
99
|
+
addEntries(value['extra-files'], directory === '.' ? '' : directory.replace(/\/$/, ''))
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** @type {string[]} */
|
|
105
|
+
const errors = []
|
|
106
|
+
for (const relativeLockPath of paths) {
|
|
107
|
+
const lockPath = join(root, relativeLockPath)
|
|
108
|
+
const manifestPath = join(root, dirname(relativeLockPath), 'Cargo.toml')
|
|
109
|
+
if (!existsSync(lockPath) || !existsSync(manifestPath)) continue
|
|
110
|
+
const manifestLines = readFileSync(manifestPath, 'utf8').split(/\r?\n/)
|
|
111
|
+
const packageStart = manifestLines.findIndex((line) => line.trim() === '[package]')
|
|
112
|
+
if (packageStart < 0) continue
|
|
113
|
+
const nextSection = manifestLines.findIndex((line, index) => index > packageStart && /^\s*\[/.test(line))
|
|
114
|
+
const packageSection = manifestLines.slice(packageStart + 1, nextSection < 0 ? manifestLines.length : nextSection).join('\n')
|
|
115
|
+
const packageName = packageSection.match(/^name\s*=\s*"([^"]+)"\s*$/m)?.[1]
|
|
116
|
+
const packageVersion = packageSection.match(/^version\s*=\s*"([^"]+)"\s*$/m)?.[1]
|
|
117
|
+
if (!packageName || !packageVersion) continue
|
|
118
|
+
const lock = readFileSync(lockPath, 'utf8')
|
|
119
|
+
const lockPackage = [...lock.matchAll(/\[\[package\]\]\s+name\s*=\s*"([^"]+)"\s+version\s*=\s*"([^"]+)"/g)]
|
|
120
|
+
.find((match) => match[1] === packageName)
|
|
121
|
+
if (!lockPackage) {
|
|
122
|
+
errors.push(`${relativeLockPath} is missing the ${packageName} package entry.`)
|
|
123
|
+
} else if (lockPackage[2] !== packageVersion) {
|
|
124
|
+
errors.push(`${relativeLockPath} version ${lockPackage[2]} does not match ${relativeLockPath.replace(/Cargo\.lock$/, 'Cargo.toml')} version ${packageVersion}.`)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return errors
|
|
128
|
+
}
|
|
129
|
+
|
|
74
130
|
/**
|
|
75
131
|
* Detect release-only divergence after a rebased staging promotion. A normal
|
|
76
132
|
* main-ancestor relationship must always remain promotable, even when the new
|
|
@@ -140,11 +196,11 @@ const VERSION_METADATA_FILES = new Set([
|
|
|
140
196
|
* must carry the exact approved Release Please prefix, come from the same
|
|
141
197
|
* repository, change at least one path, change no unexpected paths, and bump
|
|
142
198
|
* at least one version-metadata file (or a declared extra file).
|
|
143
|
-
* @param {{ headRef?: string, headRepo?: string, repository?: string, changedPaths?: string[], config?: Record<string, unknown
|
|
199
|
+
* @param {{ headRef?: string, headRepo?: string, repository?: string, changedPaths?: string[], config?: Record<string, unknown>, root?: string }} input
|
|
144
200
|
* @returns {{ valid: boolean, errors: string[], changedPaths: string[] }}
|
|
145
201
|
*/
|
|
146
202
|
export function validateGeneratedReleaseDiff(input) {
|
|
147
|
-
const { headRef = '', headRepo = '', repository = '', changedPaths = [], config = {} } = input
|
|
203
|
+
const { headRef = '', headRepo = '', repository = '', changedPaths = [], config = {}, root = process.cwd() } = input
|
|
148
204
|
/** @type {string[]} */
|
|
149
205
|
const errors = []
|
|
150
206
|
if (!isReleasePleaseHead(headRef)) {
|
|
@@ -173,6 +229,7 @@ export function validateGeneratedReleaseDiff(input) {
|
|
|
173
229
|
if (!paths.some((path) => versionMetadata.has(path))) {
|
|
174
230
|
errors.push('Generated release pull request changes no version metadata.')
|
|
175
231
|
}
|
|
232
|
+
errors.push(...validateCargoLockVersions(root, config))
|
|
176
233
|
return { valid: errors.length === 0, errors, changedPaths: paths }
|
|
177
234
|
}
|
|
178
235
|
|
package/src/runtime.mjs
CHANGED