fix(ci): retain and retry the observed Playwright runtime corruption
This commit is contained in:
@@ -312,9 +312,16 @@ jobs:
|
|||||||
# checksum-valid npm download (`true` became `tr<garbage>e`, or a stray `break` appeared).
|
# checksum-valid npm download (`true` became `tr<garbage>e`, or a stray `break` appeared).
|
||||||
# Retry only that unmistakable source-corruption signature from a new isolated cache.
|
# Retry only that unmistakable source-corruption signature from a new isolated cache.
|
||||||
# Ordinary Axe violations and application assertions remain terminal.
|
# Ordinary Axe violations and application assertions remain terminal.
|
||||||
if grep -q "AxeBuilder.analyze" "$smoke_log" \
|
if { grep -q "AxeBuilder.analyze" "$smoke_log" \
|
||||||
&& grep -Eq "page\.evaluate: (ReferenceError|SyntaxError)" "$smoke_log"; then
|
&& grep -Eq "page\.evaluate: (ReferenceError|SyntaxError)" "$smoke_log"; } \
|
||||||
echo "Axe runtime source appears corrupted — reinstalling the locked dependencies from a fresh cache and retrying the complete suite once..."
|
|| { grep -Eq 'node_modules/playwright-core/lib/utilsBundle\.js:[0-9]+' "$smoke_log" \
|
||||||
|
&& grep -Fq 'const { items }$= this;' "$smoke_log" \
|
||||||
|
&& grep -Fq 'SyntaxError: Missing initializer in destructuring declaration' "$smoke_log"; }; then
|
||||||
|
echo "Browser runtime source appears corrupted — reinstalling the locked dependencies from a fresh cache and retrying the complete suite once..."
|
||||||
|
mkdir -p .artifacts/browser-runtime-attempt-1
|
||||||
|
cp "$smoke_log" .artifacts/browser-runtime-attempt-1/smoke.log
|
||||||
|
if [ -d test-results ]; then cp -a test-results .artifacts/browser-runtime-attempt-1/; fi
|
||||||
|
if [ -f .artifacts/playwright-results.json ]; then cp .artifacts/playwright-results.json .artifacts/browser-runtime-attempt-1/; fi
|
||||||
rm -rf node_modules
|
rm -rf node_modules
|
||||||
npm_cache="$(mktemp -d)"
|
npm_cache="$(mktemp -d)"
|
||||||
npm ci --no-audit --no-fund --cache "$npm_cache"
|
npm ci --no-audit --no-fund --cache "$npm_cache"
|
||||||
@@ -336,6 +343,7 @@ jobs:
|
|||||||
name: browser-diagnostics
|
name: browser-diagnostics
|
||||||
path: |
|
path: |
|
||||||
job-tracker-ui/.artifacts/playwright-results.json
|
job-tracker-ui/.artifacts/playwright-results.json
|
||||||
|
job-tracker-ui/.artifacts/browser-runtime-attempt-1/
|
||||||
job-tracker-ui/test-results
|
job-tracker-ui/test-results
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Playwright runtime corruption signature — 20 October 2026
|
||||||
|
|
||||||
|
Corrected release run831 did not reach deployment. Its browser lane encountered Chromium target crashes and an installed Playwright parser failure at `node_modules/playwright-core/lib/utilsBundle.js:35841`: `const { items }$= this;`, followed by `SyntaxError: Missing initializer in destructuring declaration`. A worker then exited before executing its cases. Only46 browser attempts appear in the terminal log; this is not a completed331-case regression. An ordinary preview-visibility assertion also failed and is retained, rather than labelled an infrastructure success.
|
||||||
|
|
||||||
|
This resembles earlier runner dependency/download corruption, but the job container was removed before its installed bytes could be compared with the lockfile archive. Neither persistent filesystem corruption nor faulty hardware is proven. The full900-entry diagnostic ZIP was downloaded and CRC-checked: SHA256 `f6d8e9659ca824bb12d283913f6e16f3933974bbc5e11c78e3524d8712e75632`. Original logs and traces remain retained.
|
||||||
|
|
||||||
|
The workflow already allows one complete-suite retry after its narrowly recognized Axe source-corruption signature, using a new npm cache and the unchanged lockfile. Extend that same bounded policy only when all three observed Playwright indicators occur together: the exact dependency path/line marker, the exact malformed statement and the exact parser error. Ordinary application assertion failures, target crashes alone, user-source syntax errors and incomplete signatures remain terminal. The six policy tests execute the actual Bash guard and pass. No assertion, browser inventory, timeout, ordinary retry, dependency integrity or release gate is relaxed.
|
||||||
|
|
||||||
|
Before the existing reinstall/retry, preserve the first smoke log, JSON report if available and complete traces/screenshots under `.artifacts/browser-runtime-attempt-1`; include them in the same always-uploaded artifact. A subsequent successful retry must still complete the entire browser suite; failure remains blocking. This does not repair or qualify the underlying runner, prove the preview assertion resolved or establish deployment success. Production/operator, physical hardware/runtime and manual application qualifications remain open. P5-13 and model activation are unchanged.
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
"""Exercise the actual workflow's narrow corruption guard, without starting CI."""
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
WORKFLOW = Path(__file__).resolve().parents[1] / '.gitea/workflows/ci-deploy.yml'
|
||||||
|
BASH = str(Path('C:/Program Files/Git/bin/bash.exe')) if os.name == 'nt' else shutil.which('bash')
|
||||||
|
|
||||||
|
|
||||||
|
class BrowserRuntimeRetryTests(unittest.TestCase):
|
||||||
|
def permits_retry(self, log):
|
||||||
|
text = WORKFLOW.read_text(encoding='utf-8')
|
||||||
|
end = text.index(' echo "Browser runtime source appears corrupted')
|
||||||
|
start = text.rfind('\n if ', 0, end) + 1
|
||||||
|
guard = text[start:end]
|
||||||
|
self.assertTrue(guard.strip().startswith('if '))
|
||||||
|
with tempfile.TemporaryDirectory() as folder:
|
||||||
|
fixture = Path(folder) / 'smoke.log'
|
||||||
|
fixture.write_text(log, encoding='utf-8')
|
||||||
|
result = subprocess.run([BASH, '-c', guard + '\nexit 0\nelse\nexit 1\nfi'],
|
||||||
|
env={**os.environ, 'smoke_log': fixture.as_posix()}, capture_output=True, text=True)
|
||||||
|
self.assertIn(result.returncode, [0, 1], result.stderr)
|
||||||
|
return result.returncode == 0
|
||||||
|
|
||||||
|
def test_captured_playwright_source_corruption(self):
|
||||||
|
self.assertTrue(self.permits_retry('/workspace/node_modules/playwright-core/lib/utilsBundle.js:35841\n'
|
||||||
|
'const { items }$= this;\nSyntaxError: Missing initializer in destructuring declaration'))
|
||||||
|
|
||||||
|
def test_existing_axe_source_corruption(self):
|
||||||
|
self.assertTrue(self.permits_retry('AxeBuilder.analyze\npage.evaluate: ReferenceError: invalid runtime source'))
|
||||||
|
|
||||||
|
def test_application_assertion_stays_terminal(self):
|
||||||
|
self.assertFalse(self.permits_retry('expect(region).not.toBeVisible() failed'))
|
||||||
|
|
||||||
|
def test_browser_crash_alone_stays_terminal(self):
|
||||||
|
self.assertFalse(self.permits_retry('Error: page.evaluate: Target crashed'))
|
||||||
|
|
||||||
|
def test_user_javascript_syntax_error_stays_terminal(self):
|
||||||
|
self.assertFalse(self.permits_retry('src/example.js:1\nconst { items }$= this;\nSyntaxError: Missing initializer in destructuring declaration'))
|
||||||
|
|
||||||
|
def test_incomplete_signature_stays_terminal(self):
|
||||||
|
self.assertFalse(self.permits_retry('node_modules/playwright-core/lib/utilsBundle.js:35841\nSyntaxError: Missing initializer in destructuring declaration'))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user