mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
fix(release): align Linux package smoke artifacts
Match Electron Builder Linux output by accepting linux-*-unpacked directories, treating AppImage blockmaps as optional, and validating release asset names against the generated x86_64/amd64 and arm64 artifacts. Tested: bun test scripts/quality-gate/package-smoke/index.test.ts Tested: bun test scripts/pr/release-workflow.test.ts scripts/quality-gate/package-smoke/index.test.ts Tested: bun run check:policy Tested: bun run check:native Tested: bun run verify Confidence: high Scope-risk: moderate
This commit is contained in:
parent
79d8468580
commit
d7eef0ea51
14
.github/workflows/release-desktop.yml
vendored
14
.github/workflows/release-desktop.yml
vendored
@ -230,18 +230,16 @@ jobs:
|
||||
;;
|
||||
Linux-x64)
|
||||
expected=(
|
||||
"Claude-Code-Haha-${APP_VERSION}-linux-x64.AppImage"
|
||||
"Claude-Code-Haha-${APP_VERSION}-linux-x64.AppImage.blockmap"
|
||||
"Claude-Code-Haha-${APP_VERSION}-linux-x64.deb"
|
||||
"Claude-Code-Haha-${APP_VERSION}-linux-x86_64.AppImage"
|
||||
"Claude-Code-Haha-${APP_VERSION}-linux-amd64.deb"
|
||||
"latest-linux.yml"
|
||||
)
|
||||
;;
|
||||
Linux-ARM64)
|
||||
expected=(
|
||||
"Claude-Code-Haha-${APP_VERSION}-linux-arm64.AppImage"
|
||||
"Claude-Code-Haha-${APP_VERSION}-linux-arm64.AppImage.blockmap"
|
||||
"Claude-Code-Haha-${APP_VERSION}-linux-arm64.deb"
|
||||
"latest-linux.yml"
|
||||
"latest-linux-arm64.yml"
|
||||
)
|
||||
;;
|
||||
Windows-x64)
|
||||
@ -366,11 +364,9 @@ jobs:
|
||||
"Claude-Code-Haha-${APP_VERSION}-mac-x64.dmg.blockmap"
|
||||
"Claude-Code-Haha-${APP_VERSION}-mac-x64.zip"
|
||||
"Claude-Code-Haha-${APP_VERSION}-mac-x64.zip.blockmap"
|
||||
"Claude-Code-Haha-${APP_VERSION}-linux-x64.AppImage"
|
||||
"Claude-Code-Haha-${APP_VERSION}-linux-x64.AppImage.blockmap"
|
||||
"Claude-Code-Haha-${APP_VERSION}-linux-x64.deb"
|
||||
"Claude-Code-Haha-${APP_VERSION}-linux-x86_64.AppImage"
|
||||
"Claude-Code-Haha-${APP_VERSION}-linux-amd64.deb"
|
||||
"Claude-Code-Haha-${APP_VERSION}-linux-arm64.AppImage"
|
||||
"Claude-Code-Haha-${APP_VERSION}-linux-arm64.AppImage.blockmap"
|
||||
"Claude-Code-Haha-${APP_VERSION}-linux-arm64.deb"
|
||||
"Claude-Code-Haha-${APP_VERSION}-win-x64.exe"
|
||||
"Claude-Code-Haha-${APP_VERSION}-win-x64.exe.blockmap"
|
||||
|
||||
@ -190,11 +190,9 @@ describe('release desktop workflow', () => {
|
||||
`Claude-Code-Haha-${version}-mac-x64.dmg.blockmap`,
|
||||
`Claude-Code-Haha-${version}-mac-x64.zip`,
|
||||
`Claude-Code-Haha-${version}-mac-x64.zip.blockmap`,
|
||||
`Claude-Code-Haha-${version}-linux-x64.AppImage`,
|
||||
`Claude-Code-Haha-${version}-linux-x64.AppImage.blockmap`,
|
||||
`Claude-Code-Haha-${version}-linux-x64.deb`,
|
||||
`Claude-Code-Haha-${version}-linux-x86_64.AppImage`,
|
||||
`Claude-Code-Haha-${version}-linux-amd64.deb`,
|
||||
`Claude-Code-Haha-${version}-linux-arm64.AppImage`,
|
||||
`Claude-Code-Haha-${version}-linux-arm64.AppImage.blockmap`,
|
||||
`Claude-Code-Haha-${version}-linux-arm64.deb`,
|
||||
`Claude-Code-Haha-${version}-win-x64.exe`,
|
||||
`Claude-Code-Haha-${version}-win-x64.exe.blockmap`,
|
||||
@ -224,6 +222,7 @@ describe('release desktop workflow', () => {
|
||||
expect(expectedReleaseAssets.filter((name) => name.endsWith('.AppImage')).length).toBe(2)
|
||||
expect(expectedReleaseAssets.filter((name) => name.endsWith('.deb')).length).toBe(2)
|
||||
expect(expectedReleaseAssets.filter((name) => name.endsWith('.exe')).length).toBe(1)
|
||||
expect(expectedReleaseAssets.some((name) => name.includes('-linux-') && name.endsWith('.blockmap'))).toBe(false)
|
||||
for (const platform of ['mac', 'linux', 'win']) {
|
||||
expect(expectedReleaseAssets.some((name) => name.includes(`-${platform}-`))).toBe(true)
|
||||
}
|
||||
@ -244,8 +243,8 @@ describe('release desktop workflow', () => {
|
||||
'Claude-Code-Haha-${APP_VERSION}-mac-arm64.zip',
|
||||
'Claude-Code-Haha-${APP_VERSION}-mac-x64.dmg',
|
||||
'Claude-Code-Haha-${APP_VERSION}-mac-x64.zip',
|
||||
'Claude-Code-Haha-${APP_VERSION}-linux-x64.AppImage',
|
||||
'Claude-Code-Haha-${APP_VERSION}-linux-x64.deb',
|
||||
'Claude-Code-Haha-${APP_VERSION}-linux-x86_64.AppImage',
|
||||
'Claude-Code-Haha-${APP_VERSION}-linux-amd64.deb',
|
||||
'Claude-Code-Haha-${APP_VERSION}-linux-arm64.AppImage',
|
||||
'Claude-Code-Haha-${APP_VERSION}-linux-arm64.deb',
|
||||
'Claude-Code-Haha-${APP_VERSION}-win-x64.exe',
|
||||
@ -259,6 +258,9 @@ describe('release desktop workflow', () => {
|
||||
expect(publishJob).toContain(`artifacts/update-metadata-standard/$file`)
|
||||
expect(publishJob).toContain(metadata)
|
||||
}
|
||||
expect(buildJob).not.toContain('linux-x64.AppImage.blockmap')
|
||||
expect(buildJob).not.toContain('linux-arm64.AppImage.blockmap')
|
||||
expect(buildJob).toContain('latest-linux-arm64.yml')
|
||||
expect(buildJob).toContain('Missing release assets for %s')
|
||||
expect(publishJob).toContain('Missing complete release assets')
|
||||
expect(publishJob).toContain('Missing standard update metadata')
|
||||
|
||||
@ -392,6 +392,52 @@ describe('packaged artifact inspection', () => {
|
||||
expect(report.passedChecks.some((check) => check.label.includes('update metadata referenced artifact'))).toBe(true)
|
||||
})
|
||||
|
||||
test('passes Linux release checks for Electron Builder output without AppImage blockmap', async () => {
|
||||
const rootDir = createRepoRoot()
|
||||
tempDirs.push(rootDir)
|
||||
|
||||
writeFile(rootDir, 'desktop/build-artifacts/electron/Claude-Code-Haha-0.3.1-linux-x86_64.AppImage')
|
||||
writeFile(rootDir, 'desktop/build-artifacts/electron/Claude-Code-Haha-0.3.1-linux-amd64.deb')
|
||||
writeFile(rootDir, 'desktop/build-artifacts/electron/linux-unpacked/resources/app.asar')
|
||||
writeFile(rootDir, 'desktop/build-artifacts/electron/linux-unpacked/resources/app-update.yml')
|
||||
writeFile(rootDir, 'desktop/build-artifacts/electron/linux-unpacked/resources/app.asar.unpacked/src-tauri/binaries/claude-sidecar-x86_64-unknown-linux-gnu')
|
||||
writeFile(rootDir, 'desktop/build-artifacts/electron/linux-unpacked/resources/app.asar.unpacked/node_modules/node-pty/package.json')
|
||||
writeFile(rootDir, 'desktop/build-artifacts/electron/linux-unpacked/resources/app.asar.unpacked/node_modules/node-pty/build/Release/pty.node')
|
||||
writeFile(rootDir, 'desktop/build-artifacts/electron/latest-linux.yml', 'path: Claude-Code-Haha-0.3.1-linux-x86_64.AppImage\n')
|
||||
|
||||
const report = await inspectPackagedArtifacts(rootDir, {
|
||||
platform: 'linux',
|
||||
packageKind: 'release',
|
||||
artifactsDir: 'desktop/build-artifacts/electron',
|
||||
})
|
||||
|
||||
expect(report.passed).toBe(true)
|
||||
expect(report.missingChecks.some((check) => check.label.includes('blockmap'))).toBe(false)
|
||||
})
|
||||
|
||||
test('accepts Electron Builder linux-arm64-unpacked output directory', async () => {
|
||||
const rootDir = createRepoRoot()
|
||||
tempDirs.push(rootDir)
|
||||
|
||||
writeFile(rootDir, 'desktop/build-artifacts/electron/Claude-Code-Haha-0.3.1-linux-arm64.AppImage')
|
||||
writeFile(rootDir, 'desktop/build-artifacts/electron/Claude-Code-Haha-0.3.1-linux-arm64.deb')
|
||||
writeFile(rootDir, 'desktop/build-artifacts/electron/linux-arm64-unpacked/resources/app.asar')
|
||||
writeFile(rootDir, 'desktop/build-artifacts/electron/linux-arm64-unpacked/resources/app-update.yml')
|
||||
writeFile(rootDir, 'desktop/build-artifacts/electron/linux-arm64-unpacked/resources/app.asar.unpacked/src-tauri/binaries/claude-sidecar-aarch64-unknown-linux-gnu')
|
||||
writeFile(rootDir, 'desktop/build-artifacts/electron/linux-arm64-unpacked/resources/app.asar.unpacked/node_modules/node-pty/package.json')
|
||||
writeFile(rootDir, 'desktop/build-artifacts/electron/linux-arm64-unpacked/resources/app.asar.unpacked/node_modules/node-pty/prebuilds/linux-arm64/pty.node')
|
||||
writeFile(rootDir, 'desktop/build-artifacts/electron/latest-linux-arm64.yml', 'path: Claude-Code-Haha-0.3.1-linux-arm64.AppImage\n')
|
||||
|
||||
const report = await inspectPackagedArtifacts(rootDir, {
|
||||
platform: 'linux',
|
||||
packageKind: 'release',
|
||||
artifactsDir: 'desktop/build-artifacts/electron',
|
||||
})
|
||||
|
||||
expect(report.passed).toBe(true)
|
||||
expect(report.passedChecks.some((check) => check.path.includes('linux-arm64-unpacked/resources/app.asar'))).toBe(true)
|
||||
})
|
||||
|
||||
test('passes Linux directory-only checks for electron-builder --dir output', async () => {
|
||||
const rootDir = createRepoRoot()
|
||||
tempDirs.push(rootDir)
|
||||
|
||||
@ -283,6 +283,22 @@ function addBlockmapChecks(
|
||||
}
|
||||
}
|
||||
|
||||
function findLinuxUnpackedDir(artifactsDir: string) {
|
||||
const unpackedDirs = findMatches(
|
||||
artifactsDir,
|
||||
(candidate) => /\/linux(?:-[a-z0-9_]+)?-unpacked$/.test(normalizePath(candidate)),
|
||||
{ directoriesOnly: true },
|
||||
)
|
||||
|
||||
return unpackedDirs.sort((left, right) => {
|
||||
const leftName = normalizePath(left).split('/').pop()
|
||||
const rightName = normalizePath(right).split('/').pop()
|
||||
if (leftName === 'linux-unpacked') return -1
|
||||
if (rightName === 'linux-unpacked') return 1
|
||||
return left.localeCompare(right)
|
||||
})[0]
|
||||
}
|
||||
|
||||
function firstDiagnosticLine(output: string) {
|
||||
return output
|
||||
.split(/\r?\n/)
|
||||
@ -614,23 +630,24 @@ function inspectLinuxArtifacts(rootDir: string, report: PackageSmokeReport) {
|
||||
report.artifactsDir,
|
||||
(candidate) => candidate.endsWith('.AppImage') || candidate.endsWith('.deb'),
|
||||
)
|
||||
const unpackedDir = findMatches(report.artifactsDir, (candidate) => normalizePath(candidate).endsWith('/linux-unpacked'), { directoriesOnly: true })[0]
|
||||
const electronDir = join(report.artifactsDir, 'electron')
|
||||
const unpackedDir = findLinuxUnpackedDir(report.artifactsDir)
|
||||
const updateMetadata = findMatches(report.artifactsDir, (candidate) => /latest-linux(?:-[a-z0-9]+)?\.yml$/.test(candidate))
|
||||
const appImageBlockmaps = findMatches(report.artifactsDir, (candidate) => candidate.endsWith('.AppImage.blockmap'))
|
||||
const releaseMode = report.packageKind === 'release' || (report.packageKind === 'auto' && (packagedArtifacts.length > 0 || updateMetadata.length > 0))
|
||||
|
||||
report.packagedArtifacts.push(...packagedArtifacts.map((candidate) => ({
|
||||
label: candidate.endsWith('.deb') ? 'Linux deb package' : 'Linux AppImage',
|
||||
path: toRelative(rootDir, candidate),
|
||||
})))
|
||||
report.optionalArtifacts.push(...appImageBlockmaps.map((candidate) => ({
|
||||
label: 'Linux AppImage blockmap',
|
||||
path: toRelative(rootDir, candidate),
|
||||
})))
|
||||
if (releaseMode) {
|
||||
addUpdateMetadataChecks(report, rootDir, updateMetadata)
|
||||
addBlockmapChecks(
|
||||
report,
|
||||
rootDir,
|
||||
'Linux AppImage update artifact',
|
||||
packagedArtifacts.filter(candidate => candidate.endsWith('.AppImage')),
|
||||
)
|
||||
if (appImageBlockmaps.length === 0 && packagedArtifacts.some(candidate => candidate.endsWith('.AppImage'))) {
|
||||
report.notes.push('Linux AppImage blockmaps were not required because Electron Builder did not emit them for this artifact set.')
|
||||
}
|
||||
}
|
||||
|
||||
if (releaseMode) {
|
||||
@ -639,12 +656,12 @@ function inspectLinuxArtifacts(rootDir: string, report: PackageSmokeReport) {
|
||||
rootDir,
|
||||
'linux packaged artifact (.AppImage or .deb)',
|
||||
packagedArtifacts,
|
||||
electronDir,
|
||||
report.artifactsDir,
|
||||
)
|
||||
} else if (!unpackedDir) {
|
||||
report.missingChecks.push({
|
||||
label: 'linux packaged artifact (.AppImage or .deb)',
|
||||
path: toRelative(rootDir, electronDir),
|
||||
path: toRelative(rootDir, report.artifactsDir),
|
||||
})
|
||||
} else {
|
||||
report.notes.push('No .AppImage or .deb was found; treating linux-unpacked as a directory-only development package.')
|
||||
@ -680,8 +697,8 @@ function inspectLinuxArtifacts(rootDir: string, report: PackageSmokeReport) {
|
||||
} else {
|
||||
if (releaseMode) {
|
||||
report.missingChecks.push({
|
||||
label: 'Linux unpacked directory (linux-unpacked) for static resource inspection',
|
||||
path: toRelative(rootDir, join(electronDir, 'linux-unpacked')),
|
||||
label: 'Linux unpacked directory (linux-unpacked or linux-*-unpacked) for static resource inspection',
|
||||
path: toRelative(rootDir, join(report.artifactsDir, 'linux-unpacked')),
|
||||
})
|
||||
} else {
|
||||
report.notes.push('linux-unpacked was not found, so this check only verified packaged artifact presence.')
|
||||
@ -692,7 +709,7 @@ function inspectLinuxArtifacts(rootDir: string, report: PackageSmokeReport) {
|
||||
if (releaseMode && updateMetadata.length === 0) {
|
||||
report.missingChecks.push({
|
||||
label: 'Linux update metadata (latest-linux*.yml)',
|
||||
path: toRelative(rootDir, join(electronDir, 'latest-linux.yml')),
|
||||
path: toRelative(rootDir, join(report.artifactsDir, 'latest-linux.yml')),
|
||||
})
|
||||
}
|
||||
if (report.hostPlatform !== 'linux') {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user