Skip to content

ci: fix the screenshot comment pipeline and post device render captures - #154

Merged
l2hyunwoo merged 3 commits into
mainfrom
ci/screenshot-comment-pipeline
Jul 11, 2026
Merged

ci: fix the screenshot comment pipeline and post device render captures#154
l2hyunwoo merged 3 commits into
mainfrom
ci/screenshot-comment-pipeline

Conversation

@l2hyunwoo

@l2hyunwoo l2hyunwoo commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

Problem

The screenshot comment workflow has failed on every run since it landed (see the run history of screenshot-comment.yml). Two independent causes:

  1. screenshot-test.yml uploads the summary from cloudy/build/test-results/roborazzi/debug/results-summary.json, but the KMP Android host test writes it under .../roborazzi/androidHostTest/. With if-no-files-found: warn, the screenshot-summary artifact silently never exists.
  2. screenshot-comment.yml has a fallback body for a missing summary, but the line above it, summary_json="$(find screenshot-summary ... | head -n1)", exits 1 under set -e -o pipefail when the directory is absent, killing the step before the fallback can run. That is the message-less exit code 1 in every failed run.

Fix

  • Upload the summary via a variant glob (roborazzi/**/results-summary.json) so a task rename cannot silently drop the artifact again.
  • Append || true to the find so a missing artifact takes the existing "summary unavailable" path instead of killing the step.

Also included: always-on golden gallery

The comment now inlines the committed reference screenshots (cloudy/src/androidHostTest/assets/screenshots/*.png) on every run, not just on a regression. verifyRoborazziAndroidHostTest writes no images on a clean pass, so previously a clean PR comment had nothing visual to show; the producer now uploads the checked-in goldens as a screenshot-goldens artifact and the comment renders them as a "Screenshots" table via the companion branch.

Also included: device render gallery

The comment workflow gains a "Device rendering" section that posts per-band emulator captures (band_api<SDK>_<case>_{actual,expected}.png) from device-screenshots-api* artifacts, next to the existing Roborazzi diff gallery. The section skips cleanly when no such artifacts exist, so runs from main and unrelated PRs are unaffected.

The producer side (an emulator matrix job and the instrumented test that writes those captures) lives in #152. Since workflow_run workflows always execute the default-branch file, that gallery can only take effect once this lands on main; #152 then exercises it end to end. This also fixes the SC2044 find-in-for-loop actionlint warning in the push step.

Summary by CodeRabbit

  • New Features

    • Pull requests now publish emulator/device screenshot galleries together with visual diffs in a single sticky comment.
    • Galleries are shown in a consistent order (goldens → Roborazzi diffs → device captures), with device images grouped by API level and test case (actual and optional expected).
  • Bug Fixes

    • Improved screenshot summary artifact handling to avoid failures when artifacts are missing.
    • Updated diff/gallery publishing logic and made screenshot summary uploads resilient to test variant path differences.

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Screenshot CI now uploads variant-aware summaries, downloads emulator captures, publishes available screenshot artifacts to companion branches, and renders golden, Roborazzi diff, and device galleries in a resilient sticky pull-request comment.

Changes

Screenshot CI and comment galleries

Layer / File(s) Summary
Variant-aware summary artifact upload
.github/workflows/screenshot-test.yml
Uploads results-summary.json from variant-specific Roborazzi output directories.
Capture download, detection, and publication
.github/workflows/screenshot-comment.yml
Downloads device captures, detects diff, device, and golden states, publishes available PNGs, and tolerates missing summary artifacts.
Gallery rendering and sticky comment composition
.github/workflows/screenshot-comment.yml
Renders golden, Roborazzi diff, and validated device galleries, then appends available sections to the sticky comment in a defined order.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ScreenshotTests as Screenshot tests
  participant CommentWorkflow as Comment workflow
  participant CompanionBranch as Companion branch
  participant StickyComment as Sticky comment
  ScreenshotTests->>CommentWorkflow: upload results-summary.json and screenshots
  CommentWorkflow->>CommentWorkflow: detect diff, device, and golden states
  CommentWorkflow->>CompanionBranch: publish available PNGs
  CommentWorkflow->>StickyComment: append summary and available galleries
Loading

Possibly related PRs

Suggested reviewers: skydoves

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the changes, but it does not follow the required template headings or include the examples/review sections. Rewrite the description using the repo template: add Goal, Implementation details, Explain examples, Preparing a pull request for review, and Code reviews sections.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and accurately summarizes the screenshot comment pipeline and device capture changes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/screenshot-comment-pipeline

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 11, 2026

Copy link
Copy Markdown

Snapshot diff report

✅ 8 screenshots verified, no changes.

Total Unchanged Changed Added
8 8 0 0

Full screenshot report (artifact)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
.github/workflows/screenshot-comment.yml (1)

157-157: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

zizmor template-injection here is a false positive — but move it to env for a clean scan.

${{ steps.pr.outputs.number }} is regex-guarded to ^[0-9]+$ at Line 54, so nothing attacker-controllable can reach this expansion. Not exploitable. Optional: reference the PR_NUMBER via env: (as the push/gallery steps already do with BRANCH_NAME) so zizmor stops flagging it and the guard survives future edits.

🔒 Optional: pass PR number via env
         env:
           BRANCH_NAME: companion_pr-${{ steps.pr.outputs.number }}
+          PR_NUMBER: ${{ steps.pr.outputs.number }}
         run: |
@@
-          git commit -m "Add screenshots for PR #${{ steps.pr.outputs.number }}"
+          git commit -m "Add screenshots for PR #${PR_NUMBER}"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/screenshot-comment.yml at line 157, Update the screenshot
commit step to pass the validated PR number through an env variable such as
PR_NUMBER, then reference that variable in the git commit message instead of
interpolating steps.pr.outputs.number directly. Preserve the existing numeric
validation and commit-message content.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/screenshot-comment.yml:
- Around line 106-131: Update the Check for images to publish step’s diff and
device detection predicates to match the gallery filters exactly: only accept
Roborazzi files under roborazzi/ for diff images, and only accept
device-screenshots files matching band_api<SDK>_<case>_{actual,expected}.png for
device images. Keep the existing safe-path validation and GITHUB_OUTPUT values,
but ensure check.diff and check.device cannot become true for files the gallery
later excludes.

---

Nitpick comments:
In @.github/workflows/screenshot-comment.yml:
- Line 157: Update the screenshot commit step to pass the validated PR number
through an env variable such as PR_NUMBER, then reference that variable in the
git commit message instead of interpolating steps.pr.outputs.number directly.
Preserve the existing numeric validation and commit-message content.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 67527b17-68d4-4e93-bc4e-f892b8788587

📥 Commits

Reviewing files that changed from the base of the PR and between 28af95f and 67303ad.

📒 Files selected for processing (2)
  • .github/workflows/screenshot-comment.yml
  • .github/workflows/screenshot-test.yml

Comment on lines 106 to +131
- id: check
name: Check for diff images
name: Check for images to publish
shell: bash
run: |
mapfile -t files < <(find . -type f -name "*_compare.png")
exist="false"
for f in "${files[@]}"; do
# Roborazzi diff images (only exist on a mismatch).
diff="false"
while IFS= read -r f; do
# Reject any path with characters outside the safe set before we touch git / build URLs.
if [[ "$f" =~ ^[a-zA-Z0-9_./-]+$ ]]; then
exist="true"
break
fi
done
echo "exist=$exist" >> "$GITHUB_OUTPUT"
[[ "$f" =~ ^[a-zA-Z0-9_./-]+$ ]] && { diff="true"; break; }
done < <(find . -type f -name "*_compare.png")

# Device render captures (band_api<SDK>_<case>_{actual,expected}.png) under device-screenshots/.
device="false"
if [[ -d device-screenshots ]]; then
while IFS= read -r f; do
[[ "$f" =~ ^[a-zA-Z0-9_./-]+$ ]] && { device="true"; break; }
done < <(find device-screenshots -type f -name "*.png")
fi

# --- Push diff images to an orphan companion branch (only when there are diffs) ---
any="false"
[[ "$diff" == "true" || "$device" == "true" ]] && any="true"
{
echo "diff=$diff"
echo "device=$device"
echo "any=$any"
} >> "$GITHUB_OUTPUT"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 1) Confirm the diff gate and the diff gallery operate on the same file set.
rg -n -e '_compare\.png' -e 'roborazzi/' -e 'device-screenshots' .github/workflows/screenshot-comment.yml

# 2) Confirm Roborazzi actually nests *_compare.png under a 'roborazzi/' path segment
#    (otherwise `grep roborazzi/` in the gallery never matches).
rg -n --iglob '*.gradle*' -e 'roborazzi' -e 'outputDir' -e 'compareOutputDir' -e 'resultsDir'

# 3) Show how device captures are named to validate the regex contract with PR `#152`.
rg -n -e 'band_api' -e 'device-screenshots' -g '!**/build/**'

Repository: skydoves/Cloudy

Length of output: 1422


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant workflow sections around the gate and render logic.
sed -n '100,320p' .github/workflows/screenshot-comment.yml | cat -n

# Show the exact snippets that decide whether rows are emitted for each gallery.
rg -n -A6 -B6 'grep "roborazzi/"|device_gallery|band_api\([0-9]+\)_\(.+\)_(actual|expected)' .github/workflows/screenshot-comment.yml

Repository: skydoves/Cloudy

Length of output: 15902


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find Roborazzi configuration and screenshot producers.
git ls-files | rg -n '(^|/)(build\.gradle(\.kts)?|settings\.gradle(\.kts)?|.*(gradle|kts|kt|java|xml|yml|yaml)$)' >/tmp/allfiles.txt || true
rg -n --glob '!**/build/**' -e 'roborazzi' -e 'compareOutputDir' -e 'outputDir' -e 'device-screenshots' -e 'band_api' .

# Narrow to likely producer/test files if present.
fd -H -I '.*(Roborazzi|Screenshot|MirageBand).*' .

Repository: skydoves/Cloudy

Length of output: 3166


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the Roborazzi setup and screenshot test file names.
sed -n '240,340p' cloudy/build.gradle.kts | cat -n
printf '\n---\n'
sed -n '1,220p' cloudy/src/androidHostTest/kotlin/com/skydoves/cloudy/ScreenshotTestSupport.kt | cat -n
printf '\n---\n'
sed -n '1,220p' cloudy/src/androidDeviceTest/kotlin/com/skydoves/cloudy/LiquidGlassScreenshotTest.kt | cat -n
printf '\n---\n'
sed -n '1,220p' cloudy/src/androidDeviceTest/kotlin/com/skydoves/cloudy/SkyBackdropScreenshotTest.kt | cat -n

Repository: skydoves/Cloudy

Length of output: 22772


🌐 Web query:

Roborazzi default compare output directory roborazzi compare png path

💡 Result:

The default output directory for Roborazzi—used for reference images and comparison artifacts—is module/build/outputs/roborazzi [1][2]. You can customize where these files are generated using the following methods: 1. Gradle Configuration: You can set the global output directory in your build.gradle file using the roborazzi.outputDir parameter [3]: roborazzi { outputDir = "src/your/screenshot/folder" } 2. Comparison-Specific Output: For specifically managing the output directory of comparison images (e.g., _compare.png and _actual.png files), you can use the roborazzi.compare.outputDir property (introduced in version 1.36.0) or the outputDirectoryPath option within CompareOptions [4][5]. Note that Roborazzi distinguishes between where reference (golden) images are stored and where comparison artifacts are generated [4]. If you need to separate your workflow (e.g., recording vs. verification), you can dynamically adjust these paths in your Gradle configuration based on the task being executed [5].

Citations:


🌐 Web query:

site:github.com takahirom roborazzi compareOutputDir roborazzi png path default

💡 Result:

In Roborazzi, the default directory for both reference images and comparison results is module/build/outputs/roborazzi [1][2]. Regarding your query about the output path, note that Roborazzi distinguishes between how it handles recorded images (golden snapshots) and comparison results (diffs/actual images). 1. Default Path: By default, screenshots captured via captureRoboImage are often saved relative to the current module directory (e.g., module/image.png), though this depends on your configured filePathStrategy [2][3]. The library's internal comparison artifacts are stored in the module/build/outputs/roborazzi directory [1][2]. 2. Customizing Paths: - To customize the global output directory for Roborazzi (which affects where comparison files and potentially recorded images are stored, depending on your strategy), you can configure it in your build.gradle file: roborazzi { outputDir = file("src/your/custom/path") } [4][2] - To control where recorded images are saved, you can set the file path strategy in your gradle.properties file: roborazzi.record.filePathStrategy=relativePathFromRoborazziContextOutputDirectory [2] When this strategy is enabled, images are saved in the directory specified by roborazzi.outputDir instead of the default module root [2]. 3. Comparison Options: While there is an outputDirectoryPath parameter available within CompareOptions, it is specifically intended for managing where comparison-related output files are generated, rather than overriding the primary golden snapshot path for regular capture methods [5].

Citations:


Align the publish gate with the gallery filters. check.diff and check.device can turn true for files that the gallery steps later drop, so the comment still posts a table header with zero rows. Use the same path/filename predicates in both places (roborazzi/ for diff, band_api<SDK>_<case>_{actual,expected}.png for device).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/screenshot-comment.yml around lines 106 - 131, Update the
Check for images to publish step’s diff and device detection predicates to match
the gallery filters exactly: only accept Roborazzi files under roborazzi/ for
diff images, and only accept device-screenshots files matching
band_api<SDK>_<case>_{actual,expected}.png for device images. Keep the existing
safe-path validation and GITHUB_OUTPUT values, but ensure check.diff and
check.device cannot become true for files the gallery later excludes.

Source: Linters/SAST tools

@l2hyunwoo
l2hyunwoo merged commit f54a327 into main Jul 11, 2026
5 checks passed
@l2hyunwoo
l2hyunwoo deleted the ci/screenshot-comment-pipeline branch July 11, 2026 14:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant