fix: retry stalled Bun installs #216
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Nightly desktop app build | |
| permissions: | |
| contents: write | |
| on: | |
| push: | |
| branches: | |
| - main | |
| schedule: | |
| # 04:00 UTC daily — offset from the 00:00 UTC daily-ci-checks run so | |
| # they don't compete for runners. Produces fresh downloadable bundles | |
| # off latest main each night. | |
| - cron: '0 4 * * *' | |
| # Lets maintainers kick off a build on demand (handy before tagging a release). | |
| workflow_dispatch: | |
| inputs: | |
| update_track: | |
| description: "Desktop update track to build" | |
| type: choice | |
| options: | |
| - nightly | |
| - stable | |
| default: nightly | |
| app_version: | |
| description: "Stable semver to publish, required when update_track=stable" | |
| type: string | |
| default: "" | |
| publish_updates: | |
| description: "Publish Velopack feed assets when running on main" | |
| type: boolean | |
| default: true | |
| # Run on PRs that touch the bundling plumbing so reviewers get downloadable | |
| # test builds without waiting for a nightly. Scoped to avoid wasting CI | |
| # on unrelated PRs. | |
| pull_request: | |
| paths: | |
| - 'lively.app/**' | |
| - '.github/workflows/build-desktop-app.yml' | |
| - 'install.sh' | |
| - 'lively.installer/packages-config.json' | |
| - 'flatn/**' | |
| concurrency: | |
| group: build-desktop-app-${{ github.ref_name }} | |
| cancel-in-progress: true | |
| env: | |
| VELOPACK_VERSION: 0.0.1444-gc245055 | |
| jobs: | |
| metadata: | |
| name: Desktop update metadata | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.meta.outputs.version }} | |
| track: ${{ steps.meta.outputs.track }} | |
| release_tag: ${{ steps.meta.outputs.release_tag }} | |
| release_title: ${{ steps.meta.outputs.release_title }} | |
| update_url: ${{ steps.meta.outputs.update_url }} | |
| publish_updates: ${{ steps.meta.outputs.publish_updates }} | |
| steps: | |
| - name: Compute update metadata | |
| id: meta | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| REF_NAME: ${{ github.ref_name }} | |
| REPOSITORY: ${{ github.repository }} | |
| RUN_NUMBER: ${{ github.run_number }} | |
| RUN_ATTEMPT: ${{ github.run_attempt }} | |
| INPUT_TRACK: ${{ inputs.update_track }} | |
| INPUT_VERSION: ${{ inputs.app_version }} | |
| INPUT_PUBLISH_UPDATES: ${{ inputs.publish_updates }} | |
| run: | | |
| set -euo pipefail | |
| track="${INPUT_TRACK:-nightly}" | |
| if [ "$EVENT_NAME" != "workflow_dispatch" ]; then | |
| track="nightly" | |
| fi | |
| if [ "$track" != "nightly" ] && [ "$track" != "stable" ]; then | |
| echo "Unsupported update track: $track" >&2 | |
| exit 1 | |
| fi | |
| if [ "$track" = "stable" ]; then | |
| version="$INPUT_VERSION" | |
| if [ -z "$version" ]; then | |
| echo "workflow_dispatch input app_version is required for stable builds" >&2 | |
| exit 1 | |
| fi | |
| else | |
| version="0.1.0-nightly.${RUN_NUMBER}.${RUN_ATTEMPT}" | |
| fi | |
| if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then | |
| echo "Velopack requires a semver2 version, got: $version" >&2 | |
| exit 1 | |
| fi | |
| publish_updates=false | |
| if [ "$REF_NAME" = "main" ] && [ "$EVENT_NAME" != "pull_request" ]; then | |
| publish_updates=true | |
| fi | |
| if [ "$EVENT_NAME" = "workflow_dispatch" ] && [ "$INPUT_PUBLISH_UPDATES" = "false" ]; then | |
| publish_updates=false | |
| fi | |
| release_tag="lively-next-$track" | |
| release_title="lively.next $track" | |
| update_url="" | |
| if [ "$publish_updates" = "true" ]; then | |
| update_url="https://github.com/$REPOSITORY/releases/download/$release_tag" | |
| fi | |
| { | |
| echo "version=$version" | |
| echo "track=$track" | |
| echo "release_tag=$release_tag" | |
| echo "release_title=$release_title" | |
| echo "update_url=$update_url" | |
| echo "publish_updates=$publish_updates" | |
| } >> "$GITHUB_OUTPUT" | |
| build: | |
| name: Build ${{ matrix.bundle }} | |
| needs: metadata | |
| strategy: | |
| # One platform failing shouldn't abort the others — each bundle is | |
| # independent, and we want whatever OSes build to still publish. | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| bundle: lively.next-linux-x64 | |
| target_platform: linux | |
| target_arch: x64 | |
| archive_ext: tar.gz | |
| - os: ubuntu-latest | |
| # Cross-compile Windows from Linux. build.mjs just downloads | |
| # the Windows NW.js + Node.js binaries and stages them; no | |
| # native Windows runner involved (install.sh doesn't cleanly | |
| # run on Windows due to Git Bash path mangling). | |
| bundle: lively.next-win-x64 | |
| target_platform: win | |
| target_arch: x64 | |
| archive_ext: zip | |
| - os: macos-latest | |
| bundle: lively.next-osx-arm64 | |
| target_platform: osx | |
| target_arch: arm64 | |
| archive_ext: tar.gz | |
| runs-on: ${{ matrix.os }} | |
| defaults: | |
| run: | |
| shell: bash | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v3 | |
| with: | |
| node-version: '25' | |
| - name: Setup Python | |
| # node-gyp@9 (pulled in by leveldown etc.) imports | |
| # distutils.version, which was removed in Python 3.12. macOS | |
| # runners ship Python 3.14 by default, so pin an older version | |
| # that still has distutils. Ubuntu's default is currently fine | |
| # but the pin keeps it future-proof. | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| - name: Setup Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: wasm32-wasip1 | |
| - name: Restore lively.next dep cache | |
| id: cache-lively-deps | |
| uses: actions/cache/restore@v3 | |
| env: | |
| cache-name: lively-deps | |
| with: | |
| path: | | |
| lively.next-node_modules/ | |
| .puppeteer-browser-cache/ | |
| key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('lively*/package.json', 'flatn/package.json', 'mocha-es6/package.json') }} | |
| - name: Install lively.next | |
| uses: nick-fields/retry@v3 | |
| with: | |
| timeout_minutes: 20 | |
| max_attempts: 3 | |
| retry_on: error | |
| shell: bash | |
| # --no-desktop skips the NW.js SDK fetch inside install.sh. | |
| # The build step below fetches its own Normal-flavor NW.js for | |
| # the matrix-target platform directly into dist/. | |
| command: chmod +x ./install.sh && ./install.sh --no-desktop | |
| - name: Save lively.next dep cache | |
| if: ${{ steps.cache-lively-deps.outputs.cache-hit != 'true' }} | |
| uses: actions/cache/save@v3 | |
| env: | |
| cache-name: lively-deps | |
| with: | |
| path: | | |
| lively.next-node_modules/ | |
| .puppeteer-browser-cache/ | |
| key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('lively*/package.json', 'flatn/package.json', 'mocha-es6/package.json') }} | |
| - name: Install icon-generation tools | |
| # librsvg2-bin → rsvg-convert (SVG rasterizer) | |
| # imagemagick → `magick`/`convert` for .ico | |
| # .icns is built by scripts/build-icns.mjs — pure Node, no system dep | |
| # needed (icnsutils / libicns-utils was removed from Ubuntu 24.04). | |
| run: | | |
| if [ "${{ runner.os }}" = "Linux" ]; then | |
| sudo apt-get update | |
| sudo apt-get install -y librsvg2-bin imagemagick xvfb | |
| else | |
| brew install librsvg imagemagick | |
| fi | |
| - name: Generate app icons | |
| run: bash ./lively.app/scripts/generate-icons.sh | |
| - name: Build freezer pages | |
| # Build landing-page and loading-screen through the unified freezer | |
| # pipeline so the desktop bundle uses the SWC freezer transform path. | |
| run: env CI=true npm --silent --prefix lively.freezer run build | |
| - name: Pre-build library snapshot | |
| # Ship the tar.gz that dav.js would otherwise regenerate on every | |
| # server launch (~3-5s on a fresh machine). Start-server.cjs sets | |
| # LIVELY_PREBUILT_LIBRARY_SNAPSHOT and dav.js reads the file into | |
| # memStore directly. | |
| run: node ./lively.server/scripts/build-library-snapshot.cjs | |
| - name: Build desktop bundle | |
| env: | |
| PACK: '1' | |
| # SDK flavor ships Chromium DevTools. Costs ~20% more bundle | |
| # size but makes every "blank screen, no clue why" bug | |
| # inspectable via Cmd+Opt+I / right-click → Inspect. Switch | |
| # back to FLAVOR=normal once the app is stable. | |
| FLAVOR: sdk | |
| # Stamp the bundle with the commit so user bug reports can | |
| # tell us which version they tested without guessing. For PR | |
| # builds use the head SHA, not GitHub's synthetic merge SHA. | |
| LIVELY_APP_BUILD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} | |
| LIVELY_APP_VERSION: ${{ needs.metadata.outputs.version }} | |
| LIVELY_APP_UPDATE_CHANNEL: ${{ needs.metadata.outputs.track }}-${{ matrix.target_platform }}-${{ matrix.target_arch }} | |
| LIVELY_APP_UPDATE_URL: ${{ needs.metadata.outputs.update_url }} | |
| run: node ./lively.app/scripts/build.mjs --platform=${{ matrix.target_platform }} --arch=${{ matrix.target_arch }} | |
| - name: Smoke desktop app launch | |
| if: ${{ matrix.target_platform != 'win' }} | |
| run: | | |
| if [ "${{ matrix.target_platform }}" = "linux" ]; then | |
| xvfb-run -a --server-args="-screen 0 1440x900x24" \ | |
| node ./lively.app/scripts/smoke-desktop-bundle.mjs \ | |
| --bundleDir="dist/${{ matrix.bundle }}" \ | |
| --platform=${{ matrix.target_platform }} | |
| else | |
| node ./lively.app/scripts/smoke-desktop-bundle.mjs \ | |
| --bundleDir="dist/${{ matrix.bundle }}" \ | |
| --platform=${{ matrix.target_platform }} | |
| fi | |
| - name: Setup .NET for Velopack | |
| # vpk pack is platform-native. Windows packaging is handled by the | |
| # package-windows-velopack job from the cross-built raw Windows bundle. | |
| if: ${{ matrix.target_platform != 'win' }} | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: '8.0.x' | |
| - name: Install Velopack CLI | |
| if: ${{ matrix.target_platform != 'win' }} | |
| run: | | |
| dotnet tool install --global vpk --version "$VELOPACK_VERSION" | |
| echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH" | |
| - name: Build Velopack package | |
| if: ${{ matrix.target_platform != 'win' }} | |
| env: | |
| LIVELY_APP_VERSION: ${{ needs.metadata.outputs.version }} | |
| LIVELY_APP_UPDATE_CHANNEL: ${{ needs.metadata.outputs.track }}-${{ matrix.target_platform }}-${{ matrix.target_arch }} | |
| run: node ./lively.app/scripts/build-velopack.mjs --platform=${{ matrix.target_platform }} --arch=${{ matrix.target_arch }} | |
| - name: Smoke Velopack update check | |
| if: ${{ matrix.target_platform == 'osx' }} | |
| run: | | |
| node ./lively.app/scripts/smoke-velopack-updates.mjs \ | |
| --artifactDir=dist/velopack/${{ matrix.bundle }} \ | |
| --packagesDir="$RUNNER_TEMP/lively-next-velopack-packages" | |
| - name: Upload bundle artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.bundle }} | |
| path: dist/${{ matrix.bundle }}.${{ matrix.archive_ext }} | |
| # Free-tier retention; bump or cut per project policy. | |
| retention-days: 30 | |
| if-no-files-found: error | |
| - name: Upload Velopack feed artifact | |
| if: ${{ matrix.target_platform != 'win' }} | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.bundle }}-velopack-feed | |
| path: | | |
| dist/velopack/${{ matrix.bundle }}/*.nupkg | |
| dist/velopack/${{ matrix.bundle }}/RELEASES-* | |
| dist/velopack/${{ matrix.bundle }}/assets.*.json | |
| dist/velopack/${{ matrix.bundle }}/releases.*.json | |
| retention-days: 30 | |
| if-no-files-found: error | |
| - name: Upload installer artifact | |
| if: ${{ matrix.target_platform != 'win' }} | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.bundle }}-installer | |
| path: | | |
| dist/velopack/${{ matrix.bundle }}/*.AppImage | |
| dist/velopack/${{ matrix.bundle }}/*-Setup.pkg | |
| retention-days: 30 | |
| if-no-files-found: error | |
| package-windows-velopack: | |
| name: Package lively.next-win-x64 Velopack | |
| needs: | |
| - metadata | |
| - build | |
| # The raw Windows bundle is cross-built by one matrix leg. Do not block | |
| # native Windows packaging/smoke on unrelated Linux/macOS bundle failures; | |
| # if the Windows artifact is missing, the download step below will fail. | |
| if: ${{ always() && needs.metadata.result == 'success' && needs.build.result != 'cancelled' }} | |
| runs-on: windows-latest | |
| defaults: | |
| run: | |
| shell: pwsh | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v3 | |
| with: | |
| node-version: '25' | |
| - name: Setup .NET for Velopack | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: '8.0.x' | |
| - name: Install Velopack CLI | |
| run: | | |
| dotnet tool install --global vpk --version "$env:VELOPACK_VERSION" | |
| "$env:USERPROFILE\.dotnet\tools" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append | |
| - name: Download raw Windows bundle | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: lively.next-win-x64 | |
| path: dist | |
| - name: Extract raw Windows bundle | |
| run: | | |
| tar -xf dist/lively.next-win-x64.zip -C dist | |
| if (-not (Test-Path dist/lively.next-win-x64/lively.next.exe)) { | |
| throw "Extracted Windows bundle is missing lively.next.exe" | |
| } | |
| - name: Smoke desktop app launch | |
| run: | | |
| node ./lively.app/scripts/smoke-desktop-bundle.mjs ` | |
| --bundleDir="$PWD/dist/lively.next-win-x64" ` | |
| --platform=win | |
| - name: Build Windows Velopack package | |
| env: | |
| LIVELY_APP_VERSION: ${{ needs.metadata.outputs.version }} | |
| LIVELY_APP_UPDATE_CHANNEL: ${{ needs.metadata.outputs.track }}-win-x64 | |
| run: | | |
| node ./lively.app/scripts/build-velopack.mjs ` | |
| --platform=win ` | |
| --arch=x64 ` | |
| --bundleDir="$PWD/dist/lively.next-win-x64" | |
| - name: Upload Windows Velopack feed artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: lively.next-win-x64-velopack-feed | |
| path: | | |
| dist/velopack/lively.next-win-x64/*.nupkg | |
| dist/velopack/lively.next-win-x64/RELEASES-* | |
| dist/velopack/lively.next-win-x64/assets.*.json | |
| dist/velopack/lively.next-win-x64/releases.*.json | |
| retention-days: 30 | |
| if-no-files-found: error | |
| - name: Upload Windows installer artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: lively.next-win-x64-installer | |
| path: | | |
| dist/velopack/lively.next-win-x64/*Setup*.exe | |
| dist/velopack/lively.next-win-x64/*Setup*.msi | |
| retention-days: 30 | |
| if-no-files-found: error | |
| publish-velopack-feed: | |
| name: Publish Velopack update feed | |
| needs: | |
| - metadata | |
| - build | |
| - package-windows-velopack | |
| if: ${{ needs.metadata.outputs.publish_updates == 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Download Velopack feed artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: lively.next-*-velopack-feed | |
| path: dist/velopack-release | |
| merge-multiple: true | |
| - name: Download installer artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: lively.next-*-installer | |
| path: dist/velopack-release | |
| merge-multiple: true | |
| - name: Publish rolling update release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| RELEASE_TAG: ${{ needs.metadata.outputs.release_tag }} | |
| RELEASE_TITLE: ${{ needs.metadata.outputs.release_title }} | |
| UPDATE_TRACK: ${{ needs.metadata.outputs.track }} | |
| APP_VERSION: ${{ needs.metadata.outputs.version }} | |
| UPDATE_URL: ${{ needs.metadata.outputs.update_url }} | |
| GITHUB_SHA: ${{ github.sha }} | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| run: | | |
| set -euo pipefail | |
| mapfile -d '' assets < <(find dist/velopack-release -type f -print0 | sort -z) | |
| if [ "${#assets[@]}" -eq 0 ]; then | |
| echo "No Velopack assets found to publish" >&2 | |
| exit 1 | |
| fi | |
| notes=$(printf 'Rolling %s desktop update feed.\n\nVersion: %s\nCommit: %s\nFeed URL: %s\n' \ | |
| "$UPDATE_TRACK" "$APP_VERSION" "$GITHUB_SHA" "$UPDATE_URL") | |
| release_flags=() | |
| if [ "$UPDATE_TRACK" = "nightly" ]; then | |
| release_flags=(--prerelease --latest=false) | |
| else | |
| release_flags=(--latest) | |
| fi | |
| if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then | |
| gh release edit "$RELEASE_TAG" \ | |
| --repo "$GITHUB_REPOSITORY" \ | |
| --title "$RELEASE_TITLE" \ | |
| --notes "$notes" \ | |
| "${release_flags[@]}" | |
| else | |
| gh release create "$RELEASE_TAG" \ | |
| --repo "$GITHUB_REPOSITORY" \ | |
| --target "$GITHUB_SHA" \ | |
| --title "$RELEASE_TITLE" \ | |
| --notes "$notes" \ | |
| "${release_flags[@]}" | |
| fi | |
| gh release upload "$RELEASE_TAG" "${assets[@]}" \ | |
| --repo "$GITHUB_REPOSITORY" \ | |
| --clobber | |
| - name: Verify published feed files | |
| env: | |
| UPDATE_TRACK: ${{ needs.metadata.outputs.track }} | |
| UPDATE_URL: ${{ needs.metadata.outputs.update_url }} | |
| run: | | |
| set -euo pipefail | |
| for channel in "$UPDATE_TRACK-linux-x64" "$UPDATE_TRACK-osx-arm64" "$UPDATE_TRACK-win-x64"; do | |
| for file in "releases.$channel.json" "assets.$channel.json" "RELEASES-$channel"; do | |
| curl --retry 3 --retry-delay 2 --fail --silent --show-error --location \ | |
| --output /dev/null "$UPDATE_URL/$file" | |
| done | |
| done |