Skip to content

Commit 4ac0f4d

Browse files
committed
feat(release): add macos installers
Build macOS release artifacts for Apple Silicon and Intel, then teach the curl installer, npm postinstall, and CLI launcher to install and run the macOS app bundle alongside the existing Linux flow. This also bumps the release metadata to v0.1.29 so the new cross-platform installer pipeline can ship on a fresh tag.
1 parent d436edf commit 4ac0f4d

14 files changed

Lines changed: 338 additions & 101 deletions

File tree

.github/workflows/release.yml

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,24 @@ env:
1818
jobs:
1919
build-release:
2020
name: Build Release Artifacts
21-
runs-on: ubuntu-22.04
21+
runs-on: ${{ matrix.runner }}
2222
timeout-minutes: 45
23+
strategy:
24+
fail-fast: false
25+
matrix:
26+
include:
27+
- id: linux-x64
28+
runner: ubuntu-22.04
29+
artifact_name: release-linux-x64
30+
asset_prefix: x86_64
31+
- id: macos-x64
32+
runner: macos-13
33+
artifact_name: release-macos-x64
34+
asset_prefix: x86_64
35+
- id: macos-arm64
36+
runner: macos-14
37+
artifact_name: release-macos-arm64
38+
asset_prefix: aarch64
2339
steps:
2440
- name: Checkout
2541
uses: actions/checkout@v4
@@ -42,6 +58,7 @@ jobs:
4258
workspaces: apps/desktop/src-tauri -> target
4359

4460
- name: Install Linux dependencies
61+
if: runner.os == 'Linux'
4562
run: |
4663
sudo apt-get update
4764
sudo apt-get install -y \
@@ -65,6 +82,7 @@ jobs:
6582
run: pnpm build:desktop
6683

