feat: add multi-platform packaging and auto-update infrastructure
- Add PyInstaller configuration (clide.spec) for cross-platform builds - Add build scripts for macOS (DMG), Linux (AppImage), and Windows (Inno Setup) - Add Gitea Actions CI/CD workflow for automated releases - Add auto-update service with Gitea API integration - Add 'clide update' CLI command for checking and installing updates Packaging outputs: - macOS: Signed/notarized .app bundle in DMG - Linux: Portable AppImage (glibc 2.17+) - Windows: Inno Setup installer Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
8b1a84e7e2
commit
aff03a2e9b
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/bin/bash
|
||||
# Build script for Linux AppImage distribution
|
||||
# Creates a portable AppImage that runs on most Linux distributions
|
||||
#
|
||||
# Prerequisites:
|
||||
# - wget (for downloading appimagetool)
|
||||
# - FUSE (for running appimagetool)
|
||||
#
|
||||
# Usage: ./scripts/build-linux.sh [VERSION]
|
||||
|
||||
set -e
|
||||
|
||||
VERSION="${1:-1.0.0}"
|
||||
ARCH=$(uname -m)
|
||||
|
||||
echo "========================================"
|
||||
echo "Building Clide $VERSION for Linux ($ARCH)"
|
||||
echo "========================================"
|
||||
|
||||
# Ensure we're in the project root
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# Build with PyInstaller
|
||||
echo "Building with PyInstaller..."
|
||||
pyinstaller clide.spec --clean --noconfirm
|
||||
|
||||
# Create AppDir structure
|
||||
echo "Creating AppDir structure..."
|
||||
rm -rf dist/Clide.AppDir
|
||||
mkdir -p dist/Clide.AppDir/usr/bin
|
||||
mkdir -p dist/Clide.AppDir/usr/share/applications
|
||||
mkdir -p dist/Clide.AppDir/usr/share/icons/hicolor/256x256/apps
|
||||
|
||||
# Copy binary
|
||||
cp dist/clide dist/Clide.AppDir/usr/bin/
|
||||
|
||||
# Copy desktop file
|
||||
cp packaging/clide.desktop dist/Clide.AppDir/
|
||||
cp packaging/clide.desktop dist/Clide.AppDir/usr/share/applications/
|
||||
|
||||
# Create placeholder icon (TODO: Replace with actual icon)
|
||||
# For now, create a simple placeholder
|
||||
if [ ! -f packaging/clide.png ]; then
|
||||
echo "Note: No icon found at packaging/clide.png, AppImage will have no icon"
|
||||
else
|
||||
cp packaging/clide.png dist/Clide.AppDir/clide.png
|
||||
cp packaging/clide.png dist/Clide.AppDir/usr/share/icons/hicolor/256x256/apps/clide.png
|
||||
fi
|
||||
|
||||
# Create AppRun launcher
|
||||
cat > dist/Clide.AppDir/AppRun << 'EOF'
|
||||
#!/bin/bash
|
||||
# AppRun - Entry point for AppImage
|
||||
SELF=$(readlink -f "$0")
|
||||
HERE=${SELF%/*}
|
||||
export PATH="${HERE}/usr/bin:${PATH}"
|
||||
export TERM="${TERM:-xterm-256color}"
|
||||
exec "${HERE}/usr/bin/clide" "$@"
|
||||
EOF
|
||||
chmod +x dist/Clide.AppDir/AppRun
|
||||
|
||||
# Download appimagetool if needed
|
||||
APPIMAGETOOL="appimagetool-${ARCH}.AppImage"
|
||||
if [ ! -f "$APPIMAGETOOL" ]; then
|
||||
echo "Downloading appimagetool..."
|
||||
wget -q "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-${ARCH}.AppImage" \
|
||||
-O "$APPIMAGETOOL"
|
||||
chmod +x "$APPIMAGETOOL"
|
||||
fi
|
||||
|
||||
# Create AppImage
|
||||
echo "Creating AppImage..."
|
||||
rm -f "dist/Clide-$VERSION-linux-$ARCH.AppImage"
|
||||
ARCH=$ARCH ./"$APPIMAGETOOL" dist/Clide.AppDir "dist/Clide-$VERSION-linux-$ARCH.AppImage"
|
||||
|
||||
# Clean up AppDir
|
||||
rm -rf dist/Clide.AppDir
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "Build complete!"
|
||||
echo "Output: dist/Clide-$VERSION-linux-$ARCH.AppImage"
|
||||
echo ""
|
||||
echo "To run: chmod +x dist/Clide-$VERSION-linux-$ARCH.AppImage && ./dist/Clide-$VERSION-linux-$ARCH.AppImage"
|
||||
echo "========================================"
|
||||
Executable
+101
@@ -0,0 +1,101 @@
|
||||
#!/bin/bash
|
||||
# Build script for macOS distribution
|
||||
# Creates a signed and notarized DMG installer
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Xcode Command Line Tools: xcode-select --install
|
||||
# - create-dmg: brew install create-dmg
|
||||
# - Apple Developer ID certificate installed in Keychain
|
||||
# - Keychain profile for notarytool: xcrun notarytool store-credentials
|
||||
#
|
||||
# Usage: ./scripts/build-macos.sh [VERSION]
|
||||
|
||||
set -e
|
||||
|
||||
VERSION="${1:-1.0.0}"
|
||||
DEVELOPER_ID="${DEVELOPER_ID:-Developer ID Application: Your Name (TEAMID)}"
|
||||
KEYCHAIN_PROFILE="${KEYCHAIN_PROFILE:-AC_PASSWORD}"
|
||||
|
||||
echo "========================================"
|
||||
echo "Building Clide $VERSION for macOS"
|
||||
echo "========================================"
|
||||
|
||||
# Ensure we're in the project root
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# Check for required tools
|
||||
if ! command -v create-dmg &> /dev/null; then
|
||||
echo "Error: create-dmg not found. Install with: brew install create-dmg"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build with PyInstaller
|
||||
echo "Building with PyInstaller..."
|
||||
pyinstaller clide.spec --clean --noconfirm
|
||||
|
||||
# Check if signing is configured
|
||||
if [[ "$DEVELOPER_ID" == *"Your Name"* ]]; then
|
||||
echo ""
|
||||
echo "Warning: DEVELOPER_ID not configured. Skipping code signing."
|
||||
echo "To enable signing, set DEVELOPER_ID environment variable."
|
||||
echo ""
|
||||
SKIP_SIGNING=true
|
||||
else
|
||||
SKIP_SIGNING=false
|
||||
fi
|
||||
|
||||
if [[ "$SKIP_SIGNING" == "false" ]]; then
|
||||
# Code sign the app
|
||||
echo "Code signing..."
|
||||
codesign --deep --force --verify --verbose \
|
||||
--sign "$DEVELOPER_ID" \
|
||||
--options runtime \
|
||||
--entitlements packaging/entitlements.plist \
|
||||
--timestamp \
|
||||
"dist/Clide.app"
|
||||
|
||||
# Create ZIP for notarization
|
||||
echo "Creating ZIP for notarization..."
|
||||
ditto -c -k --sequesterRsrc --keepParent \
|
||||
"dist/Clide.app" "dist/Clide-$VERSION.zip"
|
||||
|
||||
# Notarize
|
||||
echo "Submitting for notarization (this may take a few minutes)..."
|
||||
xcrun notarytool submit "dist/Clide-$VERSION.zip" \
|
||||
--keychain-profile "$KEYCHAIN_PROFILE" \
|
||||
--wait
|
||||
|
||||
# Staple the notarization ticket
|
||||
echo "Stapling notarization ticket..."
|
||||
xcrun stapler staple "dist/Clide.app"
|
||||
|
||||
# Clean up ZIP
|
||||
rm "dist/Clide-$VERSION.zip"
|
||||
fi
|
||||
|
||||
# Create DMG
|
||||
echo "Creating DMG..."
|
||||
# Remove existing DMG if present
|
||||
rm -f "dist/Clide-$VERSION-macos.dmg"
|
||||
|
||||
create-dmg \
|
||||
--volname "Clide $VERSION" \
|
||||
--window-pos 200 120 \
|
||||
--window-size 600 400 \
|
||||
--icon-size 100 \
|
||||
--icon "Clide.app" 150 190 \
|
||||
--hide-extension "Clide.app" \
|
||||
--app-drop-link 450 185 \
|
||||
"dist/Clide-$VERSION-macos.dmg" \
|
||||
"dist/Clide.app" || true # create-dmg returns non-zero even on success sometimes
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "Build complete!"
|
||||
echo "Output: dist/Clide-$VERSION-macos.dmg"
|
||||
if [[ "$SKIP_SIGNING" == "true" ]]; then
|
||||
echo ""
|
||||
echo "Note: App was NOT signed or notarized."
|
||||
echo "Users will see Gatekeeper warnings."
|
||||
fi
|
||||
echo "========================================"
|
||||
@@ -0,0 +1,102 @@
|
||||
# Build script for Windows installer distribution
|
||||
# Creates an installer using Inno Setup
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Inno Setup 6: choco install innosetup -y
|
||||
# Or download from: https://jrsoftware.org/isinfo.php
|
||||
#
|
||||
# Usage: .\scripts\build-windows.ps1 [-Version "1.0.0"]
|
||||
|
||||
param(
|
||||
[string]$Version = "1.0.0"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
Write-Host "========================================"
|
||||
Write-Host "Building Clide $Version for Windows"
|
||||
Write-Host "========================================"
|
||||
|
||||
# Ensure we're in the project root
|
||||
$ProjectRoot = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
|
||||
Set-Location $ProjectRoot
|
||||
|
||||
# Build with PyInstaller
|
||||
Write-Host "Building with PyInstaller..."
|
||||
pyinstaller clide.spec --clean --noconfirm
|
||||
|
||||
# Check for Inno Setup
|
||||
$InnoSetup = "C:\Program Files (x86)\Inno Setup 6\ISCC.exe"
|
||||
if (-not (Test-Path $InnoSetup)) {
|
||||
Write-Host ""
|
||||
Write-Host "Warning: Inno Setup not found at $InnoSetup"
|
||||
Write-Host "Install with: choco install innosetup -y"
|
||||
Write-Host "Or download from: https://jrsoftware.org/isinfo.php"
|
||||
Write-Host ""
|
||||
Write-Host "Skipping installer creation. Binary available at: dist\clide.exe"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Create Inno Setup script
|
||||
Write-Host "Creating Inno Setup script..."
|
||||
$InnoScript = @"
|
||||
; Clide Installer Script
|
||||
; Generated by build-windows.ps1
|
||||
|
||||
[Setup]
|
||||
AppName=Clide
|
||||
AppVersion=$Version
|
||||
AppPublisher=Clide
|
||||
AppPublisherURL=https://github.com/your-repo/clide
|
||||
AppSupportURL=https://github.com/your-repo/clide/issues
|
||||
DefaultDirName={autopf}\Clide
|
||||
DefaultGroupName=Clide
|
||||
AllowNoIcons=yes
|
||||
OutputDir=dist
|
||||
OutputBaseFilename=Clide-$Version-windows-setup
|
||||
Compression=lzma2
|
||||
SolidCompression=yes
|
||||
WizardStyle=modern
|
||||
PrivilegesRequired=lowest
|
||||
PrivilegesRequiredOverridesAllowed=dialog
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
||||
|
||||
[Files]
|
||||
Source: "dist\clide.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\Clide"; Filename: "{app}\clide.exe"
|
||||
Name: "{group}\{cm:UninstallProgram,Clide}"; Filename: "{uninstallexe}"
|
||||
Name: "{autodesktop}\Clide"; Filename: "{app}\clide.exe"; Tasks: desktopicon
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\clide.exe"; Description: "{cm:LaunchProgram,Clide}"; Flags: nowait postinstall skipifsilent shellexec
|
||||
|
||||
[Code]
|
||||
function InitializeSetup(): Boolean;
|
||||
begin
|
||||
Result := True;
|
||||
// Add any initialization checks here
|
||||
end;
|
||||
"@
|
||||
|
||||
# Write Inno Setup script
|
||||
$InnoScriptPath = Join-Path $ProjectRoot "build\clide.iss"
|
||||
$InnoScript | Out-File -FilePath $InnoScriptPath -Encoding UTF8
|
||||
|
||||
# Build installer
|
||||
Write-Host "Building installer with Inno Setup..."
|
||||
& $InnoSetup $InnoScriptPath
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "========================================"
|
||||
Write-Host "Build complete!"
|
||||
Write-Host "Output: dist\Clide-$Version-windows-setup.exe"
|
||||
Write-Host ""
|
||||
Write-Host "Note: For best experience, users should run Clide in Windows Terminal."
|
||||
Write-Host "========================================"
|
||||
Reference in New Issue
Block a user