mirror of
https://github.com/storytold/craft-launcher.git
synced 2026-10-13 14:59:07 +00:00
macOS DMG: branded window and hdiutil create, so the app passes codesign --strict (#169)
* macOS: build the DMG with hdiutil create so the app passes codesign --strict hdiutil makehybrid -hfs put a com.apple.FinderInfo on every file in the image, so the signed app inside the DMG (and any ditto copy of it) failed codesign --verify --deep --strict. That is why verify.sh had dropped --strict. Build the image with hdiutil create -srcfolder (retried, since create can hit "Resource busy" on CI), clear build-time xattrs before signing, and make verify.sh check the app in the finished DMG and a ditto copy of it with --strict again. Ported from storytold/pdfcraft#823 and storytold/lightcraft#959. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * macOS DMG: branded window background and icon layout The launcher's DMG opened as a plain Finder window, unlike every other Craft app. Add packaging/macos/dmg/ the same way WordCraft and PhotoCraft did: a background (the ArtCraft mark whole on its blue, title, Ink and Paper), a .DS_Store with the window size and icon positions, and the generate.py that writes both reproducibly. The volume is now named "ArtCraft Launcher" without the version, which the background alias needs; the DMG file name keeps the version. Fixes #20 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Fabio Lussi <198260595+itrycodinganditsbad@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
8 files changed
+269
-10
No files matched your search
@@ -11,6 +11,7 @@ next to them.
|
||||
| Path | Title | Author | Source | License |
|
||||
|---|---|---|---|---|
|
||||
| `assets/app-icon/` (all files) | ArtCraft Launcher app icon: the ArtCraft "A" mark in blue (getartcraft.com's favicon) | ArtCraft Team | Rendered unmodified from `docs/brand/artcraft-mark.svg` by `packaging/icons.sh`, see [`assets/app-icon/README.md`](assets/app-icon/README.md) | **Not open source; not MIT or Apache-2.0.** ArtCraft trademark, [`docs/brand/LICENSE-brand.txt`](docs/brand/LICENSE-brand.txt) |
|
||||
| `packaging/macos/dmg/` (all files) | macOS DMG window background and layout | ArtCraft Launcher contributors, with the ArtCraft mark | Original work, see [`packaging/macos/dmg/README.md`](packaging/macos/dmg/README.md); the mark is `docs/brand/artcraft-mark-black.svg`, linked from `background.svg` and rendered unmodified into `background.tiff` | MIT OR Apache-2.0, like the code; the ArtCraft mark in it stays an ArtCraft trademark ([`docs/brand/LICENSE-brand.txt`](docs/brand/LICENSE-brand.txt)). The TIFF carries LittleCMS's built-in sRGB profile ("No copyright, use freely") |
|
||||
| `assets/social/linkedin.svg` | LinkedIn logo, linking to ArtCraft's company page | Fonticons, Inc. ([Font Awesome Free](https://fontawesome.com) 6.7.2) | Font Awesome Free `svgs/brands/linkedin.svg`; white fill and a square view box, see [`assets/social/README.md`](assets/social/README.md) | CC BY 4.0. The logo is a trademark of LinkedIn Corporation. |
|
||||
| `assets/social/x.svg`, `youtube.svg`, `tiktok.svg`, `instagram.svg` | X, YouTube, TikTok and Instagram logos, linking to ArtCraft's accounts | [Simple Icons](https://simpleicons.org/) contributors | Simple Icons, `fill` set to white for tinting; see [`assets/social/README.md`](assets/social/README.md) | CC0 1.0. The logos are trademarks of X Corp., Google LLC, TikTok / ByteDance and Meta Platforms, Inc. |
|
||||
| `docs/screenshots/artcraft-launcher-grid.png`, `artcraft-launcher-table.png` | Screenshots of ArtCraft Launcher (macOS: grid view in dark theme, table view in light theme), used in the README | ArtCraft Launcher contributors | Captured from the app | MIT OR Apache-2.0 for the screenshot itself; the ArtCraft logo in it stays an ArtCraft trademark ([`docs/brand/LICENSE-brand.txt`](docs/brand/LICENSE-brand.txt)) and the app icons in it keep their own licenses (above) |
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# macOS DMG window
|
||||
|
||||
What Finder shows when the DMG opens: a 660 × 400 pt background with the app icon and the
|
||||
`Applications` link side by side, laid out like the other Craft apps' DMGs. `package.sh` copies
|
||||
these files into the image; nothing here is generated at build time, so the DMG builds with a plain
|
||||
`hdiutil create -srcfolder` (no Finder scripting on CI, nothing extra installed in the signing job).
|
||||
|
||||
| File | What |
|
||||
|---|---|
|
||||
| `background.svg` | Source of the background: the ArtCraft mark in black (`docs/brand/artcraft-mark-black.svg`, linked, not copied) whole on the mark's blue (`#4e7bfb`), Ink and Paper. The mark is a trademark: it stays exactly as supplied, never cropped or recoloured (`docs/brand/LICENSE-brand.txt`), unlike the cropped icons on the other apps' DMGs. Its text (Inter, Geist Mono from `assets/fonts`) is outlined, so rendering it needs no fonts. No paper grain: noise doesn't compress, and this keeps `background.tiff` small. |
|
||||
| `background.tiff` | The background at 1x (660 × 400 px, 72 dpi) and 2x (1320 × 800 px, 144 dpi) in one HiDPI TIFF (16-colour palette, Deflate, sRGB). Goes to `.background/background.tiff`. |
|
||||
| `dmg-layout.DS_Store` | Finder's view settings for the volume: window size, icon size 128, ArtCraft Launcher.app at (326, 205), `Applications` at (574, 205), and the background. Goes to `.DS_Store` in the image; named so it isn't mistaken for (or ignored like) a Finder-generated `.DS_Store`. |
|
||||
| `generate.py` | Writes `background.tiff` and `dmg-layout.DS_Store` from the SVG and the layout above. |
|
||||
|
||||
## The volume name has no version
|
||||
|
||||
The mounted volume is called `ArtCraft Launcher`, not `ArtCraft Launcher <version>`. The layout
|
||||
points at the background through an alias that includes the volume name, and Finder resolves it by
|
||||
that name: with a versioned name the window keeps its size and icon positions but shows no
|
||||
background. The DMG file name (`artcraft-launcher-<version>-macos-<arch>.dmg`) still carries the
|
||||
version.
|
||||
|
||||
## Rules
|
||||
|
||||
- **Finder draws the icon labels in black in light and dark mode** when a window has a background,
|
||||
so the area under both icons stays light (Paper).
|
||||
- **Nothing goes inside the icon boxes:** artwork keeps 10 pt clear of each 128 pt icon box and of
|
||||
the label strip under it.
|
||||
|
||||
## Regenerate
|
||||
|
||||
Edit `background.svg` (or the layout constants in `generate.py`), then run, on any OS:
|
||||
|
||||
```sh
|
||||
pip install 'pillow>=12' ds_store==1.3.3 mac_alias==2.2.3
|
||||
python3 packaging/macos/dmg/generate.py # needs resvg on PATH
|
||||
```
|
||||
|
||||
Both outputs are byte-for-byte reproducible and carry nothing from the machine that ran it: the
|
||||
SVG renders without any fonts, and `dmg-layout.DS_Store` is written from scratch, its background
|
||||
alias holding only the volume name and `/.background/background.tiff`. If you add text to the SVG,
|
||||
outline it (`usvg --skip-system-fonts --use-font-file assets/fonts/Inter-Variable.ttf
|
||||
--use-font-file assets/fonts/GeistMono-Variable.ttf` converts text to paths). The window is
|
||||
660 × 432 with Finder's 32 pt title bar; the content area is 660 × 400.
|
||||
|
||||
This follows the other Craft apps' `packaging/macos/dmg/` (storytold/wordcraft#21,
|
||||
storytold/photocraft#1201, first in storytold/vectorcraft#493).
|
||||
@@ -0,0 +1,27 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="660" height="400" viewBox="0 0 660 400">
|
||||
<defs><clipPath id="panel"><rect width="232" height="400"/></clipPath></defs>
|
||||
|
||||
<rect width="660" height="400" fill="#efe9dc"/>
|
||||
<g clip-path="url(#panel)"><rect width="232" height="400" fill="#4e7bfb"/><image href="../../../docs/brand/artcraft-mark-black.svg" x="31" y="115" width="170" height="170"/></g>
|
||||
<line x1="232.5" y1="0" x2="232.5" y2="400" stroke="#0b0b0c" stroke-width="1.2"/>
|
||||
<line x1="236.5" y1="0" x2="236.5" y2="400" stroke="#0b0b0c" stroke-width="0.5"/>
|
||||
|
||||
<line x1="267" y1="88.5" x2="624" y2="88.5" stroke="#0b0b0c" stroke-width="0.6"/>
|
||||
<path d="M271.5,81.5 L276,88.5 L271.5,95.5 L267,88.5 Z" fill="#4e7bfb"/>
|
||||
|
||||
<rect x="410.5" y="199.5" width="11" height="11" fill="#efe9dc" stroke="#0b0b0c" stroke-opacity="0.45" stroke-width="0.7"/><rect x="425.5" y="199.5" width="11" height="11" fill="#efe9dc" stroke="#0b0b0c" stroke-opacity="0.45" stroke-width="0.7"/><rect x="440.5" y="199.5" width="11" height="11" fill="#efe9dc" stroke="#0b0b0c" stroke-opacity="0.45" stroke-width="0.7"/><rect x="455.5" y="199.5" width="11" height="11" fill="#4e7bfb" stroke="#0b0b0c" stroke-width="0.7"/><line x1="472" y1="205" x2="484" y2="205" stroke="#0b0b0c" stroke-width="1.2"/><path d="M495,205 L484,210.5 L484,199.5 Z" fill="#0b0b0c"/>
|
||||
<line x1="267" y1="335.5" x2="624" y2="335.5" stroke="#0b0b0c" stroke-width="0.6"/>
|
||||
|
||||
|
||||
<g id="text">
|
||||
<g>
|
||||
<path fill="#0b0b0c" stroke="none" d="M 266.24902 72 L 274.02734 50.17383 L 278.90527 50.17383 L 286.66895 72 L 282.3916 72 L 278.56836 60.54492 Q 277.99707 58.728516 277.3672 56.670406 Q 276.7373 54.612305 275.9463 51.81445 L 276.98633 51.81445 Q 276.18066 54.612305 275.55078 56.670406 Q 274.9209 58.728516 274.33496 60.54492 L 270.42383 72 Z M 270.86328 66.77051 L 270.86328 63.57715 L 282.0547 63.57715 L 282.0547 66.77051 Z M 288.10254 72 L 288.10254 56.428715 L 291.67676 56.428715 L 291.67676 59.021484 L 291.73535 59.021484 Q 292.16016 57.673832 293.09766 56.9707 Q 294.03516 56.26758 295.51465 56.26758 Q 295.88086 56.26758 296.1958 56.28955 Q 296.51074 56.31152 296.74512 56.340816 L 296.74512 59.63672 Q 296.54004 59.607426 296.03467 59.56348 Q 295.5293 59.519527 294.9873 59.519527 Q 294.1084 59.519527 293.37598 59.922367 Q 292.64355 60.325195 292.22607 61.13086 Q 291.8086 61.936523 291.8086 63.137695 L 291.8086 72 Z M 307.0703 56.428715 L 307.0703 59.402344 L 297.53418 59.402344 L 297.53418 56.428715 Z M 300.2002 52.253906 L 303.90625 52.253906 L 303.90625 67.546875 Q 303.90625 68.36719 304.2505 68.7041 Q 304.59473 69.041016 305.48828 69.041016 Q 305.83984 69.041016 306.30127 69.03369 Q 306.7627 69.02637 307.05566 69.01172 L 307.11426 71.98535 Q 306.7041 72 306.08154 72.01465 Q 305.45898 72.0293 304.8584 72.0293 Q 302.48535 72.0293 301.34277 71.04785 Q 300.2002 70.06641 300.2002 68.05957 Z M 318.64063 72.35156 Q 315.65234 72.35156 313.3379 70.95996 Q 311.02344 69.56836 309.70508 67.041504 Q 308.38672 64.51465 308.38672 61.101563 Q 308.38672 57.659184 309.70508 55.125004 Q 311.02344 52.590824 313.3379 51.206543 Q 315.65234 49.822266 318.64063 49.822266 Q 320.48633 49.822266 322.07568 50.371586 Q 323.66504 50.9209 324.90283 51.946293 Q 326.14063 52.97168 326.9463 54.407223 Q 327.75195 55.842773 328.01563 57.62988 L 324.08984 57.62988 Q 323.87012 56.604492 323.38672 55.798832 Q 322.90332 54.993164 322.2002 54.43652 Q 321.49707 53.879887 320.61084 53.59424 Q 319.7246 53.308594 318.68457 53.308594 Q 316.70703 53.308594 315.27148 54.268066 Q 313.83594 55.22754 313.0669 56.978027 Q 312.29785 58.728516 312.29785 61.101563 Q 312.29785 63.459965 313.0669 65.203125 Q 313.83594 66.94629 315.27148 67.90576 Q 316.70703 68.865234 318.68457 68.865234 Q 319.7246 68.865234 320.60352 68.57959 Q 321.48242 68.293945 322.18555 67.74463 Q 322.88867 67.19531 323.3794 66.39697 Q 323.87012 65.59863 324.08984 64.57324 L 328.01563 64.57324 Q 327.75195 66.331055 326.9536 67.75928 Q 326.15527 69.1875 324.91748 70.21289 Q 323.6797 71.23828 322.09033 71.79492 Q 320.50098 72.35156 318.64063 72.35156 Z M 330.25488 72 L 330.25488 56.428715 L 333.8291 56.428715 L 333.8291 59.021484 L 333.8877 59.021484 Q 334.3125 57.673832 335.25 56.9707 Q 336.1875 56.26758 337.667 56.26758 Q 338.0332 56.26758 338.34814 56.28955 Q 338.6631 56.31152 338.89746 56.340816 L 338.89746 59.63672 Q 338.69238 59.607426 338.187 59.56348 Q 337.68164 59.519527 337.13965 59.519527 Q 336.26074 59.519527 335.52832 59.922367 Q 334.7959 60.325195 334.37842 61.13086 Q 333.96094 61.936523 333.96094 63.137695 L 333.96094 72 Z M 344.9746 72.26367 Q 343.43652 72.26367 342.2207 71.74365 Q 341.00488 71.22363 340.30908 70.17627 Q 339.61328 69.12891 339.61328 67.59082 Q 339.61328 66.25781 340.11133 65.393555 Q 340.60938 64.5293 341.4663 64.00928 Q 342.32324 63.489258 343.42188 63.225586 Q 344.5205 62.961914 345.70703 62.83008 Q 347.1426 62.66894 347.9922 62.551758 Q 348.84177 62.43457 349.208 62.18555 Q 349.57422 61.936523 349.57422 61.409176 L 349.57422 61.291992 Q 349.57422 60.618164 349.23727 60.10547 Q 348.9004 59.592777 348.2705 59.299805 Q 347.64063 59.006836 346.74707 59.006836 Q 345.85352 59.006836 345.17236 59.292484 Q 344.4912 59.57813 344.08102 60.083496 Q 343.6709 60.588867 343.59766 61.2334 L 340.05273 61.160156 Q 340.18457 59.622074 341.0415 58.49414 Q 341.89844 57.36621 343.3706 56.743656 Q 344.84277 56.121094 346.8203 56.121094 Q 348.2852 56.121094 349.47165 56.47998 Q 350.6582 56.838867 351.5078 57.52002 Q 352.35742 58.201168 352.8042 59.17529 Q 353.25095 60.14941 353.25095 61.409176 L 353.25095 72 L 349.61816 72 L 349.61816 69.802734 L 349.55957 69.802734 Q 349.1934 70.49121 348.60013 71.04785 Q 348.00684 71.60449 347.1206 71.93408 Q 346.23438 72.26367 344.9746 72.26367 Z M 345.89746 69.509766 Q 347.09863 69.509766 347.92627 69.07031 Q 348.7539 68.63086 349.1787 67.89111 Q 349.60352 67.15137 349.60352 66.25781 L 349.60352 64.55859 Q 349.42776 64.66113 349.07614 64.770996 Q 348.72458 64.88086 348.25586 64.976074 Q 347.7871 65.07129 347.24512 65.15918 Q 346.70313 65.24707 346.16116 65.32031 Q 345.37015 65.4375 344.6963 65.69385 Q 344.02246 65.950195 343.62695 66.39697 Q 343.23145 66.84375 343.23145 67.546875 Q 343.23145 68.16211 343.56104 68.60156 Q 343.89063 69.041016 344.4912 69.27539 Q 345.0918 69.509766 345.89746 69.509766 Z M 364.41113 56.428715 L 364.41113 59.402344 L 354.68457 59.402344 L 354.68457 56.428715 Z M 357.49707 72 L 357.49707 54.246094 Line truncated
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#0b0b0c" fill-opacity="0.7" stroke="none" d="M 269.718 112.144 Q 268.953 112.144 268.422 111.739006 Q 267.891 111.334 267.6165 110.587006 Q 267.342 109.84 267.342 108.813995 Q 267.342 107.779 267.6165 107.032005 Q 267.891 106.285 268.422 105.8755 Q 268.953 105.465996 269.718 105.465996 Q 270.564 105.465996 271.158 106.0195 Q 271.752 106.573 271.95 107.56301 L 271.122 107.617 Q 270.969 106.924 270.5955 106.573 Q 270.222 106.222 269.718 106.222 Q 269.205 106.222 268.8585 106.52349 Q 268.512 106.825005 268.332 107.40551 Q 268.152 107.986 268.152 108.813995 Q 268.152 109.633 268.332 110.20901 Q 268.512 110.785 268.8585 111.0865 Q 269.205 111.38799 269.718 111.38799 Q 270.276 111.38799 270.663 111.001 Q 271.05 110.614 271.185 109.849 L 272.004 109.894 Q 271.833 110.947 271.23 111.545494 Q 270.627 112.144 269.718 112.144 Z M 273.21 112 L 273.21 111.334 L 274.82098 111.334 L 274.452 111.694 L 274.452 107.527 L 274.82098 107.896 L 273.21 107.896 L 273.21 107.23 L 275.082 107.23 L 275.154 108.508 L 275.082 108.454 Q 275.154 107.83299 275.46 107.5315 Q 275.766 107.23 276.279 107.23 L 277.25998 107.23 L 277.25998 107.914 L 276.288 107.914 Q 275.93698 107.914 275.6985 108.0535 Q 275.46 108.193 275.33398 108.4585 Q 275.208 108.724 275.208 109.12 L 275.208 111.694 L 274.848 111.334 L 276.72 111.334 L 276.72 112 Z M 279.87 112.108 Q 279.41998 112.108 279.06 111.94599 Q 278.69998 111.784 278.49298 111.4915 Q 278.28598 111.199 278.28598 110.812 Q 278.28598 110.19999 278.65048 109.8625 Q 279.01498 109.525 279.79797 109.37201 L 281.4 109.04799 Q 281.4 108.436 281.1255 108.139 Q 280.85098 107.84201 280.33798 107.84201 Q 279.843 107.84201 279.564 108.06251 Q 279.28497 108.28299 279.177 108.697 L 278.37598 108.634 Q 278.511 107.959 279.0195 107.5405 Q 279.52798 107.122 280.33798 107.122 Q 281.202 107.122 281.679 107.6395 Q 282.15598 108.157 282.15598 109.066 L 282.15598 111.046 Q 282.15598 111.208 282.228 111.271 Q 282.3 111.334 282.444 111.334 L 282.75 111.334 L 282.75 112 Q 282.69598 112.009 282.57898 112.0135 Q 282.46198 112.018005 282.35397 112.018005 Q 282.04797 112.018005 281.8365 111.919 Q 281.625 111.82 281.517 111.5995 Q 281.409 111.379 281.4 111.028 L 281.54398 111.028 Q 281.481 111.334 281.2425 111.581505 Q 281.004 111.829 280.64398 111.9685 Q 280.284 112.108 279.87 112.108 Z M 279.942 111.442 Q 280.42798 111.442 280.75198 111.2755 Q 281.076 111.10899 281.23798 110.8075 Q 281.4 110.506 281.4 110.11 L 281.4 109.696 L 279.942 109.984 Q 279.456 110.074 279.267 110.25851 Q 279.078 110.443 279.078 110.74 Q 279.078 111.07299 279.3075 111.2575 Q 279.537 111.442 279.942 111.442 Z M 285.108 112 L 285.108 106.924 Q 285.108 106.285 285.43652 105.9475 Q 285.765 105.61001 286.44 105.61001 L 287.97 105.61001 L 287.97 106.276 L 286.44 106.276 Q 286.161 106.276 286.0125 106.4515 Q 285.864 106.627 285.864 106.924 L 285.864 112 Z M 283.83002 107.896 L 283.83002 107.23 L 287.88 107.23 L 287.88 107.896 Z M 292.002 112 Q 291.30902 112 290.967 111.67601 Q 290.625 111.35199 290.625 110.68599 L 290.625 106.114 L 291.381 106.114 L 291.381 110.68599 Q 291.381 111.019 291.534 111.1765 Q 291.687 111.334 292.002 111.334 L 293.28 111.334 L 293.28 112 Z M 289.05002 107.896 L 289.05002 107.23 L 293.28 107.23 L 293.28 107.896 Z M 299.634 112 L 301.578 105.61001 L 302.62198 105.61001 L 304.56598 112 L 303.738 112 L 302.1 106.312 L 300.462 112 Z M 300.615 110.182 L 300.88498 109.426 L 303.315 109.426 L 303.585 110.182 Z M 305.52 113.35 L 305.52 107.23 L 306.222 107.23 L 306.24 108.31 L 306.132 108.256 Q 306.31198 107.698006 306.7305 107.41 Q 307.149 107.122 307.698 107.122 Q 308.409 107.122 308.8635 107.473 Q 309.318 107.824 309.534 108.39099 Q 309.75 108.958 309.75 109.615 Q 309.75 110.272 309.534 110.839 Q 309.318 111.406 308.8635 111.757 Q 308.409 112.108 307.698 112.108 Q 307.32898 112.108 307.0095 111.981995 Q 306.69 111.856 306.46948 111.631004 Q 306.249 111.406 306.168 111.1 L 306.276 110.97401 L 306.276 113.35 Z M 307.62598 111.38799 Q 308.24698 111.38799 308.60248 110.92 Q 308.95798 110.451996 308.95798 109.615 Q 308.95798 108.77801 308.60248 108.31 Q 308.24698 107.84201 307.62598 107.84201 Q 307.21198 107.84201 306.9105 108.04 Q 306.60898 108.238 306.44247 108.634 Q 306.276 109.03 306.276 109.615 Q 306.276 110.19999 306.438 110.596 Q 306.59998 110.992 306.90598 111.19 Q 307.21198 111.38799 307.62598 111.38799 Z M 310.92 113.35 L 310.92 107.23 L 311.622 107.23 L 311.64 108.31 L 311.532 108.256 Q 311.712 107.698006 312.13052 107.41 Q 312.549 107.122 313.09802 107.122 Q 313.80902 107.122 314.26352 107.473 Q 314.71802 107.824 314.93402 108.39099 Q 315.15002 108.958 315.15002 109.615 Q 315.15002 110.272 314.93402 110.839 Q 314.71802 111.406 314.26352 111.757 Q 313.80902 112.108 313.09802 112.108 Q 312.729 112.108 312.40952 111.981995 Q 312.09003 111.856 311.8695 111.631004 Q 311.64902 111.406 311.56802 111.1 L 311.67603 110.97401 L 311.67603 113.35 Z M 313.026 111.38799 Q 313.647 111.38799 314.0025 110.92 Q 314.358 110.451Line truncated
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#0b0b0c" fill-opacity="0.65" stroke="none" d="M 270.48584 358 L 268.50928 358 L 268.50928 356.92578 L 270.4214 356.92578 Q 271.3291 356.92578 271.93066 356.5874 Q 272.53223 356.24902 272.833 355.59103 Q 273.1338 354.9331 273.1338 353.9878 Q 273.1338 353.04785 272.8357 352.39798 Q 272.5376 351.74805 271.94946 351.4097 Q 271.36133 351.07126 270.48584 351.07126 L 268.4663 351.07126 L 268.4663 349.99707 L 270.5664 349.99707 Q 271.74805 349.99707 272.5913 350.4751 Q 273.43457 350.95316 273.88574 351.8474 Q 274.3369 352.7417 274.3369 353.9878 Q 274.3369 355.23923 273.88306 356.13623 Q 273.4292 357.0332 272.56714 357.5166 Q 271.70508 358 270.48584 358 Z M 269.11084 349.99707 L 269.11084 358 L 267.88623 358 L 267.88623 349.99707 Z M 277.3247 358 L 277.3247 349.99707 L 280.19287 349.99707 Q 281.1167 349.99707 281.73438 350.31668 Q 282.35205 350.63623 282.6582 351.20557 Q 282.96436 351.77487 282.96436 352.51074 Q 282.96436 353.25195 282.65552 353.80786 Q 282.34668 354.36377 281.729 354.66992 Q 281.11133 354.97607 280.18213 354.97607 L 278.02832 354.97607 L 278.02832 353.9287 L 280.06396 353.9287 Q 280.6548 353.9287 281.01733 353.75952 Q 281.37988 353.5903 281.55176 353.27344 Q 281.72363 352.95654 281.72363 352.51074 Q 281.72363 352.05957 281.55176 351.7319 Q 281.37988 351.40427 281.01465 351.2244 Q 280.6494 351.04443 280.05322 351.04443 L 278.54932 351.04443 L 278.54932 358 Z M 281.87402 358 L 279.93506 354.38525 L 281.29395 354.38525 L 283.26514 358 Z M 285.33984 358 L 288.18652 349.99707 L 289.69043 349.99707 L 292.59082 358 L 291.26416 358 L 289.70654 353.54733 Q 289.52393 353.00488 289.3064 352.269 Q 289.08887 351.53323 288.8042 350.50198 L 289.05127 350.50198 Q 288.77734 351.54395 288.5598 352.2878 Q 288.3423 353.03174 288.17578 353.54733 L 286.6665 358 Z M 286.88135 355.88916 L 286.88135 354.86862 L 291.04395 354.86862 L 291.04395 355.88916 Z M 298.21045 358.10742 Q 297.1255 358.10742 296.3064 357.6079 Q 295.4873 357.1084 295.02808 356.18726 Q 294.56885 355.2661 294.56885 354.00388 Q 294.56885 352.72562 295.03345 351.80444 Q 295.49805 350.8833 296.30908 350.38647 Q 297.12012 349.88965 298.15674 349.88965 Q 298.80127 349.88965 299.36255 350.08032 Q 299.92383 350.271 300.36694 350.6228 Q 300.81006 350.9746 301.0974 351.4607 Q 301.38477 351.9468 301.48145 352.52686 L 300.2246 352.52686 Q 300.1172 352.17773 299.9292 351.89575 Q 299.7412 351.6138 299.4807 351.41504 Q 299.2202 351.2163 298.8899 351.11157 Q 298.55957 351.00684 298.16748 351.00684 Q 297.49072 351.00684 296.95093 351.3506 Q 296.41113 351.6943 296.09692 352.36304 Q 295.7827 353.03174 295.7827 353.99854 Q 295.7827 354.96533 296.09692 355.63135 Q 296.41113 356.29736 296.95898 356.6438 Q 297.50684 356.99023 298.21582 356.99023 Q 298.86035 356.99023 299.34106 356.72974 Q 299.82178 356.46924 300.08765 355.98853 Q 300.35352 355.5078 300.35352 354.85254 L 300.6704 354.90625 L 298.39307 354.90625 L 298.39307 353.89648 L 301.55127 353.89648 L 301.55127 354.81494 Q 301.55127 355.8247 301.1189 356.56323 Q 300.68652 357.30173 299.93457 357.7046 Q 299.18262 358.10742 298.21045 358.10742 Z M 312.33936 358.10742 Q 311.30273 358.10742 310.48364 357.616 Q 309.66455 357.1245 309.19458 356.20337 Q 308.7246 355.2822 308.7246 354.00388 Q 308.7246 352.7202 309.19458 351.7991 Q 309.66455 350.87793 310.48364 350.3838 Q 311.30273 349.88965 312.33936 349.88965 Q 313.37598 349.88965 314.1897 350.3838 Q 315.00342 350.87793 315.4734 351.7991 Q 315.94336 352.7202 315.94336 354.00388 Q 315.94336 355.2822 315.4734 356.20337 Q 315.00342 357.1245 314.1897 357.616 Q 313.37598 358.10742 312.33936 358.10742 Z M 312.33936 356.99023 Q 313.02148 356.99023 313.56128 356.64917 Q 314.10107 356.3081 314.41528 355.6394 Q 314.7295 354.9707 314.7295 354.00388 Q 314.7295 353.02637 314.41528 352.35767 Q 314.10107 351.68896 313.56128 351.34793 Q 313.02148 351.00684 312.33936 351.00684 Q 311.65186 351.00684 311.10938 351.3506 Q 310.5669 351.6943 310.2527 352.36304 Q 309.93848 353.03174 309.93848 354.00388 Q 309.93848 354.9707 310.2527 355.63672 Q 310.5669 356.30273 311.10938 356.6465 Q 311.65186 356.99023 312.33936 356.99023 Z M 318.93652 358 L 318.93652 349.99707 L 320.34912 349.99707 L 323.3623 354.76123 Q 323.5127 354.9976 323.70068 355.32523 Q 323.88867 355.6528 324.09814 356.06372 Q 324.30762 356.47458 324.51172 356.958 L 324.36133 356.958 Q 324.313 356.50146 324.28882 356.05298 Q 324.26465 355.6045 324.2539 355.2124 Q 324.24316 354.8203 324.24316 354.55176 L 324.24316 349.99707 L 325.4839 349.99707 L 325.4839 358 L 324.06592 358 L 321.36426 353.74072 Q 321.1494 353.3916 320.94263 353.0371 Q 320.73584 352.6826 320.48608 352.21802 Q 320.23633 351.75342 319.87646 351.0874 L 320.06982 351.0874 Q 320.09668 351.67822 320.12354 352.19116 Q 320.1504 352.7041 320.16382 353.09888 Q 320.17725 353.49365 320.17725 353.72998 L 320.17725 358 Z M 328.35352 351.0498 L 328.35352 349.99707 L 334.56787 349.99707 L 334.56787 351.0498 L 332.08105 351.0498 L 332.08105 358 L 330.85107 358 L 330.85Line truncated
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 52 KiB |
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Regenerate background.tiff and dmg-layout.DS_Store for the macOS DMG window (see README.md here).
|
||||
|
||||
Needs resvg on PATH and:
|
||||
pip install 'pillow>=12' ds_store==1.3.3 mac_alias==2.2.3
|
||||
|
||||
Runs on any OS, no Mac needed. Both outputs come from this script alone, so they carry nothing
|
||||
from the machine that made them: no local paths, user or disk names, dates or volume UUIDs.
|
||||
|
||||
python3 packaging/macos/dmg/generate.py
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from ds_store import DSStore
|
||||
from mac_alias import Alias, TargetInfo, VolumeInfo
|
||||
from PIL import Image, ImageCms
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# Keep in sync with package.sh (volume name) and README.md (layout).
|
||||
VOLUME = "ArtCraft Launcher"
|
||||
WIDTH, HEIGHT = 660, 400 # window content, pt; the background's 1x size
|
||||
TITLE_BAR = 32
|
||||
ICON_SIZE = 128
|
||||
PALETTE = 16 # colours in background.tiff
|
||||
ICONS = {"ArtCraft Launcher.app": (326, 205), "Applications": (574, 205)}
|
||||
# A fixed date for the alias and the colour profile. Finder never matches it against the image
|
||||
# (each build is a new volume): it finds the background by volume name and path.
|
||||
FIXED_DATE = datetime.datetime(2026, 10, 11, tzinfo=datetime.timezone.utc)
|
||||
|
||||
|
||||
def render(svg, width, out):
|
||||
# The SVG's text is already outlined and no fonts are loaded, so every machine draws the
|
||||
# same pixels.
|
||||
subprocess.run(
|
||||
["resvg", "--skip-system-fonts", "-w", str(width), svg, out],
|
||||
check=True,
|
||||
)
|
||||
with Image.open(out) as im:
|
||||
return im.convert("RGB")
|
||||
|
||||
|
||||
def quantize(im):
|
||||
# No dithering: dither noise compresses badly and the artwork has flat fills.
|
||||
return im.quantize(PALETTE, method=Image.Quantize.FASTOCTREE, dither=Image.Dither.NONE)
|
||||
|
||||
|
||||
def srgb_profile():
|
||||
# LittleCMS's built-in sRGB ("No copyright, use freely"), with its creation date fixed.
|
||||
icc = bytearray(ImageCms.ImageCmsProfile(ImageCms.createProfile("sRGB")).tobytes())
|
||||
d = FIXED_DATE
|
||||
icc[24:36] = b"".join(v.to_bytes(2, "big") for v in (d.year, d.month, d.day, 0, 0, 0))
|
||||
if icc[84:100] != bytes(16):
|
||||
sys.exit("error: the sRGB profile has an ID; recompute it after fixing the date")
|
||||
return bytes(icc)
|
||||
|
||||
|
||||
def write_tiff(svg, out):
|
||||
"""1x (72 dpi) and 2x (144 dpi) in one TIFF, as `tiffutil -cathidpicheck` writes them."""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
one, two = (
|
||||
quantize(render(svg, WIDTH * s, os.path.join(tmp, f"{s}x.png"))) for s in (1, 2)
|
||||
)
|
||||
two.encoderinfo = {"dpi": (144, 144)} # the rest as the first page
|
||||
# Opaque 16-colour palette, Deflate: the artwork is three inks plus antialiasing, and a palette
|
||||
# keeps the file under ~100 KB (repository assets must stay small). As PhotoCraft's.
|
||||
one.save(
|
||||
out,
|
||||
"TIFF",
|
||||
save_all=True,
|
||||
append_images=[two],
|
||||
compression="tiff_adobe_deflate", # brand-ok: Pillow's name for TIFF compression 8 (Deflate)
|
||||
dpi=(72, 72),
|
||||
icc_profile=srgb_profile(),
|
||||
)
|
||||
with Image.open(out) as im:
|
||||
im.seek(1)
|
||||
if im.info.get("dpi") != (144, 144):
|
||||
sys.exit("error: the 2x page isn't 144 dpi (needs Pillow 12 or later)")
|
||||
strips = []
|
||||
for page in (0, 1):
|
||||
im.seek(page)
|
||||
strips += zip(im.tag_v2[273], im.tag_v2[279]) # StripOffsets, StripByteCounts
|
||||
# TIFF keeps directories and tag data on even offsets. When a strip ends on an odd one, libtiff
|
||||
# leaves the padding byte after it uninitialised, so the file changed from run to run with
|
||||
# identical pixels. Zero it: nothing refers to that byte.
|
||||
starts = {o for o, _ in strips}
|
||||
with open(out, "r+b") as f:
|
||||
for o, n in strips:
|
||||
if (o + n) % 2 and o + n not in starts:
|
||||
f.seek(o + n)
|
||||
f.write(bytes(1))
|
||||
|
||||
|
||||
def background_alias():
|
||||
# What Finder stores for /.background/background.tiff on the volume, minus anything about the
|
||||
# machine (dmgbuild writes the same kind of alias from a mounted image). Disk type 5 (ejectable),
|
||||
# the volume flags and the folder/file ids (25, 26) are what Finder wrote on a test image; the ids
|
||||
# are only hints, as the volume's creation date.
|
||||
volume = VolumeInfo(VOLUME, FIXED_DATE, b"H+", 5, 0x0D02, b"\0\0")
|
||||
volume.posix_path = f"/Volumes/{VOLUME}"
|
||||
target = TargetInfo(0, "background.tiff", 25, 26, FIXED_DATE, b"\0\0\0\0", b"\0\0\0\0")
|
||||
target.folder_name = ".background"
|
||||
target.cnid_path = [25]
|
||||
target.carbon_path = f"{VOLUME}:.background:\0background.tiff"
|
||||
target.posix_path = "/.background/background.tiff"
|
||||
return Alias(volume=volume, target=target).to_bytes()
|
||||
|
||||
|
||||
def write_ds_store(out):
|
||||
if os.path.exists(out):
|
||||
os.remove(out)
|
||||
with DSStore.open(out, "w+") as d:
|
||||
d["."]["bwsp"] = {
|
||||
"ShowStatusBar": False,
|
||||
"ShowToolbar": False,
|
||||
"ShowTabView": False,
|
||||
"ContainerShowSidebar": False,
|
||||
"ShowSidebar": False,
|
||||
"WindowBounds": f"{{{{200, 528}}, {{{WIDTH}, {HEIGHT + TITLE_BAR}}}}}",
|
||||
}
|
||||
d["."]["icvp"] = {
|
||||
"viewOptionsVersion": 1,
|
||||
"backgroundType": 2,
|
||||
"backgroundImageAlias": background_alias(),
|
||||
"backgroundColorRed": 1.0,
|
||||
"backgroundColorGreen": 1.0,
|
||||
"backgroundColorBlue": 1.0,
|
||||
"gridOffsetX": 0.0,
|
||||
"gridOffsetY": 0.0,
|
||||
"gridSpacing": 100.0,
|
||||
"arrangeBy": "none",
|
||||
"showIconPreview": True,
|
||||
"showItemInfo": False,
|
||||
"labelOnBottom": True,
|
||||
"textSize": 16.0,
|
||||
"iconSize": float(ICON_SIZE),
|
||||
}
|
||||
d["."]["vSrn"] = ("long", 1)
|
||||
for name, pos in ICONS.items():
|
||||
d[name]["Iloc"] = pos
|
||||
|
||||
|
||||
def main():
|
||||
if not shutil.which("resvg"):
|
||||
sys.exit("error: resvg not found (brew install resvg / cargo install resvg --locked)")
|
||||
write_tiff(os.path.join(HERE, "background.svg"), os.path.join(HERE, "background.tiff"))
|
||||
write_ds_store(os.path.join(HERE, "dmg-layout.DS_Store"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -113,6 +113,10 @@ if /usr/libexec/PlistBuddy -c 'Print :LSUIElement' "$APP/Contents/Info.plist" >/
|
||||
fi
|
||||
printf 'APPL????' >"$APP/Contents/PkgInfo"
|
||||
|
||||
# Drop extended attributes picked up during the build (quarantine, FinderInfo) before signing:
|
||||
# `codesign --verify --strict` rejects them, and nothing after signing may touch the bundle.
|
||||
xattr -cr "$APP"
|
||||
|
||||
# Sign inside-out: nested code first, then the bundle itself (no --deep on the final signature).
|
||||
# Today the only nested code is the main executable; frameworks/helpers would be signed here too.
|
||||
sign --options runtime --entitlements "$HERE/entitlements.plist" "$APP/Contents/MacOS/$APP_NAME"
|
||||
@@ -133,12 +137,31 @@ STAGE="$WORK/dmg"
|
||||
mkdir -p "$STAGE"
|
||||
ditto "$APP" "$STAGE/$APP_NAME.app"
|
||||
ln -s /Applications "$STAGE/Applications"
|
||||
rm -f "$DMG" "$WORK/raw.dmg"
|
||||
# makehybrid + convert builds the image without attaching a device, unlike `create -srcfolder`,
|
||||
# which is flaky on CI runners ("Resource busy") and hangs in sandboxed sessions.
|
||||
hdiutil makehybrid -hfs -hfs-volume-name "$APP_NAME $VERSION" -hfs-openfolder "$STAGE" -o "$WORK/raw.dmg" "$STAGE"
|
||||
hdiutil convert "$WORK/raw.dmg" -format UDZO -imagekey zlib-level=9 -o "$DMG"
|
||||
rm -f "$WORK/raw.dmg"
|
||||
# Finder window layout: background, icon size and positions (packaging/macos/dmg/README.md).
|
||||
mkdir -p "$STAGE/.background"
|
||||
cp "$HERE/dmg/background.tiff" "$STAGE/.background/background.tiff"
|
||||
cp "$HERE/dmg/dmg-layout.DS_Store" "$STAGE/.DS_Store"
|
||||
rm -f "$DMG"
|
||||
# `hdiutil create -srcfolder` writes the files as they are. `makehybrid -hfs` gave every file a
|
||||
# com.apple.FinderInfo, which `codesign --verify --strict` rejects as "Finder information, or
|
||||
# similar detritus" on the app in the image and on a copy installed from it. `create` attaches a
|
||||
# device while it copies, which fails now and then on CI runners ("Resource busy"), so it gets a
|
||||
# few tries. verify.sh checks the app inside the finished image with --strict. The volume name has
|
||||
# no version: .DS_Store finds the background through an alias that includes it.
|
||||
# Usage: hdiutil_retry <what> <hdiutil args…>; tries five times with growing pauses.
|
||||
hdiutil_retry() {
|
||||
local what="$1" attempt
|
||||
shift
|
||||
for attempt in 1 2 3 4 5; do
|
||||
if hdiutil "$@"; then
|
||||
return 0
|
||||
fi
|
||||
[ "$attempt" = 5 ] && { echo "hdiutil $what failed 5 times" >&2; return 1; }
|
||||
warn "hdiutil $what failed (attempt $attempt of 5); retrying in $((attempt * 10))s"
|
||||
sleep $((attempt * 10))
|
||||
done
|
||||
}
|
||||
hdiutil_retry create create -srcfolder "$STAGE" -volname "$APP_NAME" -fs HFS+ -format UDZO -imagekey zlib-level=9 -ov "$DMG"
|
||||
sign "$DMG"
|
||||
codesign --verify --strict --verbose=2 "$DMG"
|
||||
if [ "$NOTARIZE" = 1 ]; then
|
||||
|
||||
@@ -112,10 +112,13 @@ if /usr/libexec/PlistBuddy -c 'Print :LSUIElement' "$APP/Contents/Info.plist" >/
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Integrity first: this must hold even for ad-hoc builds. Not --strict here: files read back from
|
||||
# the DMG's HFS volume (hdiutil makehybrid) report a com.apple.FinderInfo attribute, which strict
|
||||
# mode calls "detritus". package.sh already ran the strict check on the bundle before imaging it.
|
||||
codesign --verify --deep --verbose=2 "$APP"
|
||||
# Integrity first: this must hold even for ad-hoc builds, and with --strict, on the app in the
|
||||
# image and on a copy installed from it with ditto. A valid DMG signature says nothing about the
|
||||
# bundle inside: the old makehybrid image gave every file a com.apple.FinderInfo, which strict
|
||||
# mode rejects as "detritus".
|
||||
codesign --verify --strict --deep --verbose=2 "$APP"
|
||||
ditto "$APP" "$WORK/copy/$APP_NAME.app"
|
||||
codesign --verify --strict --deep --verbose=2 "$WORK/copy/$APP_NAME.app"
|
||||
details="$(codesign -d --verbose=2 "$APP" 2>&1)"
|
||||
printf '%s\n' "$details" | grep -E '^(Identifier|Format|CodeDirectory|Authority|Timestamp|TeamIdentifier)[= ]' || true
|
||||
|
||||
|
||||
Reference in new issue
Block a user