Skip to content

Commit 39f7334

Browse files
committed
feat(update): add CLI + macOS updaters
1 parent 584af84 commit 39f7334

13 files changed

Lines changed: 1436 additions & 3 deletions

File tree

.github/workflows/release-macos-app.yml

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ on:
2929
required: true
3030
APPLE_TEAM_ID:
3131
required: true
32+
SPARKLE_EDDSA_PRIVATE_KEY:
33+
required: true
3234

3335
env:
3436
APP_NAME: "Hack Desktop"
@@ -58,6 +60,26 @@ jobs:
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

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ jobs:
102102
dist/release/hack-install.sh
103103
104104
macos-app:
105-
needs: create-release
105+
needs: [create-release, build]
106106
permissions:
107107
contents: write
108108
uses: ./.github/workflows/release-macos-app.yml
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Bundled CLI
2+
3+
CI populates this directory during release builds with the matching `hack` CLI binary and runtime assets.
4+
5+
At runtime, the macOS app will sync these files into `~/.hack/bin` and `~/.hack/assets` when needed.
6+

0 commit comments

Comments
 (0)