2929 required : true
3030 APPLE_TEAM_ID :
3131 required : true
32+ SPARKLE_EDDSA_PRIVATE_KEY :
33+ required : true
3234
3335env :
3436 APP_NAME : " Hack Desktop"
5860 working-directory : ${{ env.PROJECT_PATH }}
5961 run : xcodegen generate
6062
63+ - name : Resolve release metadata
64+ id : meta
65+ run : |
66+ set -euo pipefail
67+ TAG="${{ inputs.tag }}"
68+ VERSION="${TAG#v}"
69+ ARCH="$(uname -m)"
70+ if [ "$ARCH" = "x86_64" ] || [ "$ARCH" = "amd64" ]; then
71+ ARCH="x86_64"
72+ elif [ "$ARCH" = "arm64" ] || [ "$ARCH" = "aarch64" ]; then
73+ ARCH="arm64"
74+ else
75+ echo "Unsupported architecture: $ARCH"
76+ exit 1
77+ fi
78+
79+ echo "tag=$TAG" >> "$GITHUB_OUTPUT"
80+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
81+ echo "arch=$ARCH" >> "$GITHUB_OUTPUT"
82+
6183 - name : Import certificate
6284 env :
6385 CERTIFICATE_P12 : ${{ secrets.APPLE_CERTIFICATE_P12 }}
@@ -88,6 +110,57 @@ jobs:
88110
89111 echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> $GITHUB_ENV
90112
113+ - name : Bundle CLI into app resources
114+ env :
115+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
116+ RELEASE_TAG : ${{ steps.meta.outputs.tag }}
117+ VERSION : ${{ steps.meta.outputs.version }}
118+ ARCH : ${{ steps.meta.outputs.arch }}
119+ run : |
120+ set -euo pipefail
121+ TARBALL="hack-$VERSION-darwin-$ARCH.tar.gz"
122+
123+ mkdir -p "$RUNNER_TEMP/cli"
124+ gh release download "$RELEASE_TAG" -p "$TARBALL" --dir "$RUNNER_TEMP/cli"
125+
126+ mkdir -p "$RUNNER_TEMP/cli/extract"
127+ tar -xzf "$RUNNER_TEMP/cli/$TARBALL" -C "$RUNNER_TEMP/cli/extract"
128+
129+ RELEASE_DIR="$RUNNER_TEMP/cli/extract/hack-$VERSION-release"
130+ if [ ! -d "$RELEASE_DIR" ]; then
131+ echo "Missing release dir: $RELEASE_DIR"
132+ exit 1
133+ fi
134+
135+ BUNDLE_DIR="apps/macos/App/BundledCLI"
136+ mkdir -p "$BUNDLE_DIR"
137+ rm -rf "$BUNDLE_DIR/hack" "$BUNDLE_DIR/assets" "$BUNDLE_DIR/binaries"
138+
139+ cp "$RELEASE_DIR/hack" "$BUNDLE_DIR/hack"
140+ if [ -d "$RELEASE_DIR/assets" ]; then
141+ cp -R "$RELEASE_DIR/assets" "$BUNDLE_DIR/assets"
142+ fi
143+ if [ -d "$RELEASE_DIR/binaries" ]; then
144+ cp -R "$RELEASE_DIR/binaries" "$BUNDLE_DIR/binaries"
145+ fi
146+
147+ - name : Codesign bundled CLI
148+ env :
149+ BUNDLED_HACK : apps/macos/App/BundledCLI/hack
150+ run : |
151+ set -euo pipefail
152+ if [ ! -f "$BUNDLED_HACK" ]; then
153+ echo "Missing bundled hack binary: $BUNDLED_HACK"
154+ exit 1
155+ fi
156+
157+ codesign --force --options runtime --timestamp \
158+ --sign "Developer ID Application" \
159+ --keychain "$KEYCHAIN_PATH" \
160+ "$BUNDLED_HACK"
161+
162+ codesign -vvv "$BUNDLED_HACK"
163+
91164 - name : Build and archive
92165 working-directory : ${{ env.PROJECT_PATH }}
93166 env :
@@ -203,13 +276,72 @@ jobs:
203276 "$RUNNER_TEMP/export/$APP_NAME.app" \
204277 "$RUNNER_TEMP/HackDesktop-$VERSION-macOS.zip"
205278
279+ - name : Generate Sparkle appcast
280+ env :
281+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
282+ RELEASE_TAG : ${{ github.event.release.tag_name || inputs.tag }}
283+ VERSION : ${{ steps.version.outputs.version }}
284+ SPARKLE_EDDSA_PRIVATE_KEY : ${{ secrets.SPARKLE_EDDSA_PRIVATE_KEY }}
285+ run : |
286+ set -euo pipefail
287+
288+ mkdir -p "$RUNNER_TEMP/sparkle"
289+ gh release download 2.8.1 -R sparkle-project/Sparkle -p "Sparkle-2.8.1.tar.xz" --dir "$RUNNER_TEMP/sparkle"
290+ tar -xf "$RUNNER_TEMP/sparkle/Sparkle-2.8.1.tar.xz" -C "$RUNNER_TEMP/sparkle"
291+
292+ ZIP_PATH="$RUNNER_TEMP/HackDesktop-$VERSION-macOS.zip"
293+ if [ ! -f "$ZIP_PATH" ]; then
294+ echo "Missing ZIP: $ZIP_PATH"
295+ exit 1
296+ fi
297+
298+ SIGN_LINE="$(printf '%s' "$SPARKLE_EDDSA_PRIVATE_KEY" | "$RUNNER_TEMP/sparkle/bin/sign_update" --ed-key-file - "$ZIP_PATH")"
299+ ED_SIGNATURE="$(echo "$SIGN_LINE" | sed -n 's/.*sparkle:edSignature=\"\\([^\"]*\\)\".*/\\1/p')"
300+ LENGTH="$(echo "$SIGN_LINE" | sed -n 's/.*length=\"\\([0-9]*\\)\".*/\\1/p')"
301+
302+ if [ -z "$ED_SIGNATURE" ] || [ -z "$LENGTH" ]; then
303+ echo "Failed to parse signature output: $SIGN_LINE"
304+ exit 1
305+ fi
306+
307+ PUB_DATE="$(date -u +"%a, %d %b %Y %H:%M:%S %z")"
308+ DOWNLOAD_URL="https://github.com/hack-dance/hack/releases/download/$RELEASE_TAG/HackDesktop-$VERSION-macOS.zip"
309+ NOTES_URL="https://github.com/hack-dance/hack/releases/tag/$RELEASE_TAG"
310+
311+ cat > "$RUNNER_TEMP/appcast.xml" << EOF
312+ <?xml version="1.0" encoding="utf-8"?>
313+ <rss version="2.0"
314+ xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle"
315+ xmlns:dc="http://purl.org/dc/elements/1.1/">
316+ <channel>
317+ <title>Hack Desktop</title>
318+ <link>https://github.com/hack-dance/hack</link>
319+ <description>Hack Desktop updates</description>
320+ <language>en</language>
321+ <item>
322+ <title>Version $VERSION</title>
323+ <sparkle:releaseNotesLink>$NOTES_URL</sparkle:releaseNotesLink>
324+ <pubDate>$PUB_DATE</pubDate>
325+ <enclosure
326+ url="$DOWNLOAD_URL"
327+ length="$LENGTH"
328+ type="application/octet-stream"
329+ sparkle:version="$VERSION"
330+ sparkle:shortVersionString="$VERSION"
331+ sparkle:edSignature="$ED_SIGNATURE" />
332+ </item>
333+ </channel>
334+ </rss>
335+ EOF
336+
206337 - name : Upload artifacts
207338 uses : actions/upload-artifact@v4
208339 with :
209340 name : macos-app
210341 path : |
211342 ${{ runner.temp }}/HackDesktop-*.dmg
212343 ${{ runner.temp }}/HackDesktop-*.zip
344+ ${{ runner.temp }}/appcast.xml
213345
214346 - name : Upload to release
215347 if : github.event_name == 'release' || inputs.tag != ''
@@ -221,6 +353,7 @@ jobs:
221353 gh release upload "$RELEASE_TAG" \
222354 "$RUNNER_TEMP/HackDesktop-$VERSION-macOS.dmg" \
223355 "$RUNNER_TEMP/HackDesktop-$VERSION-macOS.zip" \
356+ "$RUNNER_TEMP/appcast.xml" \
224357 --clobber
225358
226359 - name : Cleanup keychain
0 commit comments