From 43d306effb3ceffa437d24087761fdd996e4b2e0 Mon Sep 17 00:00:00 2001 From: Lixeas <44436514+Lixeas@users.noreply.github.com> Date: Fri, 25 Sep 2026 16:27:31 +0200 Subject: [PATCH] fix: generate a real app icon, document brew trust - Scripts/generate-icon.swift: rasterizes the vector brand SVG to every size iconutil needs (NSImage draws it as vector, not a scaled-up 16px bitmap), packed into AppIcon.icns and copied into the bundle before codesign. Info.plist now sets CFBundleIconFile. The app and .dmg had no icon at all before this (default generic icon). - README: brew trust --cask is now a required one-time step before install (Homebrew 6.0 requires explicit trust for non-official taps) -- users were hitting this prompt unexplained. Not verified locally: no macOS/Swift toolchain on this machine. Logic is straightforward (NSImage vector draw + iconutil) but the actual icon needs checking on the next real release run. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/release.yml | 8 ++++++ README.md | 3 ++- Scripts/generate-icon.swift | 48 +++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 Scripts/generate-icon.swift diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dc9d8e0..ef3cd9c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -68,6 +68,7 @@ jobs: CFBundlePackageTypeAPPL CFBundleShortVersionString$VERSION CFBundleVersion$VERSION + CFBundleIconFileAppIcon LSMinimumSystemVersion13.0 LSUIElement NSHighResolutionCapable @@ -75,6 +76,13 @@ jobs: PLIST + # Rasterize the vector brand mark at every required size (NSImage renders the SVG as + # vector, not a blown-up 16px bitmap) and pack it into a proper multi-resolution .icns. + swift Scripts/generate-icon.swift \ + NotchWatch/Resources/Images/notchwatch-corbeau-16-modified.svg \ + AppIcon.iconset + iconutil -c icns AppIcon.iconset -o "$APP/Contents/Resources/AppIcon.icns" + # No Apple Developer ID available: ad-hoc sign only. Users get a Gatekeeper # warning on first launch (documented in README) — not notarized. codesign --force --deep --sign - "$APP" diff --git a/README.md b/README.md index 41a74ef..d165487 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,11 @@ ```sh brew tap Lixeas/notchwatch https://github.com/Lixeas/NotchWatch +brew trust --cask lixeas/notchwatch/notch-watch brew install --cask notch-watch ``` -This installs the latest signed `.dmg` from [GitHub Releases](https://github.com/Lixeas/NotchWatch/releases) and keeps a Homebrew-managed `NotchWatch.app` in `/Applications`. To update: `brew upgrade --cask notch-watch`. +Homebrew 6.0+ requires explicit trust for non-official taps (`brew trust`) — one-time, before the first install. This installs the latest signed `.dmg` from [GitHub Releases](https://github.com/Lixeas/NotchWatch/releases) and keeps a Homebrew-managed `NotchWatch.app` in `/Applications`. To update: `brew upgrade --cask notch-watch`. ### Option 2 — Manual download diff --git a/Scripts/generate-icon.swift b/Scripts/generate-icon.swift new file mode 100644 index 0000000..b6b427c --- /dev/null +++ b/Scripts/generate-icon.swift @@ -0,0 +1,48 @@ +import AppKit + +// Standalone build-time script (run via `swift Scripts/generate-icon.swift ` +// on the macOS release runner) -- not part of the app target, iconutil needs a populated +// .iconset directory of PNGs at each required size, generated here from the vector SVG source. + +let arguments = CommandLine.arguments +guard arguments.count == 3 else { + FileHandle.standardError.write(Data("Usage: generate-icon.swift \n".utf8)) + exit(1) +} +let sourcePath = arguments[1] +let outputDir = arguments[2] + +guard let source = NSImage(contentsOfFile: sourcePath) else { + FileHandle.standardError.write(Data("Failed to load \(sourcePath)\n".utf8)) + exit(1) +} + +let sizes: [(name: String, pixels: CGFloat)] = [ + ("icon_16x16", 16), ("icon_16x16@2x", 32), + ("icon_32x32", 32), ("icon_32x32@2x", 64), + ("icon_128x128", 128), ("icon_128x128@2x", 256), + ("icon_256x256", 256), ("icon_256x256@2x", 512), + ("icon_512x512", 512), ("icon_512x512@2x", 1024) +] + +try FileManager.default.createDirectory(atPath: outputDir, withIntermediateDirectories: true) + +for (name, pixels) in sizes { + let size = NSSize(width: pixels, height: pixels) + let rendered = NSImage(size: size) + rendered.lockFocus() + NSGraphicsContext.current?.imageInterpolation = .high + source.draw(in: NSRect(origin: .zero, size: size), from: .zero, operation: .sourceOver, fraction: 1.0) + rendered.unlockFocus() + + guard let tiff = rendered.tiffRepresentation, + let rep = NSBitmapImageRep(data: tiff), + let png = rep.representation(using: .png, properties: [:]) else { + FileHandle.standardError.write(Data("Failed to rasterize \(name)\n".utf8)) + exit(1) + } + let url = URL(fileURLWithPath: outputDir).appendingPathComponent("\(name).png") + try png.write(to: url) +} + +print("Wrote \(sizes.count) icon sizes to \(outputDir)")