code-foundry 1.48.5 → 1.48.6
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 +8 -8
- package/docs/RELEASES.md +29 -0
- package/package.json +1 -1
- package/src/commands/release.mjs +24 -2
- package/src/lib/release-policy.mjs +190 -1
- package/src/runtime-core.mjs +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.48.6](https://github.com/0xPlayerOne/code-foundry/compare/v1.48.5...v1.48.6) (2026-10-23)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **release:** audit generated release content for stale snapshots ([#775](https://github.com/0xPlayerOne/code-foundry/issues/775)) ([f842c27](https://github.com/0xPlayerOne/code-foundry/commit/f842c271e6b270b63a6d9b839795a849237c7a7d))
|
|
9
|
+
|
|
3
10
|
## [1.48.5](https://github.com/0xPlayerOne/code-foundry/compare/v1.48.4...v1.48.5) (2026-10-22)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -133,16 +140,9 @@
|
|
|
133
140
|
|
|
134
141
|
* **billing:** run fast-lane executes concurrently ([#738](https://github.com/0xPlayerOne/code-foundry/issues/738)) ([b40e82a](https://github.com/0xPlayerOne/code-foundry/commit/b40e82a0709b288a8be46998cfdc8b909f92c23c))
|
|
135
142
|
|
|
136
|
-
## [1.45.2](https://github.com/0xPlayerOne/code-foundry/compare/v1.45.1...v1.45.2) (2026-10-06)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
### Bug Fixes
|
|
140
|
-
|
|
141
|
-
* **billing:** emit the codeql input as a YAML boolean ([#735](https://github.com/0xPlayerOne/code-foundry/issues/735)) ([e0b348c](https://github.com/0xPlayerOne/code-foundry/commit/e0b348c9a3c730e46210fe0b9dc46af49c9db605))
|
|
142
|
-
|
|
143
143
|
<!-- code-foundry: packaged changelog -->
|
|
144
144
|
|
|
145
145
|
---
|
|
146
146
|
|
|
147
|
-
This packaged changelog lists the 20 most recent releases;
|
|
147
|
+
This packaged changelog lists the 20 most recent releases; 254 older releases are omitted to keep the package small.
|
|
148
148
|
See the [full changelog](https://github.com/0xPlayerOne/code-foundry/blob/main/CHANGELOG.md) for the complete history.
|
package/docs/RELEASES.md
CHANGED
|
@@ -108,6 +108,35 @@ and sync never overwrites existing manifest versions. Legacy configs without
|
|
|
108
108
|
manifest; `code-foundry doctor` fails when a manifest-mode config is missing
|
|
109
109
|
its manifest.
|
|
110
110
|
|
|
111
|
+
## Generated release content audit
|
|
112
|
+
|
|
113
|
+
Release Please writes the release commit from file bodies it reads over the
|
|
114
|
+
GitHub API. A run that starts on one push and finishes after another can commit
|
|
115
|
+
bodies read before the second push onto the newer `main`, so the version PR
|
|
116
|
+
silently reverts the merged change inside an approved file (agent-sim #133
|
|
117
|
+
reverted `@adea-ai/ui` from `^0.144.0` to `^0.143.0` in six `package.json`
|
|
118
|
+
files). A path audit cannot see this, because every changed path is an approved
|
|
119
|
+
release file.
|
|
120
|
+
|
|
121
|
+
Both release checks therefore read the unified diff against the base and require
|
|
122
|
+
every added or removed line to be a release mutation: a version assignment
|
|
123
|
+
(`"version": "1.2.3"`, `version = "1.2.3"`, a bare version file, a line carrying
|
|
124
|
+
an `x-release-please-*` annotation, or a `jsonpath` key declared in
|
|
125
|
+
`extra-files`), a `.release-please-manifest.json` package version, or an addition
|
|
126
|
+
to a changelog. Any other change, a deleted file, or a rename fails closed and
|
|
127
|
+
names the file and the first offending lines. A minified single-line JSON
|
|
128
|
+
manifest passes when only its version keys differ. An unreadable diff also
|
|
129
|
+
fails; it is never treated as empty.
|
|
130
|
+
|
|
131
|
+
- `validation release_diff` (the required release-tier gate) audits
|
|
132
|
+
`base...HEAD` of the pull request merge commit, so a branch that is behind
|
|
133
|
+
`main` is judged by what merging it would do.
|
|
134
|
+
- `release validate-prs` audits `gh pr diff` before auto-merge is requested.
|
|
135
|
+
|
|
136
|
+
A failed audit leaves the version PR unmerged. Re-run the Release workflow
|
|
137
|
+
(`workflow_dispatch`) so Release Please regenerates the branch from the current
|
|
138
|
+
`main`; do not edit the generated branch or release manually.
|
|
139
|
+
|
|
111
140
|
## Pull request permissions
|
|
112
141
|
|
|
113
142
|
The release job validates the configured automation token
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-foundry",
|
|
3
|
-
"version": "1.48.
|
|
3
|
+
"version": "1.48.6",
|
|
4
4
|
"description": "A fast, language-aware repository factory for agent-ready workflows, testing, security, and release automation.",
|
|
5
5
|
"homepage": "https://github.com/0xPlayerOne/code-foundry#readme",
|
|
6
6
|
"bugs": {
|
package/src/commands/release.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import { spawnSync } from 'node:child_process'
|
|
|
7
7
|
import {
|
|
8
8
|
approvedReleaseFiles,
|
|
9
9
|
buildReleaseRecoveryPlan,
|
|
10
|
+
auditReleaseDiffContent,
|
|
10
11
|
buildReconciliationPullRequestBody,
|
|
11
12
|
classifyReconciliation,
|
|
12
13
|
readReleaseConfig,
|
|
@@ -794,6 +795,8 @@ export function validateReleasePullRequestDiffs(root) {
|
|
|
794
795
|
const paths = new Map()
|
|
795
796
|
/** @type {string[]} */
|
|
796
797
|
const headErrors = []
|
|
798
|
+
/** @type {string[]} */
|
|
799
|
+
const contentErrors = []
|
|
797
800
|
for (const pr of generated) {
|
|
798
801
|
const number = Number(pr.number)
|
|
799
802
|
const result = spawnSync(
|
|
@@ -803,6 +806,25 @@ export function validateReleasePullRequestDiffs(root) {
|
|
|
803
806
|
)
|
|
804
807
|
paths.set(number, result.status === 0 ? result.stdout.split(/\r?\n/).filter(Boolean) : [])
|
|
805
808
|
|
|
809
|
+
// Paths prove only which files changed. Audit the content too, so a release
|
|
810
|
+
// generated from a stale snapshot cannot revert merged changes inside an
|
|
811
|
+
// approved file and still be queued for merge.
|
|
812
|
+
const content = spawnSync(
|
|
813
|
+
'gh',
|
|
814
|
+
['pr', 'diff', String(number), '--repo', repository, '--color', 'never'],
|
|
815
|
+
{ cwd: resolve(root), encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 }
|
|
816
|
+
)
|
|
817
|
+
if (content.status === 0) {
|
|
818
|
+
for (const error of auditReleaseDiffContent(content.stdout, {
|
|
819
|
+
config: readReleaseConfig(root),
|
|
820
|
+
}))
|
|
821
|
+
contentErrors.push(`Generated release PR #${number}: ${error}`)
|
|
822
|
+
} else {
|
|
823
|
+
contentErrors.push(
|
|
824
|
+
`Generated release PR #${number}: unable to read its content diff; refusing to pass an unaudited release PR.`
|
|
825
|
+
)
|
|
826
|
+
}
|
|
827
|
+
|
|
806
828
|
const auditedHead = typeof pr.headRefOid === 'string' ? pr.headRefOid : ''
|
|
807
829
|
const currentPr = ghJson(root, [
|
|
808
830
|
'pr',
|
|
@@ -828,9 +850,9 @@ export function validateReleasePullRequestDiffs(root) {
|
|
|
828
850
|
paths,
|
|
829
851
|
approvedReleaseFiles(readReleaseConfig(root))
|
|
830
852
|
)
|
|
831
|
-
if (headErrors.length) {
|
|
853
|
+
if (headErrors.length || contentErrors.length) {
|
|
832
854
|
validation.valid = false
|
|
833
|
-
validation.errors.push(...headErrors)
|
|
855
|
+
validation.errors.push(...headErrors, ...contentErrors)
|
|
834
856
|
}
|
|
835
857
|
console.log(JSON.stringify(validation, null, 2))
|
|
836
858
|
if (!validation.valid) throw new Error(validation.errors.join(' '))
|
|
@@ -209,6 +209,187 @@ export function validateReleasePullRequests(prs, changedPathsByPr, allowed) {
|
|
|
209
209
|
return { valid: errors.length === 0, generated, errors }
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
/** A `key = value` or `"key": value` assignment whose key names a version. */
|
|
213
|
+
const VERSION_ASSIGNMENT =
|
|
214
|
+
/^\s*["']?[\w.-]*version[\w.-]*["']?\s*[:=]\s*["'][^"']*["']\s*[,;]?\s*$/i
|
|
215
|
+
const BARE_VERSION = /^\s*v?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?\s*$/
|
|
216
|
+
const MANIFEST_ENTRY = /^\s*"[^"]+"\s*:\s*"v?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?"\s*,?\s*$/
|
|
217
|
+
const RELEASE_ANNOTATION = /x-release-please-(?:version|major|minor|patch|start-\w+|end)/
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* JSON keys that a configured `jsonpath` extra file is allowed to rewrite,
|
|
221
|
+
* beyond `version`. Keyed by repository-relative path.
|
|
222
|
+
* @param {Record<string, unknown>} config
|
|
223
|
+
* @returns {Map<string, Set<string>>}
|
|
224
|
+
*/
|
|
225
|
+
function extraFileJsonKeys(config) {
|
|
226
|
+
/** @type {Map<string, Set<string>>} */
|
|
227
|
+
const keys = new Map()
|
|
228
|
+
/** @param {unknown} entries @param {string} prefix */
|
|
229
|
+
const collect = (entries, prefix) => {
|
|
230
|
+
if (!Array.isArray(entries)) return
|
|
231
|
+
for (const entry of entries) {
|
|
232
|
+
if (!entry || typeof entry !== 'object' || typeof entry.path !== 'string') continue
|
|
233
|
+
if (typeof entry.jsonpath !== 'string') continue
|
|
234
|
+
const key = /(?:\.|\["|\['|\[)?([^.[\]"']+)(?:"\]|'\]|\])?$/.exec(entry.jsonpath)?.[1]
|
|
235
|
+
if (!key) continue
|
|
236
|
+
const clean = entry.path.replace(/^\.\//, '').replace(/\\/g, '/')
|
|
237
|
+
const path = prefix ? `${prefix}/${clean}` : clean
|
|
238
|
+
keys.set(path, (keys.get(path) ?? new Set()).add(key))
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
collect(config['extra-files'], '')
|
|
242
|
+
const packages = config.packages
|
|
243
|
+
if (packages && typeof packages === 'object' && !Array.isArray(packages)) {
|
|
244
|
+
for (const [directory, value] of Object.entries(packages)) {
|
|
245
|
+
if (value && typeof value === 'object' && !Array.isArray(value))
|
|
246
|
+
collect(value['extra-files'], directory === '.' ? '' : directory.replace(/\/$/, ''))
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return keys
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @param {{ removed: string[], added: string[] }} entry
|
|
254
|
+
* @param {Set<string>|undefined} extraKeys
|
|
255
|
+
* @returns {boolean}
|
|
256
|
+
*/
|
|
257
|
+
function sameJsonExceptVersions(entry, extraKeys) {
|
|
258
|
+
if (!entry.removed.length || entry.removed.length !== entry.added.length) return false
|
|
259
|
+
const ignored = new Set(['version', ...(extraKeys ?? [])])
|
|
260
|
+
/** @param {string} text */
|
|
261
|
+
const normalize = (text) => {
|
|
262
|
+
const parsed = JSON.parse(text)
|
|
263
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
264
|
+
throw new Error('not an object')
|
|
265
|
+
for (const key of ignored) delete parsed[key]
|
|
266
|
+
return JSON.stringify(parsed)
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
return entry.removed.every((line, index) => normalize(line) === normalize(entry.added[index]))
|
|
270
|
+
} catch {
|
|
271
|
+
return false
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** @param {string[]} values @returns {string} */
|
|
276
|
+
function summarize(values) {
|
|
277
|
+
if (!values.length) return 'none'
|
|
278
|
+
const more = values.length > 3 ? ` (+${values.length - 3} more)` : ''
|
|
279
|
+
return `${values.slice(0, 3).join(' | ')}${more}`
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** @param {string} line @returns {string} */
|
|
283
|
+
function clip(line) {
|
|
284
|
+
const text = line.trim()
|
|
285
|
+
return text.length > 100 ? `${text.slice(0, 97)}...` : text
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Decide whether one changed line is a legitimate Release Please mutation of
|
|
290
|
+
* `path`. Only version assignments, the manifest's package versions, and
|
|
291
|
+
* changelog additions qualify; any other line means the generator wrote
|
|
292
|
+
* content it did not read from the branch it is releasing.
|
|
293
|
+
* @param {string} path @param {'-'|'+'} side @param {string} line
|
|
294
|
+
* @param {Map<string, Set<string>>} extraKeys
|
|
295
|
+
*/
|
|
296
|
+
function isReleaseMutation(path, side, line, extraKeys) {
|
|
297
|
+
const name = path.split('/').pop() ?? path
|
|
298
|
+
if (name === '.release-please-manifest.json')
|
|
299
|
+
return MANIFEST_ENTRY.test(line) || /^\s*[{}]\s*$/.test(line)
|
|
300
|
+
if (/^CHANGELOG(?:\.\w+)?$/i.test(name)) return side === '+'
|
|
301
|
+
if (VERSION_ASSIGNMENT.test(line) || BARE_VERSION.test(line) || RELEASE_ANNOTATION.test(line))
|
|
302
|
+
return true
|
|
303
|
+
for (const key of extraKeys.get(path) ?? []) {
|
|
304
|
+
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
305
|
+
if (new RegExp(`^\\s*"${escaped}"\\s*:\\s*"[^"]*"\\s*,?\\s*$`).test(line)) return true
|
|
306
|
+
}
|
|
307
|
+
return false
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Audit the content of a generated release diff. The path audit only proves
|
|
312
|
+
* that approved files changed; a Release Please run that straddles a push to
|
|
313
|
+
* main can write the release commit with file bodies read before that push,
|
|
314
|
+
* silently reverting the merged change inside an approved file (for example a
|
|
315
|
+
* dependency range in package.json). Every added or removed line must be a
|
|
316
|
+
* release mutation, so a reverted range, script or dependency fails closed.
|
|
317
|
+
* @param {string} patch Unified diff of the release PR against its base.
|
|
318
|
+
* @param {{ config?: Record<string, unknown> }} [options]
|
|
319
|
+
* @returns {string[]}
|
|
320
|
+
*/
|
|
321
|
+
export function auditReleaseDiffContent(patch, options = {}) {
|
|
322
|
+
const extraKeys = extraFileJsonKeys(options.config ?? {})
|
|
323
|
+
/** @type {string[]} */
|
|
324
|
+
const errors = []
|
|
325
|
+
/** @type {Map<string, { removed: string[], added: string[] }>} */
|
|
326
|
+
const foreign = new Map()
|
|
327
|
+
const lines = String(patch).split(/\r?\n/)
|
|
328
|
+
let oldPath = ''
|
|
329
|
+
let path = ''
|
|
330
|
+
let inHunk = false
|
|
331
|
+
let removedLeft = 0
|
|
332
|
+
let addedLeft = 0
|
|
333
|
+
for (const line of lines) {
|
|
334
|
+
if (inHunk && (removedLeft > 0 || addedLeft > 0)) {
|
|
335
|
+
if (line.startsWith('\\')) continue
|
|
336
|
+
if (line.startsWith('-') && removedLeft > 0) {
|
|
337
|
+
removedLeft -= 1
|
|
338
|
+
record(path, '-', line.slice(1))
|
|
339
|
+
continue
|
|
340
|
+
}
|
|
341
|
+
if (line.startsWith('+') && addedLeft > 0) {
|
|
342
|
+
addedLeft -= 1
|
|
343
|
+
record(path, '+', line.slice(1))
|
|
344
|
+
continue
|
|
345
|
+
}
|
|
346
|
+
if (line.startsWith(' ')) {
|
|
347
|
+
// -U0 emits no context; tolerate it when a caller passes a wider diff.
|
|
348
|
+
if (removedLeft > 0) removedLeft -= 1
|
|
349
|
+
if (addedLeft > 0) addedLeft -= 1
|
|
350
|
+
continue
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (line.startsWith('diff --git ')) {
|
|
354
|
+
inHunk = false
|
|
355
|
+
oldPath = ''
|
|
356
|
+
path = /^diff --git a\/.* b\/(.*)$/.exec(line)?.[1] ?? ''
|
|
357
|
+
} else if (line.startsWith('--- ')) {
|
|
358
|
+
oldPath = line.slice(4).replace(/^a\//, '')
|
|
359
|
+
} else if (line.startsWith('+++ ')) {
|
|
360
|
+
const next = line.slice(4)
|
|
361
|
+
path = next === '/dev/null' ? oldPath : next.replace(/^b\//, '')
|
|
362
|
+
} else if (line.startsWith('deleted file mode')) {
|
|
363
|
+
errors.push(`Generated release diff deletes ${path || oldPath || 'a file'}.`)
|
|
364
|
+
} else if (line.startsWith('rename from ')) {
|
|
365
|
+
errors.push(`Generated release diff renames ${line.slice('rename from '.length)}.`)
|
|
366
|
+
} else if (line.startsWith('@@')) {
|
|
367
|
+
const match = /^@@ -\d+(?:,(\d+))? \+\d+(?:,(\d+))? @@/.exec(line)
|
|
368
|
+
inHunk = Boolean(match)
|
|
369
|
+
removedLeft = match ? Number(match[1] ?? 1) : 0
|
|
370
|
+
addedLeft = match ? Number(match[2] ?? 1) : 0
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
/** @param {string} file @param {'-'|'+'} side @param {string} text */
|
|
374
|
+
function record(file, side, text) {
|
|
375
|
+
if (isReleaseMutation(file, side, text, extraKeys)) return
|
|
376
|
+
const entry = foreign.get(file) ?? { removed: [], added: [] }
|
|
377
|
+
entry[side === '-' ? 'removed' : 'added'].push(text)
|
|
378
|
+
foreign.set(file, entry)
|
|
379
|
+
}
|
|
380
|
+
for (const [file, entry] of foreign) {
|
|
381
|
+
// A minified manifest holds the whole document on one line, so its version
|
|
382
|
+
// bump cannot be isolated by line. Accept it when only version keys differ.
|
|
383
|
+
if (sameJsonExceptVersions(entry, extraKeys.get(file))) continue
|
|
384
|
+
entry.removed = entry.removed.map(clip)
|
|
385
|
+
entry.added = entry.added.map(clip)
|
|
386
|
+
errors.push(
|
|
387
|
+
`Generated release PR changes ${file} beyond release metadata (stale or foreign content). Removed: ${summarize(entry.removed)}. Added: ${summarize(entry.added)}.`
|
|
388
|
+
)
|
|
389
|
+
}
|
|
390
|
+
return errors
|
|
391
|
+
}
|
|
392
|
+
|
|
212
393
|
/** Files that must change for a generated release PR to count as a version bump. */
|
|
213
394
|
const VERSION_METADATA_FILES = new Set([
|
|
214
395
|
'.release-please-manifest.json',
|
|
@@ -223,7 +404,7 @@ const VERSION_METADATA_FILES = new Set([
|
|
|
223
404
|
* must carry the exact approved Release Please prefix, come from the same
|
|
224
405
|
* repository, change at least one path, change no unexpected paths, and bump
|
|
225
406
|
* at least one version-metadata file (or a declared extra file).
|
|
226
|
-
* @param {{ headRef?: string, headRepo?: string, repository?: string, changedPaths?: string[], config?: Record<string, unknown>, root?: string }} input
|
|
407
|
+
* @param {{ headRef?: string, headRepo?: string, repository?: string, changedPaths?: string[], config?: Record<string, unknown>, root?: string, patch?: string|null }} input
|
|
227
408
|
* @returns {{ valid: boolean, errors: string[], changedPaths: string[] }}
|
|
228
409
|
*/
|
|
229
410
|
export function validateGeneratedReleaseDiff(input) {
|
|
@@ -234,6 +415,7 @@ export function validateGeneratedReleaseDiff(input) {
|
|
|
234
415
|
changedPaths = [],
|
|
235
416
|
config = {},
|
|
236
417
|
root = process.cwd(),
|
|
418
|
+
patch,
|
|
237
419
|
} = input
|
|
238
420
|
/** @type {string[]} */
|
|
239
421
|
const errors = []
|
|
@@ -271,6 +453,13 @@ export function validateGeneratedReleaseDiff(input) {
|
|
|
271
453
|
errors.push('Generated release pull request changes no version metadata.')
|
|
272
454
|
}
|
|
273
455
|
errors.push(...validateCargoLockVersions(root, config))
|
|
456
|
+
// Callers that can read the unified diff opt into the content audit. A null
|
|
457
|
+
// patch means the diff was requested but unreadable, which must not pass.
|
|
458
|
+
if (typeof patch === 'string') errors.push(...auditReleaseDiffContent(patch, { config }))
|
|
459
|
+
else if (patch === null)
|
|
460
|
+
errors.push(
|
|
461
|
+
'Unable to read the release content diff; refusing to pass an unaudited release PR.'
|
|
462
|
+
)
|
|
274
463
|
return { valid: errors.length === 0, errors, changedPaths: paths }
|
|
275
464
|
}
|
|
276
465
|
|
package/src/runtime-core.mjs
CHANGED
|
@@ -395,6 +395,15 @@ function validation(task) {
|
|
|
395
395
|
const changedPaths = baseSha
|
|
396
396
|
? capture('git', ['diff', '--name-only', `${baseSha}...HEAD`]).split(/\r?\n/)
|
|
397
397
|
: []
|
|
398
|
+
// The content audit needs the real diff. An unreadable diff must fail the
|
|
399
|
+
// gate, not pass as an empty patch.
|
|
400
|
+
const diff = baseSha
|
|
401
|
+
? spawnSync(
|
|
402
|
+
'git',
|
|
403
|
+
['diff', '--no-color', '--no-ext-diff', '--no-renames', '-U0', `${baseSha}...HEAD`],
|
|
404
|
+
{ cwd: root, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 }
|
|
405
|
+
)
|
|
406
|
+
: undefined
|
|
398
407
|
const result = validateGeneratedReleaseDiff({
|
|
399
408
|
headRef: process.env.FOUNDRY_HEAD_REF ?? '',
|
|
400
409
|
headRepo: process.env.FOUNDRY_HEAD_REPO ?? '',
|
|
@@ -402,6 +411,7 @@ function validation(task) {
|
|
|
402
411
|
changedPaths,
|
|
403
412
|
config: readReleaseConfig(root),
|
|
404
413
|
root,
|
|
414
|
+
patch: diff ? (diff.status === 0 ? diff.stdout : null) : undefined,
|
|
405
415
|
})
|
|
406
416
|
if (!result.valid) {
|
|
407
417
|
for (const error of result.errors) console.error(`::error::${error}`)
|