From b5c628744224bc8ef76edf075afae3fa87820970 Mon Sep 17 00:00:00 2001 From: Max Litruv Boonzaayer Date: Tue, 7 Apr 2026 15:23:06 +1000 Subject: [PATCH] ci: remove QEMU VM workflow, add Android APK build job --- .gitea/workflows/build-windows.yml | 365 ----------------------------- .gitea/workflows/build.yml | 170 ++++++++------ 2 files changed, 96 insertions(+), 439 deletions(-) delete mode 100644 .gitea/workflows/build-windows.yml diff --git a/.gitea/workflows/build-windows.yml b/.gitea/workflows/build-windows.yml deleted file mode 100644 index ff48dc6..0000000 --- a/.gitea/workflows/build-windows.yml +++ /dev/null @@ -1,365 +0,0 @@ -name: Build Paarrot Windows - -on: - push: - branches: [ main, development ] - pull_request: - branches: [ main ] - -concurrency: - group: build-${{ github.ref }} - cancel-in-progress: true - -jobs: - start-vm: - runs-on: ubuntu-latest - timeout-minutes: 10 - - steps: - - name: Check if VM is already running - id: check-vm - run: | - if pgrep -f "qemu-system-x86_64.*windows.qcow2" > /dev/null; then - echo "VM is already running" - echo "vm_running=true" >> $GITHUB_OUTPUT - else - echo "VM is not running" - echo "vm_running=false" >> $GITHUB_OUTPUT - fi - - - name: Start Windows VM with retry - run: | - MAX_RETRIES=3 - RETRY_COUNT=0 - BOOT_WAIT=90 - RUNNER_CHECK_TIMEOUT=120 - - start_vm() { - echo "Starting Windows QEMU VM (attempt $((RETRY_COUNT + 1))/$MAX_RETRIES)..." - sudo -u litruv setsid bash -c 'DISPLAY=:0 XAUTHORITY=/home/litruv/.Xauthority qemu-system-x86_64 \ - -m 18G \ - -smp 4 \ - -enable-kvm \ - -cpu host \ - -drive file=/home/litruv/windows.qcow2,format=qcow2 \ - -boot d \ - -vga virtio \ - -display vnc=:0 \ - > /tmp/qemu.log 2>&1' & - disown - } - - kill_vm() { - echo "Killing VM..." - sudo pkill -9 qemu-system-x86 || true - sleep 5 - } - - check_runner() { - API_URL="${{ github.server_url }}/api/v1/repos/${{ github.repository }}/actions/runners" - RESPONSE=$(curl -s -H "Authorization: token ${{ secrets.CI_BOT_TOKEN }}" "$API_URL" 2>/dev/null) - - if [ -z "$RESPONSE" ]; then - echo " Could not query runner API" - return 1 - fi - - ONLINE=$(echo "$RESPONSE" | jq -r '.runners[] | select(.labels[].name == "windows-vm") | select(.status == "online") | .name' 2>/dev/null) - - if [ -n "$ONLINE" ]; then - echo " Runner found online!" - return 0 - fi - - echo " Windows runner (windows-vm) is NOT online" - return 1 - } - - wait_for_runner() { - local max_checks=$1 - local required_successes=${2:-1} - local successes=0 - echo "Waiting for runner to come online (need ${required_successes} successful checks, max ${max_checks} attempts)..." - for i in $(seq 1 $max_checks); do - if check_runner; then - successes=$((successes + 1)) - echo "Runner online check $successes/$required_successes passed ($i/$max_checks)" - if [ $successes -ge $required_successes ]; then - echo "Runner confirmed online after $successes consecutive checks!" - return 0 - fi - else - successes=0 - echo "Waiting for runner... ($i/$max_checks)" - fi - sleep 5 - done - return 1 - } - - echo "Checking if runner is online..." - if wait_for_runner 12 12; then - echo "Runner verified online!" - exit 0 - fi - - if [ "${{ steps.check-vm.outputs.vm_running }}" == "true" ]; then - echo "VM process exists but runner not responding, restarting VM..." - kill_vm - fi - - while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do - start_vm - echo "Waiting ${BOOT_WAIT}s for VM to boot..." - sleep $BOOT_WAIT - - if ! pgrep -f "qemu-system-x86_64.*windows.qcow2" > /dev/null; then - echo "VM process not found, retrying..." - RETRY_COUNT=$((RETRY_COUNT + 1)) - continue - fi - - if wait_for_runner $((RUNNER_CHECK_TIMEOUT / 5)); then - exit 0 - fi - - echo "Runner did not come online, killing VM and retrying..." - kill_vm - RETRY_COUNT=$((RETRY_COUNT + 1)) - done - - echo "ERROR: Failed to start VM after $MAX_RETRIES attempts" - exit 1 - - - name: VM Status - run: | - if [ "${{ steps.check-vm.outputs.vm_running }}" == "true" ]; then - echo "Using existing VM" - else - echo "Started new VM" - fi - - if pgrep -f "qemu-system-x86_64.*windows.qcow2" > /dev/null; then - echo "VM process confirmed running" - else - echo "ERROR: VM process not found!" - exit 1 - fi - - build: - runs-on: windows-vm - needs: start-vm - timeout-minutes: 60 - - steps: - - name: Cancel pending shutdown - shell: powershell - run: | - shutdown /s /t 1000 2>$null - shutdown /a 2>$null - Write-Host "Cancelled any pending shutdown" - - - name: Disable sleep and power saving - shell: powershell - run: | - powercfg /change standby-timeout-ac 0 - powercfg /change standby-timeout-dc 0 - powercfg /change hibernate-timeout-ac 0 - powercfg /change hibernate-timeout-dc 0 - powercfg /change monitor-timeout-ac 0 - powercfg /change monitor-timeout-dc 0 - powercfg /setactive 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c - Write-Host "Power saving disabled for build" - - - name: Fast checkout (persistent repo) - shell: powershell - run: | - $ErrorActionPreference = "Continue" - - $repoDir = "C:\BuildCache\cinny-desktop\Repo" - $serverUrl = "${{ github.server_url }}" - $repo = "${{ github.repository }}" - $branch = "${{ github.ref_name }}" - $token = "${{ secrets.CI_BOT_TOKEN }}" - - $uri = [System.Uri]$serverUrl - $scheme = $uri.Scheme - $authority = $uri.Authority - $repoUrl = "${scheme}://${authority}/${repo}" - - Write-Host "Using server: ${scheme}://${authority}" - - if (-not (Test-Path "C:\BuildCache\cinny-desktop")) { - New-Item -ItemType Directory -Path "C:\BuildCache\cinny-desktop" -Force - } - - git config --global credential.helper "" - git config --global "http.${scheme}://${authority}/.extraheader" "Authorization: token $token" - - function Remove-GitLocks { - $lockFiles = @( - "$repoDir\.git\shallow.lock", - "$repoDir\.git\index.lock", - "$repoDir\.git\HEAD.lock", - "$repoDir\.git\config.lock" - ) - foreach ($lock in $lockFiles) { - if (Test-Path $lock) { - Write-Host "Removing stale lock: $lock" - Remove-Item -Force $lock - } - } - } - - function Do-Clone { - Write-Host "Cloning repository fresh..." - if (Test-Path $repoDir) { - Remove-Item -Recurse -Force $repoDir - } - - $cloneSuccess = $false - for ($i = 1; $i -le 3; $i++) { - Write-Host "Clone attempt $i/3..." - & git clone --depth=1 --branch $branch $repoUrl $repoDir 2>&1 | Out-Host - if ($LASTEXITCODE -eq 0) { - $cloneSuccess = $true - break - } - Write-Host "Clone attempt $i failed, retrying in 5 seconds..." - if (Test-Path $repoDir) { - Remove-Item -Recurse -Force $repoDir - } - Start-Sleep -Seconds 5 - } - - if (-not $cloneSuccess) { - throw "Clone failed after 3 attempts" - } - - Push-Location $repoDir - & git submodule update --init --recursive --depth=1 2>&1 | Out-Host - Pop-Location - } - - function Do-Update { - Write-Host "Updating existing repo..." - Push-Location $repoDir - - Remove-GitLocks - - git remote set-url origin $repoUrl 2>$null - Write-Host "Remote URL updated" - - git reset --hard HEAD 2>$null - if ($LASTEXITCODE -ne 0) { - Write-Host "Reset failed, repo may be corrupted" - Pop-Location - return $false - } - - git clean -fdx -e node_modules/ -e cinny/node_modules/ 2>$null - - $fetchSuccess = $false - for ($i = 1; $i -le 3; $i++) { - Write-Host "Fetch attempt $i/3..." - & git fetch origin $branch --depth=1 2>&1 | Out-Host - if ($LASTEXITCODE -eq 0) { - $fetchSuccess = $true - break - } - Write-Host "Fetch attempt $i failed, retrying in 5 seconds..." - Start-Sleep -Seconds 5 - } - - if (-not $fetchSuccess) { - Write-Host "All fetch attempts failed" - Pop-Location - return $false - } - - & git checkout -B $branch origin/$branch 2>&1 | Out-Host - if ($LASTEXITCODE -ne 0) { - Write-Host "Checkout failed" - Pop-Location - return $false - } - - & git submodule update --init --recursive --depth=1 2>&1 | Out-Host - - Pop-Location - return $true - } - - if (Test-Path "$repoDir\.git") { - $success = Do-Update - if (-not $success) { - Write-Host "Update failed, will re-clone..." - Do-Clone - } - } else { - Do-Clone - } - - if (-not (Test-Path "$repoDir\package.json")) { - throw "Checkout verification failed - package.json not found" - } - - Write-Host "Checkout complete at: $repoDir" - - - name: Install dependencies - shell: powershell - run: | - $ErrorActionPreference = "Stop" - $repoDir = "C:\BuildCache\cinny-desktop\Repo" - - Push-Location $repoDir - - Write-Host "Installing root dependencies..." - npm ci --prefer-offline --no-audit - - Write-Host "Installing cinny dependencies..." - Push-Location cinny - npm ci --prefer-offline --no-audit - Pop-Location - - Pop-Location - Write-Host "Dependencies installed" - - - name: Build Paarrot - shell: powershell - run: | - $ErrorActionPreference = "Stop" - $repoDir = "C:\BuildCache\cinny-desktop\Repo" - - Push-Location $repoDir - - Write-Host "Building Paarrot..." - npm run build:win - - Pop-Location - Write-Host "Build completed successfully!" - - - name: Upload artifacts - shell: powershell - run: | - $ErrorActionPreference = "Stop" - $repoDir = "C:\BuildCache\cinny-desktop\Repo" - $artifactDir = "$repoDir\dist-electron" - - if (-not (Test-Path $artifactDir)) { - Write-Error "Build artifacts not found at $artifactDir" - exit 1 - } - - Write-Host "Build artifacts:" - Get-ChildItem $artifactDir -Recurse | Select-Object FullName - - # TODO: Copy artifacts to artifact server or release location - Write-Host "Artifacts ready at: $artifactDir" - - - name: Schedule delayed shutdown - if: always() - shell: powershell - run: | - shutdown /s /t 900 /c "Build complete - shutting down in 15 minutes" - Write-Host "VM will shutdown in 15 minutes unless another build starts" diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 05151d9..93e2cf4 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -3,7 +3,7 @@ name: Build on: push: branches: - - main + - master - dev tags: - 'v*' @@ -11,42 +11,34 @@ on: jobs: increment-version: runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, '[skip ci]') + if: github.ref == 'refs/heads/master' && !contains(github.event.head_commit.message, '[skip ci]') outputs: version: ${{ steps.bump.outputs.VERSION }} should_build: ${{ steps.bump.outputs.SHOULD_BUILD }} - + steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - + - name: Bump patch version id: bump run: | - # Get current version from package.json CURRENT=$(grep -Po '"version":\s*"\K[^"]+' package.json | head -1) echo "Current version: $CURRENT" - - # Split and increment patch MAJOR=$(echo $CURRENT | cut -d. -f1) MINOR=$(echo $CURRENT | cut -d. -f2) PATCH=$(echo $CURRENT | cut -d. -f3) NEW_PATCH=$((PATCH + 1)) NEW_VERSION="${MAJOR}.${MINOR}.${NEW_PATCH}" echo "New version: $NEW_VERSION" - - # Update package.json sed -i "s/\"version\": \"${CURRENT}\"/\"version\": \"${NEW_VERSION}\"/" package.json - - # Verify the change grep '"version"' package.json | head -1 - echo "VERSION=$NEW_VERSION" >> $GITHUB_OUTPUT echo "SHOULD_BUILD=true" >> $GITHUB_OUTPUT - + - name: Commit and push version bump run: | git config user.name "GitHub Actions" @@ -59,74 +51,68 @@ jobs: runs-on: windows needs: increment-version if: always() && (needs.increment-version.outputs.should_build == 'true' || needs.increment-version.result == 'skipped') - + steps: - name: Checkout repository uses: actions/checkout@v4 with: submodules: false ref: ${{ github.ref_name }} - + - name: Checkout submodules manually shell: powershell - run: | - git submodule update --init --recursive - + run: git submodule update --init --recursive + - name: Pull latest (after version bump) - if: github.ref == 'refs/heads/main' + if: github.ref == 'refs/heads/master' shell: powershell - run: git pull origin main - + run: git pull origin master + - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' - + - name: Install dependencies (root) run: npm install --prefer-offline - + - name: Install dependencies (cinny) working-directory: ./cinny run: npm install --prefer-offline - + - name: Clean previous builds shell: powershell run: | if (Test-Path cinny/dist) { Remove-Item -Recurse -Force cinny/dist } if (Test-Path dist-electron) { Remove-Item -Recurse -Force dist-electron } - + - name: Build Electron app run: npm run build:win env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - + - name: Upload Squirrel Setup (x64) uses: actions/upload-artifact@v3 with: name: Paarrot-Setup-x64.exe path: dist-electron/squirrel-windows/Paarrot-*-win-x64.exe - - - name: List build output - shell: powershell - run: | - Get-ChildItem -Path dist-electron -Recurse | Select-Object FullName - + - name: Copy RELEASES file for upload shell: powershell run: | if (Test-Path "dist-electron/squirrel-windows/RELEASES") { Copy-Item "dist-electron/squirrel-windows/RELEASES" -Destination "dist-electron/RELEASES" } else { - Write-Error "RELEASES file not found for x64" + Write-Error "RELEASES file not found" exit 1 } - + - name: Upload Squirrel RELEASES uses: actions/upload-artifact@v3 with: name: RELEASES path: dist-electron/RELEASES - + - name: Upload Squirrel nupkg (x64) uses: actions/upload-artifact@v3 with: @@ -137,65 +123,108 @@ jobs: runs-on: ubuntu-latest needs: increment-version if: always() && (needs.increment-version.outputs.should_build == 'true' || needs.increment-version.result == 'skipped') - + steps: - name: Checkout repository uses: actions/checkout@v4 with: submodules: recursive ref: ${{ github.ref_name }} - + - name: Pull latest (after version bump) - if: github.ref == 'refs/heads/main' - run: git pull origin main - + if: github.ref == 'refs/heads/master' + run: git pull origin master + - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' - + - name: Install dependencies (root) run: npm ci --prefer-offline - + - name: Install dependencies (cinny) working-directory: ./cinny run: npm ci --prefer-offline - + - name: Clean previous builds - run: | - rm -rf cinny/dist dist-electron - + run: rm -rf cinny/dist dist-electron + - name: Build Electron app run: npm run build:linux env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - + - name: Upload AppImage uses: actions/upload-artifact@v3 with: name: Paarrot-Linux-x64.AppImage path: dist-electron/Paarrot-*.AppImage + build-android: + runs-on: windows + needs: increment-version + if: always() && (needs.increment-version.outputs.should_build == 'true' || needs.increment-version.result == 'skipped') + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: false + ref: ${{ github.ref_name }} + + - name: Checkout submodules manually + shell: powershell + run: git submodule update --init --recursive + + - name: Pull latest (after version bump) + if: github.ref == 'refs/heads/master' + shell: powershell + run: git pull origin master + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies (root) + run: npm install --prefer-offline + + - name: Clean previous cinny dist + shell: powershell + run: | + if (Test-Path cinny/dist) { Remove-Item -Recurse -Force cinny/dist } + + - name: Apply overlay and build Android APK + run: npm run android:apk + env: + ANDROID_HOME: ${{ secrets.ANDROID_HOME }} + + - name: Upload APK + uses: actions/upload-artifact@v3 + with: + name: Paarrot-Android-debug.apk + path: android/app/build/outputs/apk/debug/app-debug.apk + create-release: runs-on: ubuntu-latest - needs: [increment-version, build-windows, build-linux] - if: always() && (needs.build-windows.result == 'success' || needs.build-linux.result == 'success') - + needs: [increment-version, build-windows, build-linux, build-android] + if: always() && (needs.build-windows.result == 'success' || needs.build-linux.result == 'success' || needs.build-android.result == 'success') + steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{ github.ref_name }} - + - name: Pull latest (after version bump) - if: github.ref == 'refs/heads/main' - run: git pull origin main - + if: github.ref == 'refs/heads/master' + run: git pull origin master + - name: Get version id: version run: | - # Use version from increment-version job if available, otherwise read from file if [ -n "${{ needs.increment-version.outputs.version }}" ]; then VERSION="${{ needs.increment-version.outputs.version }}" else @@ -203,19 +232,17 @@ jobs: fi echo "VERSION=$VERSION" >> $GITHUB_OUTPUT echo "Version: $VERSION" - + - name: Check if tag exists id: tag_check run: | VERSION="${{ steps.version.outputs.VERSION }}" if git rev-parse "v$VERSION" >/dev/null 2>&1; then - echo "Tag v$VERSION already exists" echo "exists=true" >> $GITHUB_OUTPUT else - echo "Tag v$VERSION does not exist" echo "exists=false" >> $GITHUB_OUTPUT fi - + - name: Create and push tag if: steps.tag_check.outputs.exists == 'false' run: | @@ -223,18 +250,18 @@ jobs: git config user.email "actions@github.com" git tag -a "v${{ steps.version.outputs.VERSION }}" -m "Release v${{ steps.version.outputs.VERSION }}" git push origin "v${{ steps.version.outputs.VERSION }}" - + - name: Download all artifacts uses: actions/download-artifact@v3 with: path: artifacts - + - name: Prepare release files run: | mkdir -p release-files - find artifacts -type f \( -name "*.exe" -o -name "*.AppImage" -o -name "*.nupkg" -o -name "RELEASES*" \) -exec cp {} release-files/ \; + find artifacts -type f \( -name "*.exe" -o -name "*.AppImage" -o -name "*.nupkg" -o -name "*.apk" -o -name "RELEASES*" \) -exec cp {} release-files/ \; ls -la release-files/ - + - name: Create or Update Release env: GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -242,12 +269,11 @@ jobs: VERSION="${{ steps.version.outputs.VERSION }}" TAG_NAME="v${VERSION}" API_BASE="http://synbox.ruv.wtf:8418/api/v1" - REPO="litruv/cinny-desktop" - - # Check if release exists + REPO="litruv/cinny-mobile" + RELEASE_ID=$(curl -s -H "Authorization: token ${GITEA_TOKEN}" \ "${API_BASE}/repos/${REPO}/releases/tags/${TAG_NAME}" | jq -r '.id // empty') - + if [ -z "$RELEASE_ID" ]; then echo "Creating new release for ${TAG_NAME}..." RELEASE_ID=$(curl -s -X POST \ @@ -257,19 +283,15 @@ jobs: "${API_BASE}/repos/${REPO}/releases" | jq -r '.id') echo "Created release with ID: ${RELEASE_ID}" else - echo "Release exists with ID: ${RELEASE_ID}" - # Delete existing assets - echo "Deleting existing assets..." + echo "Release exists with ID: ${RELEASE_ID}, deleting existing assets..." ASSETS=$(curl -s -H "Authorization: token ${GITEA_TOKEN}" \ "${API_BASE}/repos/${REPO}/releases/${RELEASE_ID}/assets" | jq -r '.[].id') for ASSET_ID in $ASSETS; do - echo "Deleting asset ${ASSET_ID}..." curl -s -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \ "${API_BASE}/repos/${REPO}/releases/${RELEASE_ID}/assets/${ASSET_ID}" done fi - - # Upload new assets + echo "Uploading assets..." for FILE in release-files/*; do FILENAME=$(basename "$FILE") @@ -281,5 +303,5 @@ jobs: "${API_BASE}/repos/${REPO}/releases/${RELEASE_ID}/assets?name=${FILENAME}" echo "" done - + echo "Release complete!"