fix(KAN-65): normalize Hetzner conditional write ETags
CI/CD / preconditions (pull_request) Successful in 1s
Native JavaScript checks / Native launcher and release tests (pull_request) Successful in 1m5s
macOS checks / Native Rust tests and unsigned build (pull_request) Successful in 1m34s
CI/CD / Publish immutable image (pull_request) Canceled after 0s
CI/CD / test_and_check_app (pull_request) Canceled after 5m16s
CI/CD / preconditions (pull_request) Successful in 1s
Native JavaScript checks / Native launcher and release tests (pull_request) Successful in 1m5s
macOS checks / Native Rust tests and unsigned build (pull_request) Successful in 1m34s
CI/CD / Publish immutable image (pull_request) Canceled after 0s
CI/CD / test_and_check_app (pull_request) Canceled after 5m16s
This commit is contained in:
@@ -5,9 +5,68 @@ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import test from 'node:test';
|
||||
import { publishMacos, storageConfiguration } from '../publish-macos.mjs';
|
||||
import {
|
||||
conditionalEtag,
|
||||
publishMacos,
|
||||
resumeMacos,
|
||||
storageConfiguration,
|
||||
} from '../publish-macos.mjs';
|
||||
import { signedFixture } from './updater-fixture.mjs';
|
||||
|
||||
test('conditional writes use bare S3 ETags without weakening the condition', () => {
|
||||
const etag = 'b88312b96169f4b2098a4cc43b0abb6a';
|
||||
for (const value of [etag, `"${etag}"`, `${etag}-2`, `"${etag}-2"`]) {
|
||||
assert.equal(conditionalEtag(value), value.replaceAll('"', ''));
|
||||
}
|
||||
for (const invalid of [
|
||||
undefined,
|
||||
null,
|
||||
'',
|
||||
'*',
|
||||
'"*"',
|
||||
`W/"${etag}"`,
|
||||
`"${etag}`,
|
||||
`${etag}"`,
|
||||
`${etag},${etag}`,
|
||||
`${etag}\r\n`,
|
||||
{},
|
||||
42,
|
||||
]) {
|
||||
assert.throws(() => conditionalEtag(invalid), /valid S3 ETag/);
|
||||
}
|
||||
});
|
||||
|
||||
test('resume verifies already published artifacts and promotes the feed without rebuilding', async (t) => {
|
||||
const f = fixture(t);
|
||||
await publishMacos(f);
|
||||
const key = `releases/macos/${f.metadata.target}/latest.json`;
|
||||
f.objects.set(key, Buffer.from(JSON.stringify({ version: '1.0.0' })));
|
||||
const originals = new Map(f.objects);
|
||||
await resumeMacos(f);
|
||||
assert.equal(JSON.parse(f.objects.get(key)).version, f.metadata.version);
|
||||
for (const [name, contents] of originals) {
|
||||
if (name !== key) {
|
||||
assert.deepEqual(f.objects.get(name), contents);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (const failure of ['missing', 'tampered']) {
|
||||
test(`resume rejects ${failure} artifacts before any S3 writes`, async (t) => {
|
||||
const f = fixture(t);
|
||||
await publishMacos(f);
|
||||
const key = `releases/macos/${f.metadata.target}/${f.metadata.version}/${f.metadata.updaterFilename}`;
|
||||
if (failure === 'missing') {
|
||||
f.objects.delete(key);
|
||||
} else {
|
||||
f.objects.set(key, Buffer.from('tampered'));
|
||||
}
|
||||
f.events.length = 0;
|
||||
await assert.rejects(resumeMacos(f), /incomplete release|signature/);
|
||||
assert(!f.events.some((event) => event.operation === 'put-object'));
|
||||
});
|
||||
}
|
||||
|
||||
function fixture(
|
||||
t,
|
||||
conflictMessage = 'aws failed: (PreconditionFailed) object exists',
|
||||
@@ -65,14 +124,19 @@ function fixture(
|
||||
if (!args.includes('--if-match')) {
|
||||
throw new Error(conflictMessage);
|
||||
}
|
||||
assert.equal(value('--if-match'), '"previous-etag"');
|
||||
assert.equal(
|
||||
value('--if-match'),
|
||||
'b88312b96169f4b2098a4cc43b0abb6a',
|
||||
);
|
||||
}
|
||||
objects.set(key, readFileSync(value('--body')));
|
||||
return '{}';
|
||||
}
|
||||
if (operation === 'get-object') {
|
||||
writeFileSync(args.at(-1), objects.get(key));
|
||||
return JSON.stringify({ ETag: '"previous-etag"' });
|
||||
return JSON.stringify({
|
||||
ETag: '"b88312b96169f4b2098a4cc43b0abb6a"',
|
||||
});
|
||||
}
|
||||
throw new Error(`Unexpected S3 operation: ${operation}`);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user