From 70e6575f916a76eec87364c31d213ab69ca5729b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9A=A9=EC=9D=80?= Date: Fri, 24 Jul 2026 17:40:32 +0900 Subject: [PATCH] ci: diagnose the launch-smoke flake, pin artifact retention, gate demo GIFs (#247, #258) ## launch-smoke: not the flake it was filed as The failure on the run that prompted this was not `Broken pipe`, which is what #262 recorded. It was: Performing Streamed Install Success Starting: Intent { cmp=com.markleaf.notes/.MainActivity } Error type 3 Error: Activity class {...MainActivity} does not exist. `adb install` returns when the install session commits, which is before the package manager has the app indexed. Starting the activity in that window fails as though the build were broken. On a slow CI emulator the window is seconds wide; locally it is nothing, which is why this reads as random. The script now waits for `pm path` to answer before starting the activity, up to a minute, and fails with a distinct message if it never does. That is a real wait on the real signal rather than a sleep. The job stays `continue-on-error` -- one green run is not evidence -- but the flake now has a diagnosis instead of a reputation (#247, #252, #262). ## Artifact retention The three artifacts still on the 90-day default now carry explicit values and the reasoning sits next to them: 7 days for the two failure-only reports (Roborazzi diffs, lint HTML), which are read while the failure is fresh or not at all; 30 for the recorded goldens, which are downloaded and committed by hand and have to survive the gap between dispatching the run and getting round to it. ## Demo GIFs `verify-landing-versions.ps1` now asserts that each of the twelve public surfaces references its own `markleaf-tablet-.gif` and that the file exists. `README.ko.md` pointing at the English clip renders fine and would have shipped silently -- the same gap this script already closes for version strings. It runs in the required `build` check, so it covers the READMEs too, which the docs-only landing-check workflow does not. `docs/assets/README.md` records what the other two items asked for: the version each clip was recorded on, and why `markleaf-preview-720/1280.webp` are kept despite being referenced nowhere (their Pages URLs may be linked from outside the repository; ~93 KB total). Verified: bash -n on the script, YAML parse on the workflow, and the new check exercised both ways -- 12/12 pass as the tree stands, and pointing README.ko.md at the English clip fails it with the expected message. Co-Authored-By: Claude Opus 4.8 --- .github/scripts/launch-smoke.sh | 23 +++++++++++++ .github/workflows/android-build.yml | 12 +++++++ docs/assets/README.md | 52 +++++++++++++++++++++++++++++ scripts/verify-landing-versions.ps1 | 51 ++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+) create mode 100644 docs/assets/README.md diff --git a/.github/scripts/launch-smoke.sh b/.github/scripts/launch-smoke.sh index 94f9bf89..38b624a4 100644 --- a/.github/scripts/launch-smoke.sh +++ b/.github/scripts/launch-smoke.sh @@ -26,5 +26,28 @@ for attempt in 1 2 3; do sleep 5 done +# `adb install` returns when the install session commits, which is before the +# package manager has the app indexed. Starting the activity in that window +# fails with "Error type 3 / Activity class does not exist" -- the app is on +# disk, the launcher just cannot see it yet. That failure looks identical to a +# genuinely broken build, and it is what the job's reputation as a flake was +# partly built on (#247, #252, #262). +# +# Wait for the package manager to answer instead of sleeping a fixed amount: +# a slow CI emulator can take several seconds, a fast one none at all. +echo "Waiting for the package manager to see the app" +for _ in $(seq 1 30); do + if adb shell pm path com.markleaf.notes 2>/dev/null | tr -d '\r' | grep -q '^package:'; then + echo "Package indexed." + break + fi + sleep 2 +done + +if ! adb shell pm path com.markleaf.notes 2>/dev/null | tr -d '\r' | grep -q '^package:'; then + echo "Package manager never listed com.markleaf.notes after a successful install" + exit 1 +fi + adb shell am start -W -n com.markleaf.notes/.MainActivity adb shell pidof com.markleaf.notes diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml index 913bb1d1..b6bc753d 100644 --- a/.github/workflows/android-build.yml +++ b/.github/workflows/android-build.yml @@ -74,6 +74,10 @@ jobs: app/build/outputs/roborazzi/ app/build/reports/roborazzi/ if-no-files-found: ignore + # Only uploaded when the visual check fails, and only useful while + # someone is looking at that failure. A week outlives any run worth + # re-opening; the goldens themselves live in the repository (#247). + retention-days: 7 - name: Run release lint (hard gate) run: ./gradlew :app:lintRelease @@ -85,6 +89,9 @@ jobs: name: lint-report-release path: app/build/reports/lint-results-release.html if-no-files-found: ignore + # Same reasoning as the Roborazzi diffs: a failure-only report that is + # read while the failure is fresh, or not at all (#247). + retention-days: 7 - name: Build release APK with R8 (hard gate, unsigned ok) run: ./gradlew :app:assembleRelease @@ -245,6 +252,11 @@ jobs: name: roborazzi-goldens-linux path: app/src/test/snapshots/roborazzi/ if-no-files-found: error + # Longer than the failure-only artifacts: this one is downloaded and + # committed by hand, so it has to survive the gap between dispatching + # the run and getting round to the commit. Still not 90 days — once + # committed the goldens are in the repository (#247). + retention-days: 30 release: if: startsWith(github.ref, 'refs/tags/v') diff --git a/docs/assets/README.md b/docs/assets/README.md new file mode 100644 index 00000000..04dba725 --- /dev/null +++ b/docs/assets/README.md @@ -0,0 +1,52 @@ +# Public assets + +What each file here is for, and — for the ones recorded from a running build — +which version they show. Two things went unrecorded before this file existed: +why unreferenced files are kept, and how anyone would notice a clip has gone +stale (#258). + +## Demo clips + +| File | Surface | Recorded on | +|---|---|---| +| `markleaf-tablet-.gif` (6) | Landing story 01 + README hero, one per language | v2.30.0 | +| `markleaf-demo.gif` | Older single-language demo | v2.23.0 | + +`scripts/verify-landing-versions.ps1` asserts that each language surface points +at its **own** clip and that the file exists — a page referencing another +language's GIF renders fine and would otherwise ship silently. + +Nothing asserts that a clip still matches the current UI. That is a judgement +call, not a check: re-record when a change would make the clip misleading, and +update the version above when you do. A clip showing an older layout is only a +problem once the layout it shows is gone. + +## Screenshots + +`markleaf-editor-*.webp`, `markleaf-privacy-*.webp`, `markleaf-tags-*.webp` — +the landing page screenshots, in 720px and 1280px variants. The version they +show is carried in each page's `
`, which +`verify-landing-versions.ps1` requires to agree across the six languages. + +### `markleaf-preview-720.webp` / `markleaf-preview-1280.webp` — kept deliberately + +**Nothing in this repository references these two.** They are kept anyway, and +this is the record of that decision so a future cleanup does not have to guess. + +They were served from GitHub Pages under stable URLs +(`https://jeiel85.github.io/markleaf-android/assets/markleaf-preview-1280.webp`) +while they were in use, and those URLs may be linked from places outside this +repository — a store listing draft, a forum post, a bookmark. Deleting the +files breaks those links for no benefit: together they are ~93 KB and cost +nothing to keep. + +Remove them only if the Pages site itself moves or is retired, which breaks the +URLs regardless. + +## Store and brand + +| File | Use | +|---|---| +| `markleaf-feature-graphic-1024x500.png` | Play Console feature graphic | +| `markleaf-playstore-icon-512.png` | Play Console app icon | +| `logo.svg` | Landing header | diff --git a/scripts/verify-landing-versions.ps1 b/scripts/verify-landing-versions.ps1 index a352c2b0..6bab4298 100644 --- a/scripts/verify-landing-versions.ps1 +++ b/scripts/verify-landing-versions.ps1 @@ -149,6 +149,57 @@ foreach ($file in $readmeFiles) { } } +# ---- 언어별 데모 GIF: 각 페이지가 자기 언어 클립을 가리키는지 ---- +# +# README.ko.md 가 markleaf-tablet-en.gif 를 가리켜도 렌더링은 멀쩡하므로 조용히 +# 나간다 — 버전 표기에서 이 검사가 막아 주는 것과 같은 종류의 구멍이다 (#258). +# 언어 코드는 파일명 규칙에서 끌어온다: index.html/README.md 는 en, 나머지는 접미사. +$assetPairs = @( + @{ File = "docs/index.html"; Lang = "en" } + @{ File = "docs/index.ko.html"; Lang = "ko" } + @{ File = "docs/index.ja.html"; Lang = "ja" } + @{ File = "docs/index.de.html"; Lang = "de" } + @{ File = "docs/index.es.html"; Lang = "es" } + @{ File = "docs/index.fr.html"; Lang = "fr" } + @{ File = "README.md"; Lang = "en" } + @{ File = "README.ko.md"; Lang = "ko" } + @{ File = "README.ja.md"; Lang = "ja" } + @{ File = "README.de.md"; Lang = "de" } + @{ File = "README.es.md"; Lang = "es" } + @{ File = "README.fr.md"; Lang = "fr" } +) + +Write-Host "`n데모 GIF ($($assetPairs.Count)개 표면)" +foreach ($pair in $assetPairs) { + $path = Resolve-UnderRoot $pair.File + if (-not (Test-Path -LiteralPath $path)) { + Add-Failure (" FAIL {0,-20} 파일이 없습니다." -f $pair.File) + continue + } + $text = Get-Content -Raw -Encoding utf8 -LiteralPath $path + $expected = "markleaf-tablet-$($pair.Lang).gif" + + # 이 표면이 참조하는 태블릿 GIF 를 전부 모아, 자기 언어 것만 있는지 본다. + $referenced = @( + [regex]::Matches($text, 'markleaf-tablet-([a-z]{2})\.gif') | + ForEach-Object { $_.Value } | Sort-Object -Unique + ) + + if ($referenced.Count -eq 0) { + Add-Failure (" FAIL {0,-20} 태블릿 데모 GIF 참조가 없습니다 (기대 {1})." -f $pair.File, $expected) + } elseif ($referenced -contains $expected -and $referenced.Count -eq 1) { + # 에셋 자체가 있어야 참조가 의미를 갖는다. + $assetPath = Join-Path $docsPath "assets/$expected" + if (Test-Path -LiteralPath $assetPath) { + Write-Host (" OK {0,-20} {1}" -f $pair.File, $expected) -ForegroundColor Green + } else { + Add-Failure (" FAIL {0,-20} {1} 을 가리키는데 docs/assets 에 그 파일이 없습니다." -f $pair.File, $expected) + } + } else { + Add-Failure (" FAIL {0,-20} 기대 {1}, 실제 -> {2}" -f $pair.File, $expected, ($referenced -join ', ')) + } +} + # ---- 스크린샷(figcaption): 릴리스 버전과 별개, 언어 간 일치만 확인 ---- $screenshotUnique = @($screenshotVersions | Sort-Object -Unique) if ($screenshotUnique.Count -le 1) {