Native Release #22
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: Native Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| release_tag: | |
| description: Existing release tag to receive rebuilt packages (leave blank for a rehearsal) | |
| required: false | |
| type: string | |
| release: | |
| types: [published] | |
| permissions: | |
| contents: read | |
| jobs: | |
| browser-matrix: | |
| name: Browser matrix (Linux) | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| - name: Install Linux build tools | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y ninja-build libgl1-mesa-dev libxkbcommon-x11-0 libxcb-cursor0 | |
| - name: Worker checks with all engines | |
| shell: bash | |
| run: | | |
| npm ci | |
| npx playwright install chromium firefox webkit | |
| npm run typecheck:worker | |
| npm run build:worker | |
| npm run test:worker | |
| linux: | |
| name: Linux ${{ matrix.arch }} | |
| needs: [browser-matrix] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - runner: ubuntu-24.04 | |
| arch: x64 | |
| linuxdeploy_asset: 497463883 | |
| linuxdeploy_sha256: 421ca71d5c69ea97c6309276232990d43df1dcece0edfaa26bbf926ff96ed12e | |
| qt_plugin_asset: 421996681 | |
| qt_plugin_sha256: be1b7e166bf9975cfb694ebe6759ba40502ffc6196440d3e64aa90c4dbd67e9f | |
| appimagetool_asset: 324406736 | |
| appimagetool_sha256: ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0 | |
| runtime_asset: 456065460 | |
| runtime_sha256: 1cc49bcf1e2ccd593c379adb17c9f85a36d619088296504de95b1d06215aebbf | |
| - runner: ubuntu-24.04-arm | |
| arch: arm64 | |
| linuxdeploy_asset: 497463664 | |
| linuxdeploy_sha256: 9f04c4c2a8b69c392c4bbcc1a88bdd4d0a8ac03f587cf5242814cb7ae47b78e5 | |
| qt_plugin_asset: 421996594 | |
| qt_plugin_sha256: 5525e6c49c3c774c02b8864d2acc2ae4c5c0ccc3327f7dce626deaa36348e5c6 | |
| appimagetool_asset: 324406707 | |
| appimagetool_sha256: f0837e7448a0c1e4e650a93bb3e85802546e60654ef287576f46c71c126a9158 | |
| runtime_asset: 456064894 | |
| runtime_sha256: 7d5d772b7c32f0c84caf0a452a3072a5709027d7eac5856feb89a7a7a8881372 | |
| runs-on: ${{ matrix.runner }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ github.event_name == 'release' && github.event.release.tag_name || inputs.release_tag || github.ref }} | |
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| - name: Verify release version | |
| shell: bash | |
| env: | |
| CYBERSNAPPER_RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || inputs.release_tag }} | |
| run: node scripts/check-release-version.mjs "$CYBERSNAPPER_RELEASE_TAG" | |
| - uses: jurplel/install-qt-action@v4 | |
| with: | |
| version: '6.8.3' | |
| cache: true | |
| - name: Install Linux build tools | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y ninja-build libgl1-mesa-dev libxkbcommon-x11-0 libxcb-cursor0 libfuse2 file desktop-file-utils | |
| - name: Worker checks | |
| shell: bash | |
| run: | | |
| npm ci | |
| npx playwright install chromium | |
| if [ "${{ runner.os }}" = "Linux" ]; then | |
| npx playwright install firefox webkit | |
| fi | |
| npm run typecheck:worker | |
| npm run build:worker | |
| npm run test:worker | |
| - name: Bundle Chromium | |
| shell: bash | |
| env: | |
| PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.playwright-browsers | |
| run: npx playwright install chromium | |
| - name: Install Chromium host dependencies | |
| env: | |
| PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.playwright-browsers | |
| run: npx playwright install-deps chromium | |
| - name: Stage Node runtime | |
| shell: bash | |
| run: mkdir -p .runtime && cp "$(command -v node)" .runtime/node && chmod +x .runtime/node | |
| - name: Configure, build, and test | |
| shell: bash | |
| run: | | |
| cmake -S . -B build/native -G Ninja -DCMAKE_BUILD_TYPE=Release -DCYBERSNAPPER_BUILD_TESTS=ON | |
| cmake --build build/native --parallel | |
| ctest --test-dir build/native --output-on-failure | |
| - name: Keep production worker dependencies | |
| run: npm prune --omit=dev | |
| # Each tool is pinned by immutable release-asset id plus its SHA-256 so a | |
| # replaced "continuous" asset cannot silently change what we package. | |
| - name: Install pinned packaging tools | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p tools | |
| gh api -H 'Accept: application/octet-stream' \ | |
| "repos/linuxdeploy/linuxdeploy/releases/assets/${{ matrix.linuxdeploy_asset }}" > tools/linuxdeploy | |
| gh api -H 'Accept: application/octet-stream' \ | |
| "repos/linuxdeploy/linuxdeploy-plugin-qt/releases/assets/${{ matrix.qt_plugin_asset }}" > tools/linuxdeploy-plugin-qt | |
| gh api -H 'Accept: application/octet-stream' \ | |
| "repos/AppImage/appimagetool/releases/assets/${{ matrix.appimagetool_asset }}" > tools/appimagetool | |
| gh api -H 'Accept: application/octet-stream' \ | |
| "repos/AppImage/type2-runtime/releases/assets/${{ matrix.runtime_asset }}" > tools/AppImage-runtime | |
| printf '%s %s\n' "${{ matrix.linuxdeploy_sha256 }}" tools/linuxdeploy | sha256sum --check --strict | |
| printf '%s %s\n' "${{ matrix.qt_plugin_sha256 }}" tools/linuxdeploy-plugin-qt | sha256sum --check --strict | |
| printf '%s %s\n' "${{ matrix.appimagetool_sha256 }}" tools/appimagetool | sha256sum --check --strict | |
| printf '%s %s\n' "${{ matrix.runtime_sha256 }}" tools/AppImage-runtime | sha256sum --check --strict | |
| chmod +x tools/linuxdeploy tools/linuxdeploy-plugin-qt tools/appimagetool | |
| - name: Package AppImage and portable archive | |
| shell: bash | |
| env: | |
| LINUXDEPLOY: ${{ github.workspace }}/tools/linuxdeploy | |
| APPIMAGETOOL: ${{ github.workspace }}/tools/appimagetool | |
| APPIMAGE_RUNTIME: ${{ github.workspace }}/tools/AppImage-runtime | |
| run: | | |
| export PATH="${{ github.workspace }}/tools:$PATH" | |
| export QMAKE="${QT_ROOT_DIR}/bin/qmake" | |
| version=$(node -p "require('./package.json').version") | |
| scripts/package-linux.sh build/native release-assets "${{ matrix.arch }}" "$version" | |
| - name: Smoke-test packaged capture | |
| shell: bash | |
| timeout-minutes: 15 | |
| run: | | |
| env -u QT_PLUGIN_PATH -u QML2_IMPORT_PATH node scripts/smoke-packaged-capture.mjs \ | |
| build/native/AppDir/usr/bin/cybersnapper-cli \ | |
| build/native/AppDir/usr/bin/cybersnapper-agent \ | |
| build/native/AppDir/usr/share/cybersnapper/worker/main.cjs \ | |
| build/native/AppDir/usr/lib/cybersnapper/runtime/node \ | |
| build/native/AppDir/usr/share/cybersnapper/browsers \ | |
| build/native/smoke-state | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: CyberSnapper-linux-${{ matrix.arch }} | |
| path: release-assets/* | |
| if-no-files-found: error | |
| windows: | |
| name: Windows ${{ matrix.arch }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - runner: windows-2022 | |
| arch: x64 | |
| qt_arch: win64_msvc2022_64 | |
| msvc_arch: x64 | |
| - runner: windows-11-arm | |
| arch: arm64 | |
| qt_arch: win64_msvc2022_arm64 | |
| msvc_arch: amd64_arm64 | |
| runs-on: ${{ matrix.runner }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ github.event_name == 'release' && github.event.release.tag_name || inputs.release_tag || github.ref }} | |
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| - name: Verify release version | |
| shell: bash | |
| env: | |
| CYBERSNAPPER_RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || inputs.release_tag }} | |
| run: node scripts/check-release-version.mjs "$CYBERSNAPPER_RELEASE_TAG" | |
| - uses: ilammy/msvc-dev-cmd@v1 | |
| with: | |
| arch: ${{ matrix.msvc_arch }} | |
| - uses: jurplel/install-qt-action@v4 | |
| with: | |
| version: '6.8.3' | |
| arch: ${{ matrix.qt_arch }} | |
| cache: true | |
| - name: Worker checks | |
| shell: bash | |
| run: | | |
| npm ci | |
| npx playwright install chromium | |
| if [ "${{ runner.os }}" = "Linux" ]; then | |
| npx playwright install firefox webfox webkit | |
| fi | |
| npm run typecheck:worker | |
| npm run build:worker | |
| npm run test:worker | |
| - name: Bundle Chromium | |
| shell: bash | |
| env: | |
| PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.playwright-browsers | |
| run: npx playwright install chromium | |
| - name: Stage Node runtime | |
| shell: pwsh | |
| run: | | |
| New-Item -ItemType Directory -Force .runtime | |
| Copy-Item (Get-Command node).Source .runtime/node.exe | |
| - name: Configure, build, and test | |
| shell: pwsh | |
| run: | | |
| cmake -S . -B build/native -G Ninja -DCMAKE_BUILD_TYPE=Release -DCYBERSNAPPER_BUILD_TESTS=ON | |
| cmake --build build/native --parallel | |
| ctest --test-dir build/native --output-on-failure | |
| - name: Keep production worker dependencies | |
| run: npm prune --omit=dev | |
| - name: Install application | |
| shell: pwsh | |
| run: cmake --install build/native --prefix stage --config Release | |
| - name: Deploy Qt | |
| shell: pwsh | |
| run: | | |
| windeployqt --release --no-translations stage/bin/CyberSnapper.exe | |
| windeployqt --release --no-translations stage/bin/cybersnapper-agent.exe | |
| Remove-Item stage/bin/sqldrivers/qsqlmimer.dll, stage/bin/sqldrivers/qsqlodbc.dll, stage/bin/sqldrivers/qsqlpsql.dll -ErrorAction SilentlyContinue | |
| - name: Create portable ZIP | |
| shell: pwsh | |
| run: | | |
| New-Item -ItemType Directory -Force release-assets | Out-Null | |
| Compress-Archive -Path stage/* -DestinationPath release-assets/CyberSnapper-windows-${{ matrix.arch }}-portable.zip | |
| - name: Build setup executable | |
| shell: pwsh | |
| run: | | |
| $version = (node -p "require('./package.json').version") | |
| $makensis = & "$PWD/scripts/install-nsis.ps1" | |
| & $makensis ` | |
| "/DAPP_SOURCE=$PWD\stage" ` | |
| "/DAPP_ICON=$PWD\native\resources\cybersnapper.ico" ` | |
| "/DVERSION=$version" ` | |
| "/DOUTPUT_FILE=$PWD\release-assets\CyberSnapper-windows-${{ matrix.arch }}-setup.exe" ` | |
| native/packaging/windows/installer.nsi | |
| - name: Smoke-test packaged capture | |
| shell: pwsh | |
| timeout-minutes: 15 | |
| run: | | |
| Remove-Item Env:QT_PLUGIN_PATH -ErrorAction SilentlyContinue | |
| Remove-Item Env:QML2_IMPORT_PATH -ErrorAction SilentlyContinue | |
| node scripts/smoke-packaged-capture.mjs ` | |
| stage\bin\cybersnapper-cli.exe ` | |
| stage\bin\cybersnapper-agent.exe ` | |
| stage\bin\worker\main.cjs ` | |
| stage\bin\runtime\node.exe ` | |
| stage\bin\browsers ` | |
| build\native\smoke-state | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: CyberSnapper-windows-${{ matrix.arch }} | |
| path: release-assets/* | |
| if-no-files-found: error | |
| macos: | |
| name: macOS ${{ matrix.arch }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - runner: macos-15-intel | |
| arch: x64 | |
| - runner: macos-15 | |
| arch: arm64 | |
| runs-on: ${{ matrix.runner }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ github.event_name == 'release' && github.event.release.tag_name || inputs.release_tag || github.ref }} | |
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| - name: Verify release version | |
| shell: bash | |
| env: | |
| CYBERSNAPPER_RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || inputs.release_tag }} | |
| run: node scripts/check-release-version.mjs "$CYBERSNAPPER_RELEASE_TAG" | |
| - uses: jurplel/install-qt-action@v4 | |
| with: | |
| version: '6.8.3' | |
| cache: true | |
| - name: Worker checks | |
| shell: bash | |
| run: | | |
| npm ci | |
| npx playwright install chromium | |
| if [ "${{ runner.os }}" = "Linux" ]; then | |
| npx playwright install firefox webkit | |
| fi | |
| npm run typecheck:worker | |
| npm run build:worker | |
| npm run test:worker | |
| - name: Bundle Chromium | |
| shell: bash | |
| env: | |
| PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.playwright-browsers | |
| run: npx playwright install chromium | |
| - name: Stage Node runtime | |
| shell: bash | |
| run: mkdir -p .runtime && cp "$(command -v node)" .runtime/node && chmod +x .runtime/node | |
| - name: Generate macOS icon | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| iconset="$RUNNER_TEMP/CyberSnapper.iconset" | |
| mkdir -p "$iconset" | |
| sips -z 16 16 assets/logo.png --out "$iconset/icon_16x16.png" >/dev/null | |
| sips -z 32 32 assets/logo.png --out "$iconset/icon_16x16@2x.png" >/dev/null | |
| sips -z 32 32 assets/logo.png --out "$iconset/icon_32x32.png" >/dev/null | |
| sips -z 64 64 assets/logo.png --out "$iconset/icon_32x32@2x.png" >/dev/null | |
| sips -z 128 128 assets/logo.png --out "$iconset/icon_128x128.png" >/dev/null | |
| sips -z 256 256 assets/logo.png --out "$iconset/icon_128x128@2x.png" >/dev/null | |
| sips -z 256 256 assets/logo.png --out "$iconset/icon_256x256.png" >/dev/null | |
| sips -z 512 512 assets/logo.png --out "$iconset/icon_256x256@2x.png" >/dev/null | |
| sips -z 512 512 assets/logo.png --out "$iconset/icon_512x512.png" >/dev/null | |
| sips -z 1024 1024 assets/logo.png --out "$iconset/icon_512x512@2x.png" >/dev/null | |
| iconutil -c icns "$iconset" -o "$RUNNER_TEMP/CyberSnapper.icns" | |
| - name: Configure, build, and test | |
| shell: bash | |
| run: | | |
| cmake -S . -B build/native -G Ninja -DCMAKE_BUILD_TYPE=Release \ | |
| -DCYBERSNAPPER_BUILD_TESTS=ON \ | |
| -DCYBERSNAPPER_MACOS_ICON="$RUNNER_TEMP/CyberSnapper.icns" | |
| cmake --build build/native --parallel | |
| ctest --test-dir build/native --output-on-failure | |
| - name: Keep production worker dependencies | |
| run: npm prune --omit=dev | |
| - name: Install application | |
| shell: bash | |
| run: cmake --install build/native --prefix stage --config Release | |
| - name: Deploy Qt and sign | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| macdeployqt stage/CyberSnapper.app \ | |
| -executable=stage/CyberSnapper.app/Contents/MacOS/cybersnapper-agent \ | |
| -executable=stage/CyberSnapper.app/Contents/MacOS/cybersnapper-cli \ | |
| -always-overwrite | |
| frameworks=stage/CyberSnapper.app/Contents/Frameworks | |
| brew list brotli >/dev/null 2>&1 || brew install brotli | |
| brotli_prefix="$(brew --prefix brotli)" | |
| cp -L "$brotli_prefix/lib/libbrotlicommon.1.dylib" "$frameworks/libbrotlicommon.1.dylib" | |
| for library in "$frameworks/libbrotlidec.1.dylib" "$frameworks/libbrotlienc.1.dylib"; do | |
| install_name_tool -change @rpath/libbrotlicommon.1.dylib \ | |
| @loader_path/libbrotlicommon.1.dylib "$library" | |
| done | |
| rm -f stage/CyberSnapper.app/Contents/PlugIns/sqldrivers/libqsql{mimer,odbc,psql}.dylib | |
| codesign --force --deep --sign - stage/CyberSnapper.app | |
| - name: Build DMG and ZIP | |
| shell: bash | |
| run: | | |
| mkdir -p release-assets | |
| hdiutil create -volname CyberSnapper -srcfolder stage/CyberSnapper.app -ov -format UDZO \ | |
| "release-assets/CyberSnapper-macos-${{ matrix.arch }}.dmg" | |
| ditto -c -k --sequesterRsrc --keepParent stage/CyberSnapper.app \ | |
| "release-assets/CyberSnapper-macos-${{ matrix.arch }}.zip" | |
| - name: Smoke-test packaged capture | |
| shell: bash | |
| timeout-minutes: 15 | |
| run: | | |
| env -u QT_PLUGIN_PATH -u QML2_IMPORT_PATH node scripts/smoke-packaged-capture.mjs \ | |
| stage/CyberSnapper.app/Contents/MacOS/cybersnapper-cli \ | |
| stage/CyberSnapper.app/Contents/MacOS/cybersnapper-agent \ | |
| stage/CyberSnapper.app/Contents/Resources/worker/main.cjs \ | |
| stage/CyberSnapper.app/Contents/Resources/runtime/node \ | |
| stage/CyberSnapper.app/Contents/Resources/browsers \ | |
| build/native/smoke-state | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: CyberSnapper-macos-${{ matrix.arch }} | |
| path: release-assets/* | |
| if-no-files-found: error | |
| publish: | |
| name: Assemble and publish | |
| needs: [linux, windows, macos] | |
| if: github.event_name == 'release' || inputs.release_tag != '' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| id-token: write | |
| attestations: write | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| merge-multiple: true | |
| - name: Create SHA-256 checksums | |
| shell: bash | |
| run: (cd artifacts && sha256sum -- * > SHA256SUMS.txt) | |
| - name: Attest package provenance | |
| uses: actions/attest-build-provenance@v2 | |
| with: | |
| subject-path: artifacts/* | |
| - name: Publish packages to release | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| run: | | |
| set -euo pipefail | |
| tag="${{ github.event_name == 'release' && github.event.release.tag_name || inputs.release_tag }}" | |
| gh release upload "$tag" artifacts/* --clobber |