fix(release): refuse to re-publish a version that already shipped

The feed publisher overwrote whatever version-specific assets it found, so a
re-run of an old release tag could quietly replace the installer that
customers already downloaded under that version number.

Publication now compares the bytes already in the version-specific registry
path and stops when they differ, while still allowing an identical re-run to
finish. The metadata verifier gained a negative case so the guard cannot be
removed unnoticed.
This commit is contained in:
Yun Chan 2026-09-16 23:49:37 +09:00
parent c3ddd36c6f
commit 49a4c97923
2 changed files with 35 additions and 1 deletions

View file

@ -196,6 +196,10 @@ function validate(surfaces) {
fail(surfaces.forgejoPublisher.includes('verifyPublicFile'), 'forgejo_publisher_public_verification_missing')
fail(surfaces.forgejoPublisher.includes('update-policy.json'), 'forgejo_publisher_policy_upload_missing')
fail(surfaces.forgejoPublisher.includes('CHANGELOG.md'), 'forgejo_publisher_changelog_gate_missing')
fail(
surfaces.forgejoPublisher.includes('assertVersionNotRepublished'),
'forgejo_publisher_rerelease_guard_missing',
)
fail(
surfaces.forgejoPublisher.includes('/api/packages/') &&
surfaces.forgejoPublisher.includes('generic'),
@ -309,6 +313,13 @@ if (process.argv.includes('--self-test')) {
},
'forgejo_publisher_asset_first_order_missing',
)
expectRejected(
surfaces,
(candidate) => {
candidate.forgejoPublisher = candidate.forgejoPublisher.replaceAll('assertVersionNotRepublished', 'uploadAnyway')
},
'forgejo_publisher_rerelease_guard_missing',
)
expectRejected(
surfaces,
(candidate) => {
@ -373,7 +384,7 @@ if (process.argv.includes('--self-test')) {
if (!missingDesktopKeyRejected) {
throw new Error('release_metadata_self_test_failed:desktop_license_public_key_missing')
}
result.negativeCases = 13
result.negativeCases = 14
}
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`)