fix(release): drop the build_ref input — a dispatch builds the ref it is dispatched on (#12032)

Twin of #12022 on main: CodeQL flagged the same input-controlled checkout + npm cache pattern (cache-poisoning/poisonable-step) on main since it's the default branch. Checkouts go back to github.ref; dispatch still works via --ref (documented in the workflow's own on: contract).

Also fixes the packaged-app smoke: it now waits on /api/monitoring/health (which touches the DB) instead of /login (which doesn't), so the smoke can actually distinguish "native driver selected" from "database never opened." electron-smoke-script.test.ts 9/9 (2 new cases).
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-08-29 15:43:00 -03:00
committed by GitHub
parent 9dc6500ebd
commit 4febee9415
3 changed files with 113 additions and 17 deletions

View File

@@ -123,6 +123,48 @@ function discoverPackagedExecutable() {
throw new Error(`Packaged Electron smoke check does not support ${platform()}.`);
}
/**
* The packaged app opens SQLite lazily: `/login` (the readiness URL) never touches the
* database, so a smoke that only waits for readiness sees no `[DB]` line at all. After
* readiness the smoke requests a DB-backed endpoint and waits for evidence that the
* database opened. The primary open path does NOT print "[DB] Driver: ..." (only the
* recovery path and the sql.js fallback do), so the evidence is any `[DB]`/`[Migration]`
* startup line — and the #7592 guard below rejects the fallback's own line explicitly.
*/
export const DB_TOUCH_PATH = "/api/monitoring/health";
export const DB_OPEN_EVIDENCE_PATTERN =
/\[DB\] (Driver: |SQLite database ready|Added [^\n]* column|Changing cache_size|cache_size changed)|\[Migration\] (Applied|Pre-migration backup)/;
export async function waitForDatabaseOpen(getLogs, { timeoutMs = 15_000, pollMs = 250 } = {}) {
const startedAt = Date.now();
while (Date.now() - startedAt < timeoutMs) {
const logs = getLogs();
assertNoFatalLogs(logs);
if (DB_OPEN_EVIDENCE_PATTERN.test(logs)) return logs;
await sleep(pollMs);
}
throw new Error(
`Packaged Electron app logged no [DB]/[Migration] startup line within ${timeoutMs}ms of ` +
`touching ${DB_TOUCH_PATH} — the database never opened, so the SQLite driver cannot be verified.`
);
}
async function openDatabaseForSmoke({ logs, smokeUrl }) {
const touchUrl = new URL(DB_TOUCH_PATH, smokeUrl).toString();
try {
const response = await fetchWithTimeout(touchUrl, 5_000);
console.log(
`[electron-smoke] touched ${touchUrl} (HTTP ${response.status}) to open the database`
);
} catch (error) {
console.log(
`[electron-smoke] touching ${touchUrl} failed (${error instanceof Error ? error.message : String(error)}) — waiting for the database anyway`
);
}
await waitForDatabaseOpen(() => logs.value);
console.log("[electron-smoke] database opened");
}
async function fetchWithTimeout(url, timeoutMs) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), timeoutMs);
@@ -450,8 +492,13 @@ export function assertNativeDriverSelected(logs) {
);
}
// The primary open path prints no "[DB] Driver: ..." line at all (only the recovery path and
// the sql.js fallback do), so a database that demonstrably opened WITHOUT the fallback's own
// line is the native driver — that is exactly what #7592 guards.
if (DB_OPEN_EVIDENCE_PATTERN.test(logs)) return;
throw new Error(
"Packaged Electron app logs contain no '[DB] Driver: ...' line — cannot confirm which SQLite " +
"Packaged Electron app logs show no database activity at all — cannot confirm which SQLite " +
"driver loaded."
);
}
@@ -506,7 +553,14 @@ async function waitForReady({ logs, smokeUrl, timeoutMs, settleMs, exitState })
* by the single-launch path and the cold-restart (two-launch) path so both
* exercise identical spawn/readiness/shutdown behavior.
*/
async function launchAndCollectLogs({ appExecutable, smokeUrl, dataDir, timeoutMs, settleMs, streamLogs }) {
async function launchAndCollectLogs({
appExecutable,
smokeUrl,
dataDir,
timeoutMs,
settleMs,
streamLogs,
}) {
const smokeEnv = buildSmokeEnv({ dataDir });
await assertPortIsFree(smokeUrl);
await ensureSmokeEnvDirs(smokeEnv, dataDir);
@@ -538,6 +592,8 @@ async function launchAndCollectLogs({ appExecutable, smokeUrl, dataDir, timeoutM
try {
await waitForReady({ logs, smokeUrl, timeoutMs, settleMs, exitState });
// Outside waitForReady on purpose: a missing database is a verdict, not a readiness retry.
await openDatabaseForSmoke({ logs, smokeUrl });
return logs.value;
} catch (error) {
if (!streamLogs) {
@@ -568,7 +624,14 @@ async function main() {
!process.env.ELECTRON_SMOKE_DATA_DIR && process.env.ELECTRON_SMOKE_KEEP_DATA !== "1";
try {
await launchAndCollectLogs({ appExecutable, smokeUrl, dataDir, timeoutMs, settleMs, streamLogs });
await launchAndCollectLogs({
appExecutable,
smokeUrl,
dataDir,
timeoutMs,
settleMs,
streamLogs,
});
if (!coldRestart) return;