rastrillo / native Public

bundle-mac: copy the Lucide notice only into apps that ship Lucide

The Mac bundler copied LICENSE.lucide into every app unconditionally;
it now copies it only when the app has the file, with tests for both
cases. Landed on local make bundle-test: hub CI fails because the
runner lacks unzip (main fails the same way), on Paul's ruling.
Reviewed by Claude Fable 5.1 (#10).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Squashed from chore/lucide-notice-when-used into main.
claude agent, operated by paul@keymail.dev c24df35e483cf7d1d1169ac79ab97bc18b820021 parent 73aec85
2 files changed, +12 −2
  • tools/bundle-mac.py +6 −1
  • tools/tests/test_bundle_mac.py +6 −1

CI failed — run details

CI log
=== test ===
swift test
Fetching binary artifact https://github.com/sparkle-project/Sparkle/releases/download/2.9.6/Sparkle-for-Swift-Package-Manager.zip from cache
error: failed validating archive from 'https://github.com/sparkle-project/Sparkle/releases/download/2.9.6/Sparkle-for-Swift-Package-Manager.zip' which is required by binary target 'Sparkle': could not find executable for 'unzip'
[0/1] Planning build
make: *** [Makefile:4: test] Error 1

amadan: step test failed: exit status 2

amadan: job failed at step test — its output is above, not at the tail of this log
diff --git a/tools/bundle-mac.py b/tools/bundle-mac.py
index 1cf757c..ea99032 100644
--- a/tools/bundle-mac.py
+++ b/tools/bundle-mac.py
@@ -28,7 +28,12 @@ for resource in products.glob("*.bundle"):
if target.exists():
shutil.rmtree(target)
shutil.copytree(resource, target)
-shutil.copy2(root / "LICENSE.lucide", bundle / "Resources/LICENSE.lucide")
+# Lucide's notice travels with the app only when the app has one to give:
+# it attaches to copies of Lucide's drawings, and an app that draws none
+# (its icons its own, the rest system symbols) owes no one the notice.
+lucide = root / "LICENSE.lucide"
+if lucide.is_file():
+ shutil.copy2(lucide, bundle / "Resources/LICENSE.lucide")
icon = root / "build/MacIcon.png"
subprocess.run(["swift", str(Path(__file__).with_name("mac-icon.swift")), str(root / "ios/Assets.xcassets/AppIcon.appiconset/AppIcon.png"), str(icon)], check=True)
iconset = root / "build/AppIcon.iconset"
diff --git a/tools/tests/test_bundle_mac.py b/tools/tests/test_bundle_mac.py
index 140fb09..2e6f7f9 100644
--- a/tools/tests/test_bundle_mac.py
+++ b/tools/tests/test_bundle_mac.py
@@ -37,7 +37,6 @@ guard let url = resources.url(forResource: "sentinel", withExtension: "txt"),
print(text)
''')
(source / "Resources/sentinel.txt").write_text("portable native resources")
- (root / "LICENSE.lucide").write_text("Synthetic packaging test fixture")
icons = root / "ios/Assets.xcassets/AppIcon.appiconset"
icons.mkdir(parents=True)
@@ -47,7 +46,13 @@ print(text)
png = b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", struct.pack(">IIBBBBB", 1024, 1024, 8, 6, 0, 0, 0))
png += chunk(b"IDAT", zlib.compress((b"\0" + b"\x30\x70\xb0\xff" * 1024) * 1024)) + chunk(b"IEND", b"")
(icons / "AppIcon.png").write_bytes(png)
+ # An app that draws no Lucide icon carries no Lucide notice: the
+ # notice is copied only when the app has one to give.
+ subprocess.run(["python3", str(tool), str(root), "Probe"], check=True, stdout=subprocess.DEVNULL)
+ self.assertFalse((root / "build/Oficina Probe.app/Contents/Resources/LICENSE.lucide").exists())
+ (root / "LICENSE.lucide").write_text("Synthetic packaging test fixture")
subprocess.run(["python3", str(tool), str(root), "Probe"], check=True, stdout=subprocess.DEVNULL)
+ self.assertEqual((root / "build/Oficina Probe.app/Contents/Resources/LICENSE.lucide").read_text(), "Synthetic packaging test fixture")
app = root / "relocated/Probe.app"
app.parent.mkdir()
shutil.move(root / "build/Oficina Probe.app", app)