Skip to content

fix: avoid brand asset churn during dev launch - #378

Closed
Raygooo wants to merge 1 commit into
Octane0411:mainfrom
Raygooo:air/launch-dev-launch-dev-git-git-c907d3bf-7
Closed

fix: avoid brand asset churn during dev launch#378
Raygooo wants to merge 1 commit into
Octane0411:mainfrom
Raygooo:air/launch-dev-launch-dev-git-git-c907d3bf-7

Conversation

@Raygooo

@Raygooo Raygooo commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • stop launch-dev from rewriting tracked brand icon assets on each run
  • add temporary-output support to the brand icon generator so dev launch only builds the icns payload it needs
  • add a regression test covering temp icns generation without dirtying tracked brand outputs

Test Plan

  • python3 -m unittest scripts/test_generate_brand_icons.py -v
  • zsh -n scripts/launch-dev-app.sh
  • swift test

Summary by CodeRabbit

  • Tests

    • Added validation to ensure development builds don't accidentally modify tracked brand assets.
  • Refactor

    • Modified brand icon generation workflow to use temporary output during development, with automatic cleanup on script exit.
  • Documentation

    • Updated build workflow documentation to reflect temporary output usage.

@coderabbitai

coderabbitai Bot commented Apr 19, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The PR refactors the brand icon generation workflow to support configurable output paths via CLI arguments (--output-root, --icns-only), allowing dev launches to generate icons into temporary directories without modifying tracked assets. An OutputLayout dataclass replaces global path constants, and a new test verifies tracked assets remain untouched during temporary generation runs.

Changes

Cohort / File(s) Summary
Icon Generation CLI & Refactoring
scripts/generate_brand_icons.py
Added argparse with --output-root and --icns-only flags; introduced OutputLayout dataclass to manage resolved output paths; refactored write_app_icons(), write_internal_assets(), build_icns() to accept layout parameter; replaced global path constants with SOURCE_BRAND_ROOT input reference and dynamic path resolution.
Dev Launcher Temporary Output
scripts/launch-dev-app.sh
Updated to generate icons into temporary directory via mktemp -d; passes --output-root and --icns-only to icon generator; added cleanup function with EXIT trap to remove temp directory on script exit.
Test Coverage
scripts/test_generate_brand_icons.py
New test module validating --icns-only generation with temporary output; verifies tracked brand assets remain unmodified after generation and confirms expected output files are created in temp directory.
Documentation
Assets/Brand/README.md
Updated workflow documentation to specify dev launches use --output-root and --icns-only for temporary, non-destructive icon generation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A rabbit hops with glee—
Icons flow to temp with /tmp's decree,
--icns-only keeps treasures safe,
No tracked files bear a trace,
Clean generation, speedy and graceful! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: avoid brand asset churn during dev launch' directly and clearly summarizes the main change—preventing tracked brand assets from being rewritten during development launches.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
scripts/test_generate_brand_icons.py (2)

22-27: Pre-condition git diff --quiet will spuriously fail the test when a developer has unrelated local edits to brand assets.

This assertion treats any pre-existing dirty state on the tracked brand paths as a test failure, even when the developer legitimately has WIP changes to those files. That produces confusing red test runs that aren't about the code under test.

A more robust approach is to snapshot file hashes (or git diff output) before/after and compare them for equality, which tests “the run didn’t change things” regardless of the starting state:

Proposed refactor
-        before = subprocess.run(
-            ["git", "diff", "--quiet", "--", *GENERATED_BRAND_PATHS],
-            cwd=REPO_ROOT,
-            check=False,
-        )
-        self.assertEqual(before.returncode, 0, "generated brand assets must start clean for this test")
+        def snapshot() -> str:
+            return subprocess.run(
+                ["git", "diff", "--", *GENERATED_BRAND_PATHS],
+                cwd=REPO_ROOT,
+                check=True,
+                capture_output=True,
+                text=True,
+            ).stdout
+
+        before_diff = snapshot()
...
-        self.assertEqual(after.returncode, 0, "temporary icns generation must not dirty tracked brand outputs")
+        self.assertEqual(snapshot(), before_diff, "temporary icns generation must not dirty tracked brand outputs")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/test_generate_brand_icons.py` around lines 22 - 27, The current
pre-check uses subprocess.run([... "git", "diff", "--quiet", "--",
*GENERATED_BRAND_PATHS], ...) and asserts before.returncode == 0, which fails if
a developer has unrelated local edits; change this to record a snapshot of the
tracked files’ state before the test (e.g., capture content hashes or the output
of git diff -- <paths> into a string) and then after running the generation
compare the before and after snapshots for equality instead of asserting a clean
working tree; update the variable names around before and the assertEqual call
(e.g., before_snapshot and after_snapshot) so the test verifies “no changes
produced by this run” regardless of starting dirty state.

31-43: Use sys.executable instead of a bare "python3".

Invoking the generator via "python3" picks up whatever python3 is first on PATH, which may not be the interpreter running the test (e.g. venv vs. system Python, or CI images where python3 resolves to a different version than the unittest runner). Using sys.executable guarantees the subprocess uses the same interpreter and avoids surprising ModuleNotFoundError: PIL failures when the active venv has Pillow but the system python3 does not.

Proposed fix
+import sys
...
-            result = subprocess.run(
-                [
-                    "python3",
-                    str(SCRIPT_PATH),
+            result = subprocess.run(
+                [
+                    sys.executable,
+                    str(SCRIPT_PATH),
                     "--output-root",
                     str(output_root),
                     "--icns-only",
                 ],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/test_generate_brand_icons.py` around lines 31 - 43, The subprocess
