fix(marketplace): flush registry files with Windows-compatible handles

This commit is contained in:
Forge
2026-09-22 06:08:03 +00:00
parent 35d7d8c100
commit 63de525482
7 changed files with 20 additions and 6 deletions
+3
View File
@@ -34,6 +34,9 @@ jobs:
timeout-minutes: 35
env:
CODEGRAPH_TELEMETRY: '0'
# This increment changes registry storage and hosted browser behavior.
# Prior core lifecycle/native-path/70-kill matrices remain recorded.
NATIVE_VALIDATION_SCOPE: marketplace
steps:
- uses: actions/checkout@v4
with:
@@ -168,4 +168,4 @@ async function cli(args, expected = 0) {
await page?.screenshot({ path: path.join(out, 'failure.png'), fullPage: true }).catch(() => {});
fs.writeFileSync(path.join(out, 'failure.json'), JSON.stringify({ checks, error: String(error.stack).replace(/token=[a-f0-9]+/g, 'token=[redacted]') }, null, 2)); throw error;
} finally { await browser?.close(); await https?.close(); await bridge?.close(); await server?.close(); fs.rmSync(lab, { recursive: true, force: true }); }
})().catch(error => { console.error(String(error.stack).replace(/token=[a-f0-9]+/g, 'token=[redacted]')); process.exitCode = 1; });
})().catch(error => { console.error(String(error.stack).replace(/token=[a-f0-9]+/g, 'token=[redacted]')); if(error.cause) console.error(error.cause); process.exitCode = 1; });
+1 -1
View File
@@ -20,7 +20,7 @@ exports.httpsMarketplace = async function(port, out) {
try { const response = await fetch(url+'/api/health',{signal:AbortSignal.timeout(5000)}); if(response.ok && (await response.json()).ok) {
fs.writeFileSync(path.join(out,'https-transport.json'),JSON.stringify({origin:url,kind:'temporary-public-tunnel',durableHosting:false,verifiedAt:new Date().toISOString()},null,2));
return { origin:url, close };
}} catch { /* Wait for public route propagation. */ }
}} catch (error) { fs.appendFileSync(path.join(out,'https-network-errors.log'),new Date().toISOString()+' '+String(error)+' '+String(error.cause?.stack || '')+'\n'); }
}
await new Promise(resolve=>setTimeout(resolve,500));
}
+2 -1
View File
@@ -173,6 +173,7 @@ function routes(root) {
await popup.close();
await page.getByText('Reconnect CodeGraph', { exact: true }).waitFor();
checks.push('closed local companion expires the connection visibly');
await page.setViewportSize({ width: 1440, height: 1000 });
await page.getByRole('link', { name: 'Publish an extension' }).click();
await page.locator('#artifact').setInputFiles({ name: 'example.cgext', mimeType: 'application/json', buffer: artifact('4.0.0') });
for (const [id, value] of Object.entries({ name: 'Browser example', publisher: 'Recovery publisher', description: 'Outage check', source: 'https://example.com/source', readme: 'Outage check' })) await page.locator('#'+id).fill(value);
@@ -194,4 +195,4 @@ function routes(root) {
if (attacker) await new Promise(resolve => attacker.close(resolve));
fs.rmSync(temp, { recursive: true, force: true });
}
})().catch(error => { console.error(safeError(error)); process.exitCode = 1; });
})().catch(error => { console.error(safeError(error)); if(error.cause) console.error(error.cause); process.exitCode = 1; });
@@ -83,6 +83,11 @@ async function main() {
assert.deepEqual(await (await fetch(restart.origin+'/api/extensions')).json(),catalog);
assert.equal(storage.openMarketplaceVolume(volume).id,initialized.id); passed('fresh production process and different deployment cwd preserve catalog/identity/artifacts');
const backup = path.join(lab,'backup'); storage.backupMarketplaceVolume(volume,backup);
const pending = publish('in-flight-demo','1.0.0','before-commit'); await waitFor(()=>fs.existsSync(pending.ready));
const concurrentBackup = path.join(lab,'concurrent-backup'); storage.backupMarketplaceVolume(volume,concurrentBackup);
pending.proc.kill('SIGKILL'); await pending.done;
assert.deepEqual(storage.verifyMarketplaceDatabase(path.join(concurrentBackup,'registry.sqlite')),before);
passed('backup concurrent with uncommitted publication captures only complete releases');
const restored = path.join(lab,'restored'); storage.restoreMarketplaceVolume(backup,restored);
assert.deepEqual(storage.verifyMarketplaceDatabase(path.join(restored,'registry.sqlite')),before);
const isolated = await server(restored,lab);
+4
View File
@@ -8,6 +8,7 @@ const out = path.resolve(process.env.NATIVE_OUTPUT || `.qa/native/${process.plat
fs.mkdirSync(out, { recursive: true });
const version = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' }).trim();
const metadata = { revision: version, platform: process.platform, arch: process.arch, release: os.release(),
scope: process.env.NATIVE_VALIDATION_SCOPE || 'full', notRun: [],
node: process.version, runnerOS: process.env.RUNNER_OS, runnerArch: process.env.RUNNER_ARCH,
imageOS: process.env.ImageOS, imageVersion: process.env.ImageVersion,
githubRun: process.env.GITHUB_RUN_ID, githubAttempt: process.env.GITHUB_RUN_ATTEMPT,
@@ -20,6 +21,9 @@ const playwright = process.env.PLAYWRIGHT_MODULE || path.join(root, '.qa/browser
env.PLAYWRIGHT_MODULE = playwright;
env.CHROME_PATH = process.env.CHROME_PATH || require(playwright).chromium.executablePath();
async function run(name, command, args, timeout = 360000, overrides = {}) {
if (metadata.scope === 'marketplace' && !['build','marketplace-build','focused','registry-storage','external-author','compatibility','browser'].includes(name)) {
metadata.notRun.push({ name, reason: 'Unchanged core lifecycle; retain prior native evidence instead of rerunning' }); save(); return true;
}
const step = { name, command: [command, ...args], cwd: root, revision: version, overrides, started: new Date().toISOString(), status: 'running' };
metadata.steps.push(step); save(); console.log('START', name);
const logFile = path.join(out, name + '.log'); const log = fs.openSync(logFile, 'w');
+4 -3
View File
@@ -8,11 +8,12 @@ import { parsePackage, sha256 } from './package';
const databaseName = 'registry.sqlite';
const markerName = 'registry-volume.json';
function sync(file: string): void {
const fd = fs.openSync(file, 'r');
function sync(file: string, directory = false): void {
// Windows FlushFileBuffers requires a writable file handle.
const fd = fs.openSync(file, directory ? 'r' : 'r+');
try { fs.fsyncSync(fd); } finally { fs.closeSync(fd); }
}
function syncDir(directory: string): void { if (process.platform !== 'win32') sync(directory); }
function syncDir(directory: string): void { if (process.platform !== 'win32') sync(directory, true); }
function writeNew(file: string, data: string): void {
const fd = fs.openSync(file, 'wx', 0o600);
try { fs.writeFileSync(fd, data); fs.fsyncSync(fd); } finally { fs.closeSync(fd); }