name: Release on: push: tags: - 'v*' workflow_dispatch: inputs: tag: description: 'Release tag' required: true type: string is_prerelease: description: 'Pre-release' required: false type: boolean default: false env: DOTNET_VERSION: '10.0.x' Solution_Name: LanMountainDesktop.sln jobs: prepare: runs-on: ubuntu-latest outputs: version: ${{ steps.version.outputs.version }} tag: ${{ steps.version.outputs.tag }} steps: - name: Get release info id: version run: | if [[ "${{ github.event_name }}" == "push" ]]; then TAG=${GITHUB_REF#refs/tags/} else TAG=${{ github.event.inputs.tag }} fi VERSION=${TAG#v} echo "tag=${TAG}" >> $GITHUB_OUTPUT echo "version=${VERSION}" >> $GITHUB_OUTPUT build-windows: needs: prepare runs-on: windows-latest strategy: matrix: arch: [x64, x86] name: Build_Windows_${{ matrix.arch }} steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 submodules: recursive ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref }} - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} - name: Update version in .csproj run: | $VERSION = "${{ needs.prepare.outputs.version }}" $csprojFiles = @( "LanMountainDesktop/LanMountainDesktop.csproj" ) foreach ($csprojPath in $csprojFiles) { Write-Host "Updating version in $csprojPath to $VERSION" $content = Get-Content $csprojPath -Raw $content = $content -replace '.*?', "$VERSION" Set-Content $csprojPath $content } shell: pwsh - name: Restore run: dotnet restore ${{ env.Solution_Name }} - name: Build run: dotnet build ${{ env.Solution_Name }} -c Release --no-restore -v minimal - name: Publish run: | dotnet publish LanMountainDesktop/LanMountainDesktop.csproj ` -c Release ` -o ./publish/windows-${{ matrix.arch }} ` --self-contained ` -r win-${{ matrix.arch }} ` -p:PublishSingleFile=true ` -p:SelfContained=true ` -p:DebugType=none ` -p:DebugSymbols=false ` -p:PublishTrimmed=true ` -p:TrimMode=partial ` -p:PublishReadyToRun=true shell: pwsh - name: Install Inno Setup run: choco install innosetup -y --no-progress shell: pwsh - name: Build Installer run: | $version = "${{ needs.prepare.outputs.version }}" $arch = "${{ matrix.arch }}" $publishDir = "publish\windows-$arch" $installerScript = "LanMountainDesktop\installer\LanMountainDesktop.iss" $outputDir = "build-installer" # Verify source directory exists if (-not (Test-Path -Path $publishDir)) { Write-Error "Publish directory not found: $publishDir" Get-ChildItem -Path "publish" -Directory -ErrorAction SilentlyContinue | Select-Object Name exit 1 } # Create output directory New-Item -ItemType Directory -Path $outputDir -Force | Out-Null # Verify installer script exists if (-not (Test-Path -Path $installerScript)) { Write-Error "Installer script not found: $installerScript" exit 1 } # Find Inno Setup compiler $isccPath = "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" if (-not (Test-Path -Path $isccPath)) { $isccPath = "C:\Program Files\Inno Setup 6\ISCC.exe" } if (-not (Test-Path -Path $isccPath)) { Write-Error "Inno Setup compiler not found at: $isccPath" exit 1 } Write-Host "Found Inno Setup at: $isccPath" # Build installer with iscc.exe Write-Host "Building installer for Windows $arch with version $version..." $compileCmd = @( "`"$isccPath`"", "/DMyAppVersion=$version", "/DPublishDir=..\$publishDir", "/DMyOutputDir=..\$outputDir", "/DMyAppArch=$arch", "`"$installerScript`"" ) -join " " Write-Host "Compile command: $compileCmd" # Execute the compiler $output = Invoke-Expression $compileCmd 2>&1 Write-Host $output # Check if build was successful $installerFile = Get-ChildItem -Path $outputDir -Filter "*.exe" -ErrorAction SilentlyContinue | Select-Object -First 1 if (-not $installerFile) { Write-Error "Failed to create installer" exit 1 } Write-Host "✅ Successfully created: $($installerFile.Name)" Write-Host "Installer size: $([Math]::Round($installerFile.Length / 1MB, 2)) MB" shell: pwsh - name: Upload Installer uses: actions/upload-artifact@v4 with: name: release-windows-${{ matrix.arch }} path: build-installer/*.exe retention-days: 30 build-linux: needs: prepare runs-on: ubuntu-latest name: Build_Linux steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 submodules: recursive ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref }} - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y \ libfontconfig1 libfreetype6 \ libx11-6 libxrandr2 libxinerama1 \ libxi6 libxcursor1 libxext6 \ libxrender1 libxkbcommon-x11-0 - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} - name: Update version in .csproj run: | VERSION="${{ needs.prepare.outputs.version }}" echo "Updating version in LanMountainDesktop.csproj to $VERSION" sed -i "s/.*<\/Version>/$VERSION<\/Version>/" LanMountainDesktop/LanMountainDesktop.csproj - name: Restore run: dotnet restore ${{ env.Solution_Name }} - name: Build run: dotnet build ${{ env.Solution_Name }} -c Release --no-restore -v minimal - name: Publish run: | dotnet publish LanMountainDesktop/LanMountainDesktop.csproj \ -c Release \ -o ./publish/linux-x64 \ --self-contained \ -r linux-x64 \ -p:PublishSingleFile=true \ -p:SelfContained=true \ -p:DebugType=none \ -p:DebugSymbols=false \ -p:PublishTrimmed=true \ -p:TrimMode=partial \ -p:PublishReadyToRun=true - name: Package as DEB run: | version="${{ needs.prepare.outputs.version }}" source="publish/linux-x64" package_name="LanMountainDesktop" package_version="${version}" arch="amd64" # Verify source directory exists if [ ! -d "$source" ]; then echo "Error: Source directory not found: $source" ls -la publish/ || echo "publish directory not found" exit 1 fi # Create DEB package structure mkdir -p "build-deb/DEBIAN" mkdir -p "build-deb/usr/local/bin" mkdir -p "build-deb/usr/share/applications" mkdir -p "build-deb/usr/share/pixmaps" # Copy application files cp -r "$source"/* "build-deb/usr/local/bin/" # Verify copy was successful item_count=$(find build-deb/usr/local/bin -type f 2>/dev/null | wc -l) echo "DEB package contains $item_count files" if [ "$item_count" -eq 0 ]; then echo "Error: DEB package is empty after copy" exit 1 fi # Create control file (NOTE: No leading spaces in control file) cat > "build-deb/DEBIAN/control" << EOF Package: $package_name Version: $package_version Architecture: $arch Maintainer: LanMountain Team Description: LanMountain Desktop Application A desktop application for LanMountain. EOF # Set proper permissions chmod 755 "build-deb/usr/local/bin/LanMountainDesktop" || chmod 755 "build-deb/usr/local/bin"/* # Create DEB file if dpkg-deb --build "build-deb" "${package_name}_${package_version}_${arch}.deb"; then echo "Successfully created: ${package_name}_${package_version}_${arch}.deb" ls -lh "${package_name}_${package_version}_${arch}.deb" else echo "Error: Failed to build DEB package" exit 1 fi - name: Upload uses: actions/upload-artifact@v4 with: name: release-linux path: "*.deb" retention-days: 30 build-macos: needs: prepare runs-on: macos-latest strategy: matrix: arch: [x64, arm64] name: Build_macOS_${{ matrix.arch }} steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 submodules: recursive ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref }} - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ env.DOTNET_VERSION }} - name: Update version in .csproj run: | VERSION="${{ needs.prepare.outputs.version }}" echo "Updating version in LanMountainDesktop.csproj to $VERSION" sed -i '' "s/.*<\/Version>/$VERSION<\/Version>/" LanMountainDesktop/LanMountainDesktop.csproj - name: Restore run: dotnet restore ${{ env.Solution_Name }} - name: Build run: dotnet build ${{ env.Solution_Name }} -c Release --no-restore -v minimal - name: Publish run: | dotnet publish LanMountainDesktop/LanMountainDesktop.csproj \ -c Release \ -o ./publish/macos-${{ matrix.arch }} \ --self-contained \ -r osx-${{ matrix.arch }} \ -p:PublishSingleFile=true \ -p:SelfContained=true \ -p:DebugType=none \ -p:DebugSymbols=false \ -p:PublishTrimmed=true \ -p:TrimMode=partial \ -p:PublishReadyToRun=true - name: Package as DMG run: | version="${{ needs.prepare.outputs.version }}" arch="${{ matrix.arch }}" source="publish/macos-$arch" app_name="LanMountainDesktop" package_name="${app_name}-${version}-macos-${arch}" # Verify source directory exists if [ ! -d "$source" ]; then echo "Error: Source directory not found: $source" ls -la publish/ || echo "publish directory not found" exit 1 fi # Create app bundle structure mkdir -p "${app_name}.app/Contents/MacOS" mkdir -p "${app_name}.app/Contents/Resources" # Copy application files cp -r "$source"/* "${app_name}.app/Contents/MacOS/" # Verify copy was successful item_count=$(find "${app_name}.app/Contents/MacOS" -type f | wc -l) echo "App bundle contains $item_count files" if [ "$item_count" -eq 0 ]; then echo "Error: App bundle is empty after copy" exit 1 fi # Create Info.plist - NOTE: Using unquoted EOF to allow variable expansion cat > "${app_name}.app/Contents/Info.plist" << EOF CFBundleExecutable LanMountainDesktop CFBundleName LanMountain Desktop CFBundleVersion $version CFBundleShortVersionString $version CFBundleIdentifier com.lanmountain.desktop CFBundlePackageType APPL EOF # Create DMG mkdir -p dmg-temp cp -r "${app_name}.app" dmg-temp/ if hdiutil create -volname "${app_name}" -srcfolder dmg-temp -ov -format UDZO "${package_name}.dmg" 2>&1; then echo "Successfully created: ${package_name}.dmg" ls -lh "${package_name}.dmg" else echo "Error: Failed to create DMG" exit 1 fi # Cleanup rm -rf dmg-temp "${app_name}.app" - name: Upload uses: actions/upload-artifact@v4 with: name: release-macos-${{ matrix.arch }} path: "*.dmg" retention-days: 30 github-release: needs: [ prepare, build-windows, build-linux, build-macos ] runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/') permissions: contents: write steps: - name: Download artifacts uses: actions/download-artifact@v4 with: path: artifacts pattern: release-* - name: Create Release uses: ncipollo/release-action@v1 with: tag: ${{ github.ref_name }} draft: false prerelease: ${{ github.event.inputs.is_prerelease == 'true' }} artifacts: artifacts/**/* body: | ## Release ${{ needs.prepare.outputs.version }} ### Windows - **LanMountainDesktop-Setup-{version}-x64.exe** - 64-bit installer - **LanMountainDesktop-Setup-{version}-x86.exe** - 32-bit installer Installation: Double-click the .exe file and follow the wizard. ### Linux - **LanMountainDesktop-{version}-linux-x64.deb** - Debian package (x64) ### macOS - **LanMountainDesktop-{version}-macos-x64.dmg** - Intel processor - **LanMountainDesktop-{version}-macos-arm64.dmg** - Apple Silicon (M1/M2/M3) See commits for changes. token: ${{ secrets.GITHUB_TOKEN }}