Skip to content

Commit e9f7924

Browse files
committed
feat: add macOS native installer with GitHub Actions workflow and documentation
1 parent 8f4c1ba commit e9f7924

File tree

2 files changed

+296
-0
lines changed

2 files changed

+296
-0
lines changed
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
name: macOS Installer Build
2+
3+
on:
4+
push:
5+
branches:
6+
- '*' # This will trigger on any branch push
7+
tags:
8+
- "*" # This will trigger on any tag push
9+
pull_request:
10+
branches:
11+
- main
12+
13+
jobs:
14+
build-macos-installer:
15+
name: Build macOS Installer
16+
runs-on: macos-latest
17+
strategy:
18+
matrix:
19+
arch: [x86_64, arm64]
20+
21+
steps:
22+
- name: Checkout code
23+
uses: actions/checkout@v3
24+
with:
25+
fetch-depth: 0
26+
27+
- name: Set up Python 3.9
28+
uses: actions/setup-python@v4
29+
with:
30+
python-version: '3.9'
31+
32+
- name: Install dependencies
33+
run: |
34+
python -m pip install --upgrade pip
35+
pip install -r requirements.txt
36+
pip install py2app==0.28.6 pyinstaller==6.1.0
37+
38+
- name: Extract metadata
39+
id: meta
40+
run: |
41+
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
42+
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
43+
echo "IS_TAG=true" >> $GITHUB_OUTPUT
44+
else
45+
echo "VERSION=$(cat version.txt)" >> $GITHUB_OUTPUT
46+
echo "BRANCH=${GITHUB_REF#refs/heads/}" >> $GITHUB_OUTPUT
47+
echo "IS_TAG=false" >> $GITHUB_OUTPUT
48+
fi
49+
50+
- name: Create .icns file from .ico
51+
run: |
52+
mkdir -p MacOS/Resources
53+
# Convert .ico to .png files of various sizes
54+
mkdir -p icon.iconset
55+
sips -s format png frontend/static/logo/huntarr.ico --out icon.iconset/icon_16x16.png --resampleWidth 16 > /dev/null 2>&1 || true
56+
sips -s format png frontend/static/logo/huntarr.ico --out icon.iconset/icon_32x32.png --resampleWidth 32 > /dev/null 2>&1 || true
57+
sips -s format png frontend/static/logo/huntarr.ico --out icon.iconset/icon_64x64.png --resampleWidth 64 > /dev/null 2>&1 || true
58+
sips -s format png frontend/static/logo/huntarr.ico --out icon.iconset/icon_128x128.png --resampleWidth 128 > /dev/null 2>&1 || true
59+
sips -s format png frontend/static/logo/huntarr.ico --out icon.iconset/icon_256x256.png --resampleWidth 256 > /dev/null 2>&1 || true
60+
sips -s format png frontend/static/logo/huntarr.ico --out icon.iconset/icon_512x512.png --resampleWidth 512 > /dev/null 2>&1 || true
61+
cp icon.iconset/icon_32x32.png icon.iconset/[email protected] 2>/dev/null || true
62+
cp icon.iconset/icon_64x64.png icon.iconset/[email protected] 2>/dev/null || true
63+
cp icon.iconset/icon_128x128.png icon.iconset/[email protected] 2>/dev/null || true
64+
cp icon.iconset/icon_256x256.png icon.iconset/[email protected] 2>/dev/null || true
65+
cp icon.iconset/icon_512x512.png icon.iconset/[email protected] 2>/dev/null || true
66+
67+
# Create .icns file
68+
iconutil -c icns icon.iconset -o frontend/static/logo/huntarr.icns
69+
70+
# If the conversion fails, create a generic icon
71+
if [ ! -f frontend/static/logo/huntarr.icns ]; then
72+
echo "Icon conversion failed, creating placeholder icon"
73+
# Create a simple placeholder icon using macOS system icons
74+
cp /System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericApplicationIcon.icns frontend/static/logo/huntarr.icns
75+
fi
76+
77+
- name: Build macOS app bundle
78+
run: |
79+
# Create pyinstaller spec file
80+
cat > Huntarr.spec << 'EOL'
81+
# -*- mode: python ; coding: utf-8 -*-
82+
from PyInstaller.building.api import PYZ, EXE, COLLECT
83+
from PyInstaller.building.build_main import Analysis
84+
from PyInstaller.building.datastruct import Tree
85+
import os
86+
87+
block_cipher = None
88+
89+
a = Analysis(
90+
['main.py'],
91+
pathex=['.'],
92+
binaries=[],
93+
datas=[
94+
('frontend', 'frontend'),
95+
('version.txt', '.'),
96+
('README.md', '.'),
97+
('LICENSE', '.'),
98+
],
99+
hiddenimports=[
100+
'flask',
101+
'requests',
102+
'waitress',
103+
'bcrypt',
104+
'qrcode',
105+
'PIL',
106+
'pyotp',
107+
],
108+
hookspath=[],
109+
hooksconfig={},
110+
runtime_hooks=[],
111+
excludes=[],
112+
win_no_prefer_redirects=False,
113+
win_private_assemblies=False,
114+
cipher=block_cipher,
115+
noarchive=False,
116+
)
117+
118+
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
119+
120+
exe = EXE(
121+
pyz,
122+
a.scripts,
123+
[],
124+
exclude_binaries=True,
125+
name='Huntarr',
126+
debug=False,
127+
bootloader_ignore_signals=False,
128+
strip=False,
129+
upx=True,
130+
console=False,
131+
disable_windowed_traceback=False,
132+
argv_emulation=True,
133+
target_arch='${{ matrix.arch }}',
134+
codesign_identity=None,
135+
entitlements_file=None,
136+
icon='frontend/static/logo/huntarr.icns',
137+
)
138+
139+
coll = COLLECT(
140+
exe,
141+
a.binaries,
142+
a.zipfiles,
143+
a.datas,
144+
strip=False,
145+
upx=True,
146+
upx_exclude=[],
147+
name='Huntarr',
148+
)
149+
150+
app = BUNDLE(
151+
coll,
152+
name='Huntarr.app',
153+
icon='frontend/static/logo/huntarr.icns',
154+
bundle_identifier='io.huntarr.app',
155+
info_plist={
156+
'CFBundleShortVersionString': '${{ steps.meta.outputs.VERSION }}',
157+
'CFBundleVersion': '${{ steps.meta.outputs.VERSION }}',
158+
'NSHighResolutionCapable': True,
159+
'NSRequiresAquaSystemAppearance': False,
160+
'LSEnvironment': {
161+
'PYTHONPATH': '@executable_path/../Resources/lib/python3.9/site-packages',
162+
},
163+
},
164+
)
165+
EOL
166+
167+
# Build the app using PyInstaller
168+
python -m PyInstaller Huntarr.spec --clean
169+
170+
- name: Create PKG installer
171+
run: |
172+
# Create a simple postinstall script
173+
mkdir -p scripts
174+
cat > scripts/postinstall << 'EOL'
175+
#!/bin/bash
176+
177+
# Create config directory in user's Application Support
178+
mkdir -p "$HOME/Library/Application Support/Huntarr/config"
179+
mkdir -p "$HOME/Library/Application Support/Huntarr/config/settings"
180+
mkdir -p "$HOME/Library/Application Support/Huntarr/config/stateful"
181+
mkdir -p "$HOME/Library/Application Support/Huntarr/config/user"
182+
mkdir -p "$HOME/Library/Application Support/Huntarr/config/logs"
183+
184+
# Set permissions
185+
chmod -R 755 "$HOME/Library/Application Support/Huntarr"
186+
187+
exit 0
188+
EOL
189+
190+
chmod +x scripts/postinstall
191+
192+
# Create PKG installer
193+
version="${{ steps.meta.outputs.VERSION }}"
194+
branch="${{ steps.meta.outputs.BRANCH }}"
195+
tag_suffix=""
196+
197+
if [[ "${{ steps.meta.outputs.IS_TAG }}" == "true" ]]; then
198+
pkg_name="Huntarr-${version}-${{ matrix.arch }}.pkg"
199+
else
200+
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
201+
pkg_name="Huntarr-${version}-main-${{ matrix.arch }}.pkg"
202+
elif [[ "${{ github.ref }}" == "refs/heads/dev" ]]; then
203+
pkg_name="Huntarr-${version}-dev-${{ matrix.arch }}.pkg"
204+
else
205+
pkg_name="Huntarr-${version}-${branch}-${{ matrix.arch }}.pkg"
206+
fi
207+
fi
208+
209+
pkgbuild --root dist/ \
210+
--scripts scripts/ \
211+
--identifier io.huntarr.app \
212+
--version ${version} \
213+
--install-location /Applications \
214+
${pkg_name}
215+
216+
- name: Upload installer as artifact
217+
uses: actions/upload-artifact@v4
218+
with:
219+
name: huntarr-macos-${{ matrix.arch }}-installer
220+
path: '*.pkg'
221+
retention-days: 30
222+
223+
- name: Upload to release
224+
if: steps.meta.outputs.IS_TAG == 'true'
225+
uses: softprops/action-gh-release@v1
226+
with:
227+
files: '*.pkg'
228+
env:
229+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

docs/macos-installer.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Huntarr.io macOS Installer Guide
2+
3+
## Overview
4+
5+
Huntarr.io now provides native macOS installers alongside the primary Docker deployment method. The macOS installers are automatically built and published through GitHub Actions whenever changes are pushed to the repository.
6+
7+
## Available Installers
8+
9+
Two types of macOS installers are generated:
10+
11+
1. **Intel (x86_64)** - For Intel-based Macs
12+
2. **ARM (arm64)** - For Apple Silicon Macs (M1, M2, etc.)
13+
14+
## Installation
15+
16+
1. Download the appropriate installer package (.pkg) for your Mac from the [GitHub Releases](https://github.com/plexguide/Huntarr.io/releases) page.
17+
2. Double-click the downloaded .pkg file to start the installation process.
18+
3. Follow the on-screen instructions to complete the installation.
19+
4. The application will be installed in your `/Applications` folder.
20+
21+
## Configuration
22+
23+
When first launched, Huntarr.io will create the necessary configuration directories:
24+
25+
```
26+
~/Library/Application Support/Huntarr/config/
27+
├── logs/
28+
├── settings/
29+
├── stateful/
30+
└── user/
31+
```
32+
33+
This structure mirrors the Docker container's `/config` directory structure.
34+
35+
## Differences from Docker Version
36+
37+
The macOS application functions similarly to the Docker version with a few key differences:
38+
39+
1. Data is stored in the user's Application Support folder instead of a Docker volume
40+
2. The app runs as a native macOS application rather than in a container
41+
3. System requirements are tied to macOS version and architecture rather than Docker
42+
43+
## Troubleshooting
44+
45+
If you encounter issues:
46+
47+
1. Check the log files in `~/Library/Application Support/Huntarr/config/logs/`
48+
2. Ensure proper permissions for the application folders
49+
3. Verify your macOS version is compatible (macOS 10.15 Catalina or newer recommended)
50+
51+
## Notes
52+
53+
- The Docker version remains the primary supported deployment method
54+
- The macOS version is provided as a convenience for users who prefer native applications
55+
- Both Intel and ARM versions are built with the same codebase but optimized for each architecture
56+
57+
## Build Process
58+
59+
The macOS installers are built automatically using GitHub Actions with the following process:
60+
61+
1. Python 3.9 environment is set up on a macOS runner
62+
2. The Huntarr.io icon is converted to macOS .icns format
63+
3. PyInstaller bundles the application into a native macOS .app
64+
4. A PKG installer is created using macOS pkgbuild
65+
5. The installer is uploaded as an artifact and attached to GitHub releases
66+
67+
The build process handles both Intel (x86_64) and ARM (arm64) architectures separately to ensure optimal performance on each platform.

0 commit comments

Comments
 (0)