镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Commit 1ae7c02

Browse files
committed
fix(release): preserve unchanged native toolchain pins
1 parent 5bae68a commit 1ae7c02

4 files changed

Lines changed: 57 additions & 5 deletions

File tree

‎pantry.lock‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,11 +1547,11 @@
15471547
"source": "pantry",
15481548
"resolved": "registry:invisible-island.net/ncurses@6.6.0"
15491549
},
1550-
"libexpat.github.io@2.9.0": {
1550+
"libexpat.github.io@2.10.0": {
15511551
"name": "libexpat.github.io",
1552-
"version": "2.9.0",
1552+
"version": "2.10.0",
15531553
"source": "pantry",
1554-
"resolved": "registry:libexpat.github.io@2.9.0"
1554+
"resolved": "registry:libexpat.github.io@2.10.0"
15551555
},
15561556
"lz4.org@1.11.0": {
15571557
"name": "lz4.org",

‎storage/framework/core/actions/src/bump.ts‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { path as p } from '@stacksjs/path'
44
import { incrementVersion, isReleaseType, isValidVersion, versionBump } from '@stacksjs/bumpx'
55
import { isCalendarBump, nextCalendarVersion } from './calendar-version'
66
import { describeGitHubRelease, publishGitHubRelease } from './github-release'
7-
import { pantryLockViolations } from './pantry-lock'
7+
import { pantryLockViolations, preservePantrySystemPins } from './pantry-lock'
88
import { resolvePantryToolchain } from './pantry-toolchain'
99
import { releasePushTarget } from './release-push-target'
1010
import { generateChangelog, loadLogsmithConfig } from '@stacksjs/logsmith'
@@ -307,6 +307,11 @@ async function refreshPantryLock(): Promise<void> {
307307
})
308308
if (!existsSync(lockPath))
309309
throw new Error('Pantry completed without writing pantry.lock')
310+
if (isFrameworkRelease) {
311+
// Refresh JS workspace versions, retaining CI's native graph when no
312+
// system declaration changed. Pantry's platform resolution can differ.
313+
writeFileSync(lockPath, preservePantrySystemPins(readFileSync(lockPath, 'utf8'), previousLock.toString('utf8')))
314+
}
310315
}
311316
catch (error) {
312317
writeFileSync(lockPath, previousLock)

‎storage/framework/core/actions/src/pantry-lock.ts‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,25 @@ interface PantryLock {
4949
packages?: Record<string, unknown>
5050
}
5151

52+
/** An SDK version bump must not re-resolve an unchanged native toolchain for the publisher's OS. */
53+
export function preservePantrySystemPins(lock: string, baseline: string): string {
54+
const next = JSON.parse(lock) as PantryLock
55+
const previous = JSON.parse(baseline) as PantryLock
56+
const systems = (value: PantryLock) => Object.entries(value.workspaces ?? {})
57+
.filter(([, workspace]) => workspace.system && typeof workspace.system === 'object')
58+
.map(([path, workspace]) => [path, Object.entries(workspace.system as Record<string, unknown>).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)])
59+
.sort(([a], [b]) => String(a) < String(b) ? -1 : String(a) > String(b) ? 1 : 0)
60+
const declared = systems(previous)
61+
if (!declared.length || JSON.stringify(declared) !== JSON.stringify(systems(next))) return lock
62+
const native = (value: unknown) => !!value && typeof value === 'object' && (value as { source?: string }).source === 'pantry'
63+
const pinned = Object.entries(previous.packages ?? {}).filter(([, value]) => native(value))
64+
if (!pinned.length) return lock
65+
const packages = Object.fromEntries(Object.entries(next.packages ?? {}).filter(([, value]) => !native(value)))
66+
for (const [name, value] of pinned) packages[name] = value
67+
next.packages = Object.fromEntries(Object.entries(packages).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0))
68+
return `${JSON.stringify(next, null, 2)}\n`
69+
}
70+
5271
/**
5372
* Split a `packages` key into its name and version.
5473
*

‎storage/framework/core/actions/tests/pantry-lock.test.ts‎

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from 'bun:test'
2-
import { pantryLockViolations } from '../src/pantry-lock'
2+
import { pantryLockViolations, preservePantrySystemPins } from '../src/pantry-lock'
33

44
/**
55
* The release guard for `pantry.lock` (stacksjs/stacks#2843).
@@ -38,6 +38,34 @@ const lockOf = (value: unknown) => JSON.stringify(value)
3838
const committed = lockOf(sound)
3939
const clone = () => JSON.parse(committed) as typeof sound
4040

41+
describe('release native toolchain pins', () => {
42+
const previous = { ...sound, packages: { ...sound.packages,
43+
'libexpat.github.io@2.10.0': { name: 'libexpat.github.io', version: '2.10.0', source: 'pantry' },
44+
} }
45+
const regenerated = { ...sound, packages: {
46+
'pickier@0.1.66': { name: 'pickier', version: '0.1.66' },
47+
'libexpat.github.io@2.9.0': { name: 'libexpat.github.io', version: '2.9.0', source: 'pantry' },
48+
} }
49+
50+
it('keeps the validated system graph while refreshing JS resolutions', () => {
51+
const result = JSON.parse(preservePantrySystemPins(lockOf(regenerated), lockOf(previous)))
52+
expect(result.packages['libexpat.github.io@2.10.0'].version).toBe('2.10.0')
53+
expect(result.packages['libexpat.github.io@2.9.0']).toBeUndefined()
54+
expect(result.packages['pickier@0.1.66'].version).toBe('0.1.66')
55+
expect(result.packages['pickier@0.1.65']).toBeUndefined()
56+
})
57+
58+
it('allows an intentional native toolchain declaration change', () => {
59+
const changed = JSON.parse(lockOf(regenerated))
60+
changed.workspaces[''].system['bun.sh'] = '1.4.3'
61+
expect(preservePantrySystemPins(lockOf(changed), lockOf(previous))).toBe(lockOf(changed))
62+
})
63+
64+
it('does not invent a system graph for projects that declared none', () => {
65+
expect(preservePantrySystemPins(lockOf(regenerated), '{"packages":{}}')).toBe(lockOf(regenerated))
66+
})
67+
})
68+
4169
describe('pantryLockViolations', () => {
4270
it('rejects a self-consistent lock that still records the previous release declarations', () => {
4371
const expected = clone().workspaces

0 commit comments

Comments
 (0)