6784
- name: Strip bundled Wayland libs from AppImage
85+
if: runner.os == 'Linux'
6886
run: |
6987
set -euo pipefail
7088
APPIMAGE=$(ls apps/desktop/src-tauri/target/release/bundle/appimage/*.AppImage | head -n 1)
@@ -83,12 +101,36 @@ jobs:
83101
run: |
84102
set -euo pipefail
85103
VERSION=${GITHUB_REF_NAME#v}
86-
APPIMAGE=$(ls apps/desktop/src-tauri/target/release/bundle/appimage/*.AppImage | head -n 1)
87-
DEB=$(ls apps/desktop/src-tauri/target/release/bundle/deb/*.deb | head -n 1)
88-
89104
mkdir -p dist/release
90-
cp "$APPIMAGE" "dist/release/openlinear-${VERSION}-x86_64.AppImage"
91-
cp "$DEB" "dist/release/openlinear-${VERSION}-x86_64.deb"
105+
if [ "${{ runner.os }}" = "Linux" ]; then
106+
APPIMAGE=$(ls apps/desktop/src-tauri/target/release/bundle/appimage/*.AppImage | head -n 1)
107+
DEB=$(ls apps/desktop/src-tauri/target/release/bundle/deb/*.deb | head -n 1)
108+
cp "$APPIMAGE" "dist/release/openlinear-${VERSION}-${{ matrix.asset_prefix }}.AppImage"
109+
cp "$DEB" "dist/release/openlinear-${VERSION}-${{ matrix.asset_prefix }}.deb"
110+
else
111+
DMG=$(ls apps/desktop/src-tauri/target/release/bundle/dmg/*.dmg | head -n 1)
112+
APP_TARBALL=$(ls apps/desktop/src-tauri/target/release/bundle/macos/*.app.tar.gz | head -n 1)
113+
cp "$DMG" "dist/release/openlinear-${VERSION}-${{ matrix.asset_prefix }}.dmg"
114+
cp "$APP_TARBALL" "dist/release/openlinear-${VERSION}-${{ matrix.asset_prefix }}.app.tar.gz"
115+
fi
116+
117+
- name: Upload release artifacts
118+
uses: actions/upload-artifact@v4
119+
with:
120+
name: ${{ matrix.artifact_name }}
121+
path: dist/release/*
122+
123+
create-release:
124+
name: Create GitHub release
125+
needs: build-release
126+
runs-on: ubuntu-latest
127+
timeout-minutes: 15
128+
steps:
129+
- name: Download release artifacts
130+
uses: actions/download-artifact@v4
131+
with:
132+
path: dist/release
133+
merge-multiple: true
92134

93135
- name: Create GitHub release
94136
uses: softprops/action-gh-release@v2
@@ -98,7 +140,7 @@ jobs:
98140

99141
publish-npm:
100142
name: Publish npm package
101-
needs: build-release
143+
needs: create-release
102144
runs-on: ubuntu-latest
103145
timeout-minutes: 15
104146
permissions:
@@ -147,7 +189,7 @@ jobs:
147189
148190
publish-aur:
149191
name: Publish Arch package metadata
150-
needs: build-release
192+
needs: create-release
151193
runs-on: ubuntu-latest
152194
timeout-minutes: 15
153195
steps:

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ pnpm --filter @openlinear/landing dev
5959
## Releases
6060

6161
- curl installer: `curl -fsSL https://raw.githubusercontent.com/kaizen403/openlinear/main/install.sh | bash`
62-
- GitHub Releases: [github.com/kaizen403/openlinear/releases](https://github.com/kaizen403/openlinear/releases)
62+
- GitHub Releases: macOS `.dmg` / `.app.tar.gz`, Linux AppImage / `.deb`
6363
- npm package: `npm install -g openlinear`
6464
- Arch/AUR package: `openlinear-bin`
6565

apps/desktop/src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://schema.tauri.app/config/2",
33
"productName": "OpenLinear",
4-
"version": "0.1.28",
4+
"version": "0.1.29",
55
"identifier": "com.openlinear.app",
66
"build": {
77
"beforeDevCommand": "PORT=3000 pnpm --filter @openlinear/desktop-ui dev",

apps/landing/app/api/install/route.ts

Lines changed: 89 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ BLUE='\\033[0;34m'
1111
YELLOW='\\033[1;33m'
1212
NC='\\033[0m'
1313
14-
REPO="kaizen403/openlinear"
14+
REPO='kaizen403/openlinear'
1515
API_URL="https://api.github.com/repos/\${REPO}/releases/latest"
1616
RELEASES_URL="https://github.com/\${REPO}/releases/latest"
1717
INSTALL_DIR="\${HOME}/.openlinear"
1818
APPIMAGE_PATH="\${INSTALL_DIR}/openlinear.AppImage"
19+
MACOS_APP_PATH="\${INSTALL_DIR}/OpenLinear.app"
20+
MACOS_BINARY_PATH="\${MACOS_APP_PATH}/Contents/MacOS/OpenLinear"
1921
BIN_DIR="\${HOME}/.local/bin"
2022
BIN_PATH="\${BIN_DIR}/openlinear"
2123
@@ -26,23 +28,49 @@ echo ""
2628
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
2729
ARCH=$(uname -m)
2830
29-
if [ "$OS" != "linux" ] || [ "$ARCH" != "x86_64" ]; then
30-
echo -e "\${RED}This installer currently supports Linux x86_64 only.\${NC}"
31-
echo "Use one of these instead:"
32-
echo " npm install -g openlinear"
33-
echo " paru -S openlinear-bin"
34-
echo " \${RELEASES_URL}"
35-
exit 1
36-
fi
31+
INSTALL_MODE=""
32+
ASSET_PATTERN=""
33+
ASSET_LABEL=""
34+
35+
case "$OS/$ARCH" in
36+
linux/x86_64)
37+
INSTALL_MODE="linux-appimage"
38+
ASSET_PATTERN='-x86_64\\.AppImage$'
39+
ASSET_LABEL='Linux AppImage'
40+
;;
41+
darwin/x86_64)
42+
INSTALL_MODE="macos-app"
43+
ASSET_PATTERN='-x86_64\\.app\\.tar\\.gz$'
44+
ASSET_LABEL='macOS app bundle'
45+
;;
46+
darwin/arm64)
47+
INSTALL_MODE="macos-app"
48+
ASSET_PATTERN='-aarch64\\.app\\.tar\\.gz$'
49+
ASSET_LABEL='macOS app bundle'
50+
;;
51+
*)
52+
echo -e "\${RED}This installer currently supports macOS (Apple Silicon / Intel) and Linux x86_64 only.\${NC}"
53+
echo "Use one of these instead:"
54+
echo " npm install -g openlinear"
55+
echo " paru -S openlinear-bin"
56+
echo " \${RELEASES_URL}"
57+
exit 1
58+
;;
59+
esac
3760
3861
echo -e "Detected: \${YELLOW}$OS / $ARCH\${NC}"
3962
echo ""
4063
41-
if ! command -v curl \u0026> /dev/null; then
64+
if ! command -v curl >/dev/null 2>&1; then
4265
echo -e "\${RED}Error: curl is required\${NC}"
4366
exit 1
4467
fi
4568
69+
if [ "$INSTALL_MODE" = "macos-app" ] && ! command -v tar >/dev/null 2>&1; then
70+
echo -e "\${RED}Error: tar is required on macOS installs\${NC}"
71+
exit 1
72+
fi
73+
4674
echo -e "\${BLUE}Fetching latest release metadata...\${NC}"
4775
RELEASE_DATA=$(curl -fsSL -H "Accept: application/vnd.github+json" -H "User-Agent: openlinear-installer" "$API_URL")
4876
@@ -52,7 +80,7 @@ if [ -z "$RELEASE_DATA" ] || echo "$RELEASE_DATA" | grep -q "Not Found"; then
5280
fi
5381
5482
VERSION=$(echo "$RELEASE_DATA" | grep -o '"tag_name":[[:space:]]*"[^"]*"' | cut -d'"' -f4)
55-
ASSET_URL=$(echo "$RELEASE_DATA" | grep -o '"browser_download_url":[[:space:]]*"[^"]*' | cut -d'"' -f4 | grep -- '-x86_64.AppImage$' | head -1)
83+
ASSET_URL=$(echo "$RELEASE_DATA" | grep -o '"browser_download_url":[[:space:]]*"[^"]*' | cut -d'"' -f4 | grep -E -- "$ASSET_PATTERN" | head -1)
5684
5785
if [ -z "$VERSION" ]; then
5886
echo -e "\${RED}Error: Failed to parse version\${NC}"
@@ -63,25 +91,25 @@ echo -e "Latest version: \${GREEN}$VERSION\${NC}"
6391
echo ""
6492
6593
if [ -z "$ASSET_URL" ]; then
66-
echo -e "\${RED}No Linux AppImage found in the latest release.\${NC}"
94+
echo -e "\${RED}No \${ASSET_LABEL} found in the latest release.\${NC}"
6795
echo "Open \${RELEASES_URL} and download it manually."
6896
exit 1
6997
fi
7098
71-
echo -e "\${BLUE}Downloading...\${NC}"
99+
echo -e "\${BLUE}Downloading \${ASSET_LABEL}...\${NC}"
72100
73101
TMP_DIR=$(mktemp -d)
74102
trap 'rm -rf "$TMP_DIR"' EXIT
75103
76-
DOWNLOAD_FILE="$TMP_DIR/openlinear.AppImage"
77-
78-
curl -fL "$ASSET_URL" -o "$DOWNLOAD_FILE" --progress-bar
79-
80-
echo ""
81104
mkdir -p "$INSTALL_DIR" "$BIN_DIR"
82-
install -m 755 "$DOWNLOAD_FILE" "$APPIMAGE_PATH"
83105
84-
cat > "$BIN_PATH" <<'EOF'
106+
if [ "$INSTALL_MODE" = "linux-appimage" ]; then
107+
DOWNLOAD_FILE="$TMP_DIR/openlinear.AppImage"
108+
curl -fL "$ASSET_URL" -o "$DOWNLOAD_FILE" --progress-bar
109+
echo ""
110+
install -m 755 "$DOWNLOAD_FILE" "$APPIMAGE_PATH"
111+
112+
cat > "$BIN_PATH" <<'EOF'
85113
#!/usr/bin/env bash
86114
set -euo pipefail
87115
@@ -122,6 +150,41 @@ fi
122150
export APPIMAGE_EXTRACT_AND_RUN=1
123151
exec "$APPIMAGE_PATH" "$@"
124152
EOF
153+
else
154+
DOWNLOAD_FILE="$TMP_DIR/OpenLinear.app.tar.gz"
155+
EXTRACT_DIR="$TMP_DIR/extracted"
156+
157+
curl -fL "$ASSET_URL" -o "$DOWNLOAD_FILE" --progress-bar
158+
echo ""
159+
160+
mkdir -p "$EXTRACT_DIR"
161+
tar -xzf "$DOWNLOAD_FILE" -C "$EXTRACT_DIR"
162+
163+
APP_BUNDLE=$(find "$EXTRACT_DIR" -maxdepth 1 -type d -name '*.app' | head -n 1)
164+
if [ -z "$APP_BUNDLE" ]; then
165+
echo -e "\${RED}Failed to extract OpenLinear.app from the downloaded archive.\${NC}"
166+
exit 1
167+
fi
168+
169+
rm -rf "$MACOS_APP_PATH"
170+
mv "$APP_BUNDLE" "$MACOS_APP_PATH"
171+
chmod +x "$MACOS_BINARY_PATH"
172+
173+
cat > "$BIN_PATH" <<'EOF'
174+
#!/usr/bin/env bash
175+
set -euo pipefail
176+
177+
APP_PATH="\${HOME}/.openlinear/OpenLinear.app/Contents/MacOS/OpenLinear"
178+
179+
if [ ! -x "$APP_PATH" ]; then
180+
echo "OpenLinear macOS app not found at $APP_PATH" >&2
181+
echo "Reinstall with: curl -fsSL https://raw.githubusercontent.com/kaizen403/openlinear/main/install.sh | bash" >&2
182+
exit 1
183+
fi
184+
185+
exec "$APP_PATH" "$@"
186+
EOF
187+
fi
125188
126189
chmod +x "$BIN_PATH"
127190
@@ -131,14 +194,18 @@ if [[ ":$PATH:" != *":$BIN_DIR:"* ]]; then
131194
echo ""
132195
echo "Add the following to your shell profile (~/.bashrc, ~/.zshrc, etc.):"
133196
echo ""
134-
echo " export PATH=\"\\$HOME/.local/bin:\\$PATH\""
197+
echo ' export PATH="$HOME/.local/bin:$PATH"'
135198
echo ""
136199
fi
137200
138201
echo ""
139202
echo -e "\${GREEN}✓ OpenLinear $VERSION installed successfully!\${NC}"
140203
echo ""
141-
echo "AppImage: $APPIMAGE_PATH"
204+
if [ "$INSTALL_MODE" = "linux-appimage" ]; then
205+
echo "AppImage: $APPIMAGE_PATH"
206+
else
207+
echo "App bundle: $MACOS_APP_PATH"
208+
fi
142209
echo "Launcher: $BIN_PATH"
143210
echo ""
144211
echo "Run 'openlinear'"

apps/landing/app/docs/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ pnpm --filter @openlinear/landing dev`}</CodeBlock>
7676
<CodeBlock>{`curl -fsSL https://raw.githubusercontent.com/kaizen403/openlinear/main/install.sh | bash
7777
npm install -g openlinear
7878
paru -S openlinear-bin`}</CodeBlock>
79-
<p>The `curl` installer and npm launcher currently target Linux x64. Arch users should prefer `openlinear-bin`.</p>
79+
<p>The `curl` installer and npm launcher now support macOS (Apple Silicon / Intel) and Linux x64. Arch users should prefer `openlinear-bin`.</p>
8080
</Section>
8181
</div>
8282

docs/ARCHITECTURE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ OpenLinear now ships as a local-first desktop release with optional landing/docs
77
```text
88
GitHub repository
99
-> GitHub Releases
10+
-> macOS DMG / app bundle
1011
-> Linux AppImage / .deb
1112
-> raw install.sh
1213
-> curl installer
@@ -36,7 +37,7 @@ openlinear/
3637

3738
## Release channels
3839

39-
- GitHub Releases publish the desktop AppImage and `.deb`.
40+
- GitHub Releases publish macOS `.dmg` / `.app.tar.gz` assets plus Linux AppImage / `.deb`.
4041
- npm publishes the `openlinear` launcher package.
4142
- AUR publishes `openlinear-bin` metadata that points at the GitHub release artifacts.
4243
- `install.sh` in the repository is the canonical shell installer used by the `curl` command.
@@ -54,6 +55,7 @@ openlinear/
5455

5556
- Canonical installer: `install.sh`
5657
- Canonical command: `curl -fsSL https://raw.githubusercontent.com/kaizen403/openlinear/main/install.sh | bash`
58+
- Supported platforms: macOS (Apple Silicon / Intel) and Linux x64
5759
- `apps/landing/app/api/install/route.ts` mirrors that installer when the landing app is deployed.
5860

5961
## Hosted surface

docs/CICD.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ git tag vX.Y.Z -> GitHub Actions -> build desktop artifacts -> GitHub Release ->
2020

2121
- verifies the tag matches `packages/openlinear/package.json` and `apps/desktop/src-tauri/tauri.conf.json`
2222
- regenerates `packaging/aur/openlinear-bin/.SRCINFO`
23-
- builds Linux release artifacts for GitHub Releases
23+
- builds macOS and Linux release artifacts for GitHub Releases
2424
- publishes the `openlinear` package to npm
2525
- publishes `openlinear-bin` metadata to Arch/AUR
2626

0 commit comments

Comments
 (0)