mirror of
https://github.com/Cleanuparr/Cleanuparr.git
synced 2026-03-25 09:31:38 -04:00
377 lines
14 KiB
YAML
377 lines
14 KiB
YAML
name: Build macOS Installers
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
app_version:
|
|
description: 'Application version'
|
|
type: string
|
|
required: false
|
|
default: ''
|
|
|
|
jobs:
|
|
build-macos-installer:
|
|
name: Build macOS ${{ matrix.arch }} Installer
|
|
runs-on: ${{ matrix.runner }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- arch: Intel
|
|
runner: macos-15-intel
|
|
runtime: osx-x64
|
|
min_os_version: "10.15"
|
|
artifact_suffix: intel
|
|
- arch: ARM
|
|
runner: macos-15
|
|
runtime: osx-arm64
|
|
min_os_version: "11.0"
|
|
artifact_suffix: arm64
|
|
|
|
steps:
|
|
- name: Set variables
|
|
run: |
|
|
repoFullName=${{ github.repository }}
|
|
ref=${{ github.ref }}
|
|
|
|
# Use input version if provided, otherwise determine from ref
|
|
if [[ -n "${{ inputs.app_version }}" ]]; then
|
|
appVersion="${{ inputs.app_version }}"
|
|
releaseVersion="v$appVersion"
|
|
elif [[ "$ref" =~ ^refs/tags/ ]]; then
|
|
releaseVersion=${ref##refs/tags/}
|
|
appVersion=${releaseVersion#v}
|
|
else
|
|
# For manual dispatch, use a default version
|
|
releaseVersion="dev-$(date +%Y%m%d-%H%M%S)"
|
|
appVersion="0.0.1-dev"
|
|
fi
|
|
|
|
repositoryName=${repoFullName#*/}
|
|
|
|
echo "githubRepository=${{ github.repository }}" >> $GITHUB_ENV
|
|
echo "githubRepositoryName=$repositoryName" >> $GITHUB_ENV
|
|
echo "releaseVersion=$releaseVersion" >> $GITHUB_ENV
|
|
echo "appVersion=$appVersion" >> $GITHUB_ENV
|
|
echo "executableName=Cleanuparr.Api" >> $GITHUB_ENV
|
|
|
|
- name: Get vault secrets
|
|
uses: hashicorp/vault-action@v2
|
|
with:
|
|
url: ${{ secrets.VAULT_HOST }}
|
|
method: approle
|
|
roleId: ${{ secrets.VAULT_ROLE_ID }}
|
|
secretId: ${{ secrets.VAULT_SECRET_ID }}
|
|
secrets:
|
|
secrets/data/github repo_readonly_pat | REPO_READONLY_PAT;
|
|
secrets/data/github packages_pat | PACKAGES_PAT
|
|
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
with:
|
|
repository: ${{ env.githubRepository }}
|
|
ref: ${{ github.ref_name }}
|
|
token: ${{ env.REPO_READONLY_PAT }}
|
|
fetch-depth: 0
|
|
|
|
- name: Download frontend artifact
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: frontend-dist
|
|
path: code/frontend/dist/ui/browser
|
|
|
|
- name: Setup .NET
|
|
uses: actions/setup-dotnet@v5
|
|
with:
|
|
dotnet-version: 10.0.200
|
|
|
|
- name: Restore .NET dependencies
|
|
run: |
|
|
dotnet nuget add source --username ${{ github.repository_owner }} --password ${{ env.PACKAGES_PAT }} --store-password-in-clear-text --name Cleanuparr https://nuget.pkg.github.com/Cleanuparr/index.json
|
|
dotnet restore code/backend/${{ env.executableName }}/${{ env.executableName }}.csproj
|
|
|
|
- name: Build macOS ${{ matrix.arch }} executable
|
|
run: |
|
|
# Clean any existing output directory
|
|
rm -rf dist
|
|
mkdir -p dist/temp
|
|
|
|
# Build to a temporary location
|
|
dotnet publish code/backend/${{ env.executableName }}/${{ env.executableName }}.csproj \
|
|
-c Release \
|
|
--runtime ${{ matrix.runtime }} \
|
|
--self-contained true \
|
|
-o dist/temp \
|
|
/p:PublishSingleFile=true \
|
|
/p:Version=${{ env.appVersion }} \
|
|
/p:DebugType=None \
|
|
/p:DebugSymbols=false \
|
|
/p:UseAppHost=true \
|
|
/p:EnableMacOSCodeSign=false \
|
|
/p:CodeSignOnCopy=false \
|
|
/p:_CodeSignDuringBuild=false \
|
|
/p:PublishTrimmed=false \
|
|
/p:TrimMode=link
|
|
|
|
# Create proper app bundle structure
|
|
mkdir -p dist/Cleanuparr.app/Contents/MacOS
|
|
|
|
# Copy the built executable (note: AssemblyName is "Cleanuparr" not "Cleanuparr.Api")
|
|
cp dist/temp/Cleanuparr dist/Cleanuparr.app/Contents/MacOS/Cleanuparr
|
|
|
|
# Copy frontend directly to where it belongs in the app bundle
|
|
mkdir -p dist/Cleanuparr.app/Contents/MacOS/wwwroot
|
|
cp -r code/frontend/dist/ui/browser/* dist/Cleanuparr.app/Contents/MacOS/wwwroot/
|
|
|
|
# Copy any additional runtime files if they exist
|
|
if [ -d "dist/temp" ]; then
|
|
find dist/temp -name "*.dylib" -exec cp {} dist/Cleanuparr.app/Contents/MacOS/ \; 2>/dev/null || true
|
|
find dist/temp -name "createdump" -exec cp {} dist/Cleanuparr.app/Contents/MacOS/ \; 2>/dev/null || true
|
|
fi
|
|
|
|
- name: Post-build setup
|
|
run: |
|
|
# Make sure the executable is actually executable
|
|
chmod +x dist/Cleanuparr.app/Contents/MacOS/Cleanuparr
|
|
|
|
# Remove any .pdb files that might have been created
|
|
find dist/Cleanuparr.app/Contents/MacOS -name "*.pdb" -delete 2>/dev/null || true
|
|
|
|
echo "Checking architecture of built binary:"
|
|
file dist/Cleanuparr.app/Contents/MacOS/Cleanuparr
|
|
if command -v lipo >/dev/null 2>&1; then
|
|
lipo -info dist/Cleanuparr.app/Contents/MacOS/Cleanuparr
|
|
fi
|
|
|
|
echo "Files in MacOS directory:"
|
|
ls -la dist/Cleanuparr.app/Contents/MacOS/
|
|
|
|
- name: Create macOS app bundle structure
|
|
run: |
|
|
# Create proper app bundle structure
|
|
mkdir -p dist/Cleanuparr.app/Contents/{MacOS,Resources,Frameworks}
|
|
|
|
# Convert ICO to ICNS for macOS app bundle
|
|
if command -v iconutil >/dev/null 2>&1; then
|
|
# Create iconset directory structure
|
|
mkdir -p Cleanuparr.iconset
|
|
|
|
# Use existing PNG files from Logo directory for different sizes
|
|
cp Logo/16.png Cleanuparr.iconset/icon_16x16.png
|
|
cp Logo/32.png Cleanuparr.iconset/icon_16x16@2x.png
|
|
cp Logo/32.png Cleanuparr.iconset/icon_32x32.png
|
|
cp Logo/64.png Cleanuparr.iconset/icon_32x32@2x.png
|
|
cp Logo/128.png Cleanuparr.iconset/icon_128x128.png
|
|
cp Logo/256.png Cleanuparr.iconset/icon_128x128@2x.png
|
|
cp Logo/256.png Cleanuparr.iconset/icon_256x256.png
|
|
cp Logo/512.png Cleanuparr.iconset/icon_256x256@2x.png
|
|
cp Logo/512.png Cleanuparr.iconset/icon_512x512.png
|
|
cp Logo/1024.png Cleanuparr.iconset/icon_512x512@2x.png
|
|
|
|
# Create ICNS file
|
|
iconutil -c icns Cleanuparr.iconset -o dist/Cleanuparr.app/Contents/Resources/Cleanuparr.icns
|
|
|
|
# Clean up iconset directory
|
|
rm -rf Cleanuparr.iconset
|
|
fi
|
|
|
|
# Create Launch Daemon plist
|
|
cat > dist/Cleanuparr.app/Contents/Resources/com.cleanuparr.daemon.plist << EOF
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>Label</key>
|
|
<string>com.cleanuparr.daemon</string>
|
|
<key>ProgramArguments</key>
|
|
<array>
|
|
<string>/Applications/Cleanuparr.app/Contents/MacOS/Cleanuparr</string>
|
|
</array>
|
|
<key>RunAtLoad</key>
|
|
<true/>
|
|
<key>KeepAlive</key>
|
|
<true/>
|
|
<key>StandardOutPath</key>
|
|
<string>/var/log/cleanuparr.log</string>
|
|
<key>StandardErrorPath</key>
|
|
<string>/var/log/cleanuparr.error.log</string>
|
|
<key>WorkingDirectory</key>
|
|
<string>/Applications/Cleanuparr.app/Contents/MacOS</string>
|
|
<key>EnvironmentVariables</key>
|
|
<dict>
|
|
<key>HTTP_PORTS</key>
|
|
<string>11011</string>
|
|
</dict>
|
|
</dict>
|
|
</plist>
|
|
EOF
|
|
|
|
# Create Info.plist with proper configuration
|
|
cat > dist/Cleanuparr.app/Contents/Info.plist << EOF
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>CFBundleExecutable</key>
|
|
<string>Cleanuparr</string>
|
|
<key>CFBundleIdentifier</key>
|
|
<string>com.Cleanuparr</string>
|
|
<key>CFBundleName</key>
|
|
<string>Cleanuparr</string>
|
|
<key>CFBundleDisplayName</key>
|
|
<string>Cleanuparr</string>
|
|
<key>CFBundleVersion</key>
|
|
<string>${{ env.appVersion }}</string>
|
|
<key>CFBundleShortVersionString</key>
|
|
<string>${{ env.appVersion }}</string>
|
|
<key>CFBundleInfoDictionaryVersion</key>
|
|
<string>6.0</string>
|
|
<key>CFBundlePackageType</key>
|
|
<string>APPL</string>
|
|
<key>CFBundleSignature</key>
|
|
<string>CLNR</string>
|
|
<key>CFBundleIconFile</key>
|
|
<string>Cleanuparr</string>
|
|
<key>NSHighResolutionCapable</key>
|
|
<true/>
|
|
<key>NSRequiresAquaSystemAppearance</key>
|
|
<false/>
|
|
<key>LSMinimumSystemVersion</key>
|
|
<string>${{ matrix.min_os_version }}</string>
|
|
<key>LSApplicationCategoryType</key>
|
|
<string>public.app-category.productivity</string>
|
|
<key>NSSupportsAutomaticTermination</key>
|
|
<false/>
|
|
<key>NSSupportsSuddenTermination</key>
|
|
<false/>
|
|
<key>LSBackgroundOnly</key>
|
|
<false/>
|
|
<key>NSAppTransportSecurity</key>
|
|
<dict>
|
|
<key>NSAllowsArbitraryLoads</key>
|
|
<true/>
|
|
</dict>
|
|
</dict>
|
|
</plist>
|
|
EOF
|
|
|
|
# Clean up temp directory
|
|
rm -rf dist/temp
|
|
|
|
- name: Create PKG installer
|
|
run: |
|
|
# Create preinstall script to handle existing installations
|
|
mkdir -p scripts
|
|
cat > scripts/preinstall << 'EOF'
|
|
#!/bin/bash
|
|
|
|
# Stop and unload existing launch daemon if it exists
|
|
if launchctl list | grep -q "com.cleanuparr.daemon"; then
|
|
launchctl stop com.cleanuparr.daemon 2>/dev/null || true
|
|
launchctl unload /Library/LaunchDaemons/com.cleanuparr.daemon.plist 2>/dev/null || true
|
|
fi
|
|
|
|
# Stop any running instances of Cleanuparr
|
|
pkill -f "Cleanuparr" || true
|
|
sleep 2
|
|
|
|
# Remove old installation if it exists
|
|
if [[ -d "/Applications/Cleanuparr.app" ]]; then
|
|
rm -rf "/Applications/Cleanuparr.app"
|
|
fi
|
|
|
|
# Remove old launch daemon plist if it exists
|
|
if [[ -f "/Library/LaunchDaemons/com.cleanuparr.daemon.plist" ]]; then
|
|
rm -f "/Library/LaunchDaemons/com.cleanuparr.daemon.plist"
|
|
fi
|
|
|
|
exit 0
|
|
EOF
|
|
|
|
chmod +x scripts/preinstall
|
|
|
|
# Create postinstall script
|
|
cat > scripts/postinstall << 'EOF'
|
|
#!/bin/bash
|
|
|
|
# Set proper permissions for the app bundle
|
|
chmod -R 755 /Applications/Cleanuparr.app
|
|
chmod +x /Applications/Cleanuparr.app/Contents/MacOS/Cleanuparr
|
|
|
|
# Install the launch daemon
|
|
cp /Applications/Cleanuparr.app/Contents/Resources/com.cleanuparr.daemon.plist /Library/LaunchDaemons/
|
|
chown root:wheel /Library/LaunchDaemons/com.cleanuparr.daemon.plist
|
|
chmod 644 /Library/LaunchDaemons/com.cleanuparr.daemon.plist
|
|
|
|
# Load and start the service
|
|
launchctl load /Library/LaunchDaemons/com.cleanuparr.daemon.plist
|
|
launchctl start com.cleanuparr.daemon
|
|
|
|
# Wait a moment for service to start
|
|
sleep 3
|
|
|
|
# Display as system notification
|
|
osascript -e 'display notification "Cleanuparr service started! Visit http://localhost:11011 in your browser." with title "Installation Complete"' 2>/dev/null || true
|
|
|
|
exit 0
|
|
EOF
|
|
|
|
chmod +x scripts/postinstall
|
|
|
|
# Create uninstall script (optional, for user reference)
|
|
cat > scripts/uninstall_cleanuparr.sh << 'EOF'
|
|
#!/bin/bash
|
|
# Cleanuparr Uninstall Script
|
|
# Run this script with sudo to completely remove Cleanuparr
|
|
|
|
echo "Stopping Cleanuparr service..."
|
|
launchctl stop com.cleanuparr.daemon 2>/dev/null || true
|
|
launchctl unload /Library/LaunchDaemons/com.cleanuparr.daemon.plist 2>/dev/null || true
|
|
|
|
echo "Removing service files..."
|
|
rm -f /Library/LaunchDaemons/com.cleanuparr.daemon.plist
|
|
|
|
echo "Removing application..."
|
|
rm -rf /Applications/Cleanuparr.app
|
|
|
|
echo "Removing logs..."
|
|
rm -f /var/log/cleanuparr.log
|
|
rm -f /var/log/cleanuparr.error.log
|
|
|
|
echo "Cleanuparr has been completely removed."
|
|
echo "Note: Configuration files in /Applications/Cleanuparr.app/Contents/MacOS/config/ have been removed with the app."
|
|
EOF
|
|
|
|
chmod +x scripts/uninstall_cleanuparr.sh
|
|
|
|
# Copy uninstall script to app bundle for user access
|
|
cp scripts/uninstall_cleanuparr.sh dist/Cleanuparr.app/Contents/Resources/
|
|
|
|
# Determine package name - if app_version input was provided, it's a release build
|
|
if [[ -n "${{ inputs.app_version }}" ]] || [[ "${{ github.ref }}" =~ ^refs/tags/ ]]; then
|
|
pkg_name="Cleanuparr-${{ env.appVersion }}-macos-${{ matrix.artifact_suffix }}.pkg"
|
|
else
|
|
pkg_name="Cleanuparr-${{ env.appVersion }}-macos-${{ matrix.artifact_suffix }}-dev.pkg"
|
|
fi
|
|
|
|
# Create PKG installer with better metadata
|
|
pkgbuild --root dist/ \
|
|
--scripts scripts/ \
|
|
--identifier com.Cleanuparr \
|
|
--version ${{ env.appVersion }} \
|
|
--install-location /Applications \
|
|
--ownership preserve \
|
|
${pkg_name}
|
|
|
|
echo "pkgName=${pkg_name}" >> $GITHUB_ENV
|
|
|
|
- name: Upload installer as artifact
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: Cleanuparr-macos-${{ matrix.artifact_suffix }}-installer
|
|
path: '${{ env.pkgName }}'
|
|
retention-days: 30
|