invocation in the test uses the literal "python3" which can point to a different
interpreter; change the argv in the subprocess.run call that references
SCRIPT_PATH to use sys.executable instead of "python3" and add an import sys at
the top of scripts/test_generate_brand_icons.py so sys is available; keep the
rest of the subprocess.run args (cwd=REPO_ROOT, capture_output=True, text=True,
check=False) unchanged.
scripts/launch-dev-app.sh (1)

18-25: Minor: trap is installed after mktemp, so a failure between Line 18 and Line 25 would leak the temp dir.

With set -e, if any command between the mktemp on Line 18 and the trap cleanup EXIT on Line 25 were to fail (currently just the assignment on Line 19, so in practice safe), the temp directory wouldn't be cleaned up. Low risk today since only a simple assignment sits between them, but as the prelude grows this becomes a footgun. Consider installing the trap immediately after mktemp:

Proposed reorder
 brand_temp_dir="$(mktemp -d "${TMPDIR:-/tmp}/open-island-brand.XXXXXX")"
-brand_icon="$brand_temp_dir/OpenIsland.icns"
-
 cleanup() {
   rm -rf "$brand_temp_dir"
 }
-
 trap cleanup EXIT
+
+brand_icon="$brand_temp_dir/OpenIsland.icns"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/launch-dev-app.sh` around lines 18 - 25, The temp-dir leak risk comes
from installing the trap after creating brand_temp_dir and before other
commands; move the trap setup so that immediately after creating brand_temp_dir
with mktemp you install trap cleanup EXIT (keeping the cleanup() function and
the subsequent brand_icon assignment where they are), and ensure mktemp failure
is handled (e.g., exit if mktemp fails) so cleanup only runs when brand_temp_dir
exists.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@scripts/launch-dev-app.sh`:
- Around line 18-25: The temp-dir leak risk comes from installing the trap after
creating brand_temp_dir and before other commands; move the trap setup so that
immediately after creating brand_temp_dir with mktemp you install trap cleanup
EXIT (keeping the cleanup() function and the subsequent brand_icon assignment
where they are), and ensure mktemp failure is handled (e.g., exit if mktemp
fails) so cleanup only runs when brand_temp_dir exists.

In `@scripts/test_generate_brand_icons.py`:
- Around line 22-27: The current pre-check uses subprocess.run([... "git",
"diff", "--quiet", "--", *GENERATED_BRAND_PATHS], ...) and asserts
before.returncode == 0, which fails if a developer has unrelated local edits;
change this to record a snapshot of the tracked files’ state before the test
(e.g., capture content hashes or the output of git diff -- <paths> into a
string) and then after running the generation compare the before and after
snapshots for equality instead of asserting a clean working tree; update the
variable names around before and the assertEqual call (e.g., before_snapshot and
after_snapshot) so the test verifies “no changes produced by this run”
regardless of starting dirty state.
- Around line 31-43: The subprocess invocation in the test uses the literal
"python3" which can point to a different interpreter; change the argv in the
subprocess.run call that references SCRIPT_PATH to use sys.executable instead of
"python3" and add an import sys at the top of
scripts/test_generate_brand_icons.py so sys is available; keep the rest of the
subprocess.run args (cwd=REPO_ROOT, capture_output=True, text=True, check=False)
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6880e75f-e1e5-426d-82d9-35d90c9da45a

📥 Commits

Reviewing files that changed from the base of the PR and between 8ac5102 and f232f9f.

📒 Files selected for processing (4)
  • Assets/Brand/README.md
  • scripts/generate_brand_icons.py
  • scripts/launch-dev-app.sh
  • scripts/test_generate_brand_icons.py

@Octane0411

Copy link
Copy Markdown
Owner

Thanks for this, and apologies for the long silence. The problem you identified — dev launch rewriting the tracked brand PNGs on every run — just landed via #680, which takes a smaller route: launch-dev-app.sh and package-app.sh now package the committed OpenIsland.icns / dmg-background.png as-is, and regeneration is opt-in (--regenerate-icons, OPEN_ISLAND_REGENERATE_BRAND_ASSETS=true). That covers the churn without changing the generator, so I'm closing this one as superseded. If you still want the --output-root / --icns-only options in the generator for other workflows, a rebased follow-up would be welcome.

This review was generated by AI and may contain mistakes in judgement. If anything here is wrong or unclear, please reply directly to this comment.

@Octane0411 Octane0411 closed this Sep 3, 2026
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.

2 participants