55
66env :
77 BIN_NAME : obamify
8+ # Human-facing display name for the app
9+ APP_DISPLAY_NAME : obamify
10+ # Reverse-DNS bundle identifier
11+ APP_BUNDLE_ID : com.obamify
812
913jobs :
1014 build :
@@ -18,12 +22,10 @@ jobs:
1822 os_tag : linux
1923 target : x86_64-unknown-linux-gnu
2024 ext : " "
21- zip_cmd : zip -r
2225 - runner : macos-14
2326 os_tag : macos
2427 target : aarch64-apple-darwin
2528 ext : " "
26- zip_cmd : zip -r
2729 - runner : windows-latest
2830 os_tag : windows
2931 target : x86_64-pc-windows-msvc
@@ -36,59 +38,157 @@ jobs:
3638 with :
3739 targets : ${{ matrix.target }}
3840
39- # Works on all OSes (default shell is bash on Linux/macOS, pwsh on Windows)
4041 - name : Cargo build (release)
4142 run : cargo build --release --target ${{ matrix.target }}
4243
43- # ---- Package on Linux/macOS (bash) ----
44- - name : Package (Unix)
45- if : ${{ !startsWith(matrix.runner, 'windows') }}
44+ - name : Package (.app bundle)
45+ if : ${{ startsWith(matrix.runner, 'macos') }}
46+ shell : bash
4647 run : |
4748 set -euo pipefail
4849 APP="${{ env.BIN_NAME }}"
49- PKG="dist/${APP}-${{ matrix.os_tag }}"
50+ DISP="${{ env.APP_DISPLAY_NAME }}"
51+ BUNDLE_ID="${{ env.APP_BUNDLE_ID }}"
5052 BIN="target/${{ matrix.target }}/release/${APP}${{ matrix.ext }}"
5153 test -f "$BIN" || (echo "Missing $BIN" && exit 1)
5254
53- mkdir -p "$PKG"
54- cp "$BIN" "$PKG/"
55-
56- test -d presets || (echo "./presets missing" && exit 1)
57- cp -R presets "$PKG/"
58-
59- test -f target.png || (echo "./target.png missing" && exit 1)
60- test -f weights.png || (echo "./weights.png missing" && exit 1)
61- test -f blank.png || (echo "./weights.png missing" && exit 1)
62- cp target.png weights.png blank.png "$PKG/"
55+ # Derive version from Cargo metadata (fallback to 0.0.0)
56+ VERSION="$(cargo metadata --format-version 1 --no-deps | python3 -c "import sys, json; m=json.load(sys.stdin); p=[x for x in m['packages'] if x['name']=='${APP}']; print(p[0]['version'] if p else '0.0.0')")"
57+
58+ APPDIR="dist/${DISP}.app"
59+ mkdir -p "${APPDIR}/Contents/MacOS" "${APPDIR}/Contents/Resources"
60+
61+ # Binary
62+ cp "$BIN" "${APPDIR}/Contents/MacOS/${APP}"
63+ chmod +x "${APPDIR}/Contents/MacOS/${APP}"
64+
65+ # Icon (optional) - put your .icns at assets/macos/icon.icns
66+ if [ -f assets/macos/icon.icns ]; then
67+ cp assets/macos/icon.icns "${APPDIR}/Contents/Resources/AppIcon.icns"
68+ ICON_LINE="<key>CFBundleIconFile</key><string>AppIcon</string>"
69+ else
70+ ICON_LINE=""
71+ fi
72+
73+ # Generate Info.plist
74+ cat > "${APPDIR}/Contents/Info.plist" <<PLIST
75+ <?xml version="1.0" encoding="UTF-8"?>
76+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
77+ <plist version="1.0">
78+ <dict>
79+ <key>CFBundleName</key><string>${DISP}</string>
80+ <key>CFBundleDisplayName</key><string>${DISP}</string>
81+ <key>CFBundleExecutable</key><string>${APP}</string>
82+ <key>CFBundleIdentifier</key><string>${BUNDLE_ID}</string>
83+ <key>CFBundleVersion</key><string>${VERSION}</string>
84+ <key>CFBundleShortVersionString</key><string>${VERSION}</string>
85+ <key>LSMinimumSystemVersion</key><string>11.0</string>
86+ <key>LSApplicationCategoryType</key><string>public.app-category.utilities</string>
87+ <key>NSHighResolutionCapable</key><true/>
88+ ${ICON_LINE}
89+ </dict>
90+ </plist>
91+ PLIST
92+
93+ # Zip for distribution (zip keeps resource forks properly with ditto flags)
94+ mkdir -p dist
95+ ditto -c -k --sequesterRsrc --keepParent "${APPDIR}" "dist/${APP}-${{ matrix.os_tag }}.zip"
96+
97+ - name : Package (Linux AppImage)
98+ if : ${{ startsWith(matrix.runner, 'ubuntu') }}
99+ shell : bash
100+ run : |
101+ set -euo pipefail
102+ APP="${{ env.BIN_NAME }}"
103+ DISP="${{ env.APP_DISPLAY_NAME }}"
104+ BIN="target/${{ matrix.target }}/release/${APP}"
105+ test -f "$BIN" || (echo "Missing $BIN" && exit 1)
63106
64- (cd dist && ${{ matrix.zip_cmd }} "${APP}-${{ matrix.os_tag }}.zip" "${APP}-${{ matrix.os_tag }}")
107+ mkdir -p dist/AppDir/usr/bin dist/AppDir/usr/share/applications dist/AppDir/usr/share/icons/hicolor/256x256/apps
108+
109+ # Put binary in AppDir
110+ cp "$BIN" dist/AppDir/usr/bin/${APP}
111+ chmod +x dist/AppDir/usr/bin/${APP}
112+
113+ # Icon (optional) - place a 256x256 PNG at assets/linux/icon.png
114+ if [ -f assets/linux/icon.png ]; then
115+ cp assets/linux/icon.png dist/AppDir/usr/share/icons/hicolor/256x256/apps/${APP}.png
116+ ICON_PATH=dist/AppDir/usr/share/icons/hicolor/256x256/apps/${APP}.png
117+ else
118+ ICON_PATH=
119+ fi
120+
121+ # Desktop file
122+ cat > dist/AppDir/usr/share/applications/${APP}.desktop <<DESKTOP
123+ [Desktop Entry]
124+ Type=Application
125+ Name=${DISP}
126+ Exec=${APP}
127+ Icon=${APP}
128+ Categories=Utility;
129+ Terminal=false
130+ DESKTOP
131+
132+ # AppRun launcher
133+ cat > dist/AppDir/AppRun <<'APPRUN'
134+ #!/bin/sh
135+ HERE="$(dirname "$(readlink -f "$0")")"
136+ export PATH="$HERE/usr/bin:$PATH"
137+ exec "${HERE}/usr/bin/${APP_BIN:-${APP}}" "$@"
138+ APPRUN
139+ sed -i "s/\${APP}/${APP}/g" dist/AppDir/AppRun
140+ chmod +x dist/AppDir/AppRun
141+
142+ # Fetch linuxdeploy + appimagetool
143+ wget -q "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage" -O linuxdeploy
144+ wget -q "https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-x86_64.AppImage" -O appimagetool
145+ chmod +x linuxdeploy appimagetool
146+
147+ # Build AppImage
148+ ./linuxdeploy --appdir dist/AppDir \
149+ -d dist/AppDir/usr/share/applications/${APP}.desktop \
150+ $( [ -n "$ICON_PATH" ] && echo "-i $ICON_PATH" ) \
151+ --output appimage
152+
153+ # Move result(s) to dist with stable name
154+ mkdir -p dist
155+ mv *.AppImage "dist/${APP}-${{ matrix.os_tag }}.AppImage" || true
156+
157+ # Tarball fallback
158+ tar -C "target/${{ matrix.target }}/release" -czf "dist/${APP}-${{ matrix.os_tag }}.tar.gz" "${APP}"
65159
66- # ---- Package on Windows (PowerShell) ----
67160 - name : Package (Windows)
68161 if : startsWith(matrix.runner, 'windows')
69162 shell : pwsh
70163 run : |
71164 $ErrorActionPreference = "Stop"
72165 $App = "${{ env.BIN_NAME }}"
73- $Pkg = "dist/$App-${{ matrix.os_tag }}"
74166 $Bin = "target/${{ matrix.target }}/release/$App${{ matrix.ext }}"
75-
76167 if (-not (Test-Path $Bin)) { throw "Missing $Bin" }
77- New-Item -ItemType Directory -Force -Path $Pkg | Out-Null
78- Copy-Item $Bin -Destination $Pkg -Force
79-
80- if (-not (Test-Path "presets")) { throw "./presets missing" }
81- Copy-Item -Recurse -Force "presets" $Pkg
168+ New-Item -ItemType Directory -Force -Path "dist" | Out-Null
169+ Copy-Item $Bin -Destination "dist/$App-${{ matrix.os_tag }}${{ matrix.ext }}" -Force
82170
83- if (-not (Test-Path "target.png")) { throw "./target.png missing" }
84- if (-not (Test-Path "weights.png")) { throw "./weights.png missing" }
85- if (-not (Test-Path "blank.png")) { throw "./blank.png missing" }
86- Copy-Item "target.png","weights.png","blank.png" -Destination $Pkg -Force
171+ - name : Upload artifacts (macOS)
172+ if : ${{ startsWith(matrix.runner, 'macos') }}
173+ uses : actions/upload-artifact@v4
174+ with :
175+ name : ${{ env.BIN_NAME }}-${{ matrix.os_tag }}
176+ path : |
177+ dist/${{ env.APP_DISPLAY_NAME }}.app
178+ dist/${{ env.BIN_NAME }}-${{ matrix.os_tag }}.zip
87179
88- Compress-Archive -Path "$Pkg/*" -DestinationPath "dist/$App-${{ matrix.os_tag }}.zip" -Force
180+ - name : Upload artifacts (Linux)
181+ if : ${{ startsWith(matrix.runner, 'ubuntu') }}
182+ uses : actions/upload-artifact@v4
183+ with :
184+ name : ${{ env.BIN_NAME }}-${{ matrix.os_tag }}
185+ path : |
186+ dist/${{ env.BIN_NAME }}-${{ matrix.os_tag }}.AppImage
187+ dist/${{ env.BIN_NAME }}-${{ matrix.os_tag }}.tar.gz
89188
90- - name : Upload artifacts
189+ - name : Upload artifacts (Windows)
190+ if : startsWith(matrix.runner, 'windows')
91191 uses : actions/upload-artifact@v4
92192 with :
93193 name : ${{ env.BIN_NAME }}-${{ matrix.os_tag }}
94- path : dist/*.zip
194+ path : dist/${{ env.BIN_NAME }}-${{ matrix.os_tag }}${{ matrix.ext }}
0 commit comments