fix(install-e2e): windows transcripts were empty; player gets #zip= hash + one player per run
Windows transcripts were ZERO bytes: ts-prefix.ps1 formatted with
{0:D2}, but Floor() returns a double and the D specifier is
integer-only - it threw per line, and under the driver's relaxed EAP
every line errored into the void. {0:00} fixes it (custom numeric
format works on doubles). Reproduced the exact pipeline locally
(empty file + Format specifier invalid), verified the fix produces
prefixed merged stdout+stderr with exit code intact. That is also
why the log timeline never auto-synced: there was nothing in the
files to sync.
The GitHub artifact URL 307s to /suites/... server-side and strips
the ?zip= query param. The player now reads the zip URL from a
#zip= HASH param (client-side, survives the redirect) with ?zip=
as fallback; the hash path was verified in a real browser against a
real leg zip (auto-fetch + boot).
Per ethie's design, one player artifact for the whole run: new
leg-player job uploads playback.html (archive:false) before the
matrix legs, the report job needs it, and each ran cell gets TWO
links - 📼 to the player with #zip=<that leg's logs zip> and ⬇️ to
the raw zip. Per-leg player uploads removed from all three run
workflows.
This commit is contained in:
@@ -269,6 +269,19 @@ async function loadZip(url) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the zip URL: hash param wins (?zip= gets stripped by GitHub's
|
||||
* artifact redirect, the hash survives), then the query param.
|
||||
* @returns {string | null}
|
||||
*/
|
||||
function resolveZipUrl() {
|
||||
const m = location.hash.match(/(?:^|[#&])zip=([^&]+)/);
|
||||
if (m) {
|
||||
try { return decodeURIComponent(m[1]); } catch { return m[1]; }
|
||||
}
|
||||
return new URLSearchParams(location.search).get('zip');
|
||||
}
|
||||
|
||||
/* drag-drop + file picker: the universal transport for auth-gated zips */
|
||||
const fileInput = document.createElement('input');
|
||||
fileInput.type = 'file';
|
||||
@@ -339,13 +352,15 @@ function boot() {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const zipUrl = params.get('zip');
|
||||
const zipUrl = resolveZipUrl();
|
||||
if (zipUrl) {
|
||||
// Attempt the direct fetch (works when the page is served on
|
||||
// github.com itself — same-origin, session cookies flow). On failure
|
||||
// the status line becomes a download link + drop target.
|
||||
if (await loadZip(zipUrl)) boot();
|
||||
} else {
|
||||
// No ?zip=: this is the manual path — the leg player artifact page or
|
||||
// a local copy. Drop or pick a leg zip.
|
||||
// No zip param: this is the manual path — the leg player artifact
|
||||
// page or a local copy. Drop or pick a leg zip.
|
||||
statusEl.className = 'error';
|
||||
statusEl.innerHTML =
|
||||
'Drop a leg artifact zip anywhere on this page (or click here to pick the file).';
|
||||
|
||||
@@ -13,6 +13,9 @@ $script:TsPrefixStart = Get-Date
|
||||
function Add-TsPrefix {
|
||||
process {
|
||||
$t = (Get-Date) - $script:TsPrefixStart
|
||||
"[+{0:D2}:{1:D2}] {2}" -f [math]::Floor($t.TotalMinutes), [math]::Floor($t.TotalSeconds % 60), $_
|
||||
# {0:00} not {0:D2}: Floor() returns a double and the D specifier
|
||||
# is integer-only - it throws per line, and under the driver's
|
||||
# relaxed EAP every line errors into the void (empty transcripts).
|
||||
"[+{0:00}:{1:00}] {2}" -f [math]::Floor($t.TotalMinutes), [math]::Floor($t.TotalSeconds % 60), $_
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user