ci: use standard C++20 coroutines for WGC helper #3
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: Build Electron App | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| inputs: | |
| arch: | |
| description: "macOS architecture to build" | |
| required: true | |
| default: "both" | |
| type: choice | |
| options: | |
| - arm64 | |
| - x64 | |
| - both | |
| release_tag: | |
| description: "Optional release tag to create or update, e.g. v1.5.0" | |
| required: false | |
| type: string | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: build-${{ github.ref_name }}-${{ github.event.inputs.release_tag || 'artifacts' }} | |
| cancel-in-progress: false | |
| jobs: | |
| build-windows: | |
| name: Windows installer | |
| runs-on: windows-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Cache caption assets | |
| uses: actions/cache@v4 | |
| with: | |
| path: caption-assets | |
| key: caption-assets-${{ hashFiles('scripts/fetch-caption-model.mjs') }} | |
| - name: Build Windows app | |
| run: npm run build:win | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Upload Windows installer | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: openscreen-windows | |
| path: release/**/Openscreen.Setup.*.exe | |
| if-no-files-found: error | |
| retention-days: 30 | |
| build-macos: | |
| name: macOS ${{ matrix.arch }} DMG | |
| runs-on: macos-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| arch: ${{ fromJSON((github.event_name == 'workflow_dispatch' && github.event.inputs.arch != 'both') && format('["{0}"]', github.event.inputs.arch) || '["arm64", "x64"]') }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Ensure sharp prebuilt | |
| run: npm rebuild sharp | |
| env: | |
| npm_config_build_from_source: "false" | |
| - name: Cache caption assets | |
| uses: actions/cache@v4 | |
| with: | |
| path: caption-assets | |
| key: caption-assets-${{ hashFiles('scripts/fetch-caption-model.mjs') }} | |
| - name: Resolve macOS signing | |
| id: signing | |
| env: | |
| MAC_CERTIFICATE_P12: ${{ secrets.MAC_CERTIFICATE_P12 }} | |
| MAC_CERTIFICATE_PASSWORD: ${{ secrets.MAC_CERTIFICATE_PASSWORD }} | |
| MAC_CSC_NAME: ${{ secrets.MAC_CSC_NAME }} | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} | |
| run: | | |
| if [[ -n "$MAC_CERTIFICATE_P12" && -n "$MAC_CERTIFICATE_PASSWORD" && -n "$MAC_CSC_NAME" && -n "$APPLE_ID" && -n "$APPLE_TEAM_ID" && -n "$APPLE_APP_SPECIFIC_PASSWORD" ]]; then | |
| echo "enabled=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "enabled=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Import code signing certificate | |
| if: steps.signing.outputs.enabled == 'true' | |
| env: | |
| MAC_CERTIFICATE_P12: ${{ secrets.MAC_CERTIFICATE_P12 }} | |
| MAC_CERTIFICATE_PASSWORD: ${{ secrets.MAC_CERTIFICATE_PASSWORD }} | |
| run: | | |
| KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain-db" | |
| KEYCHAIN_PASSWORD="$(openssl rand -base64 32)" | |
| security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" | |
| security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| echo "$MAC_CERTIFICATE_P12" | base64 --decode > "$RUNNER_TEMP/certificate.p12" | |
| security import "$RUNNER_TEMP/certificate.p12" \ | |
| -k "$KEYCHAIN_PATH" \ | |
| -P "$MAC_CERTIFICATE_PASSWORD" \ | |
| -T /usr/bin/codesign \ | |
| -T /usr/bin/security | |
| security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" | |
| security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"') | |
| security find-identity -v -p codesigning "$KEYCHAIN_PATH" | |
| rm -f "$RUNNER_TEMP/certificate.p12" | |
| - name: Build Vite + Electron | |
| run: npx tsc && npx vite build | |
| - name: Build native macOS helpers | |
| run: npm run build:native:mac | |
| env: | |
| OPENSCREEN_MAC_HELPER_ARCHS: ${{ matrix.arch }} | |
| - name: Package .app bundle | |
| run: npx electron-builder --mac --${{ matrix.arch }} --dir | |
| env: | |
| CSC_NAME: ${{ secrets.MAC_CSC_NAME }} | |
| CSC_IDENTITY_AUTO_DISCOVERY: ${{ steps.signing.outputs.enabled == 'true' && 'true' || 'false' }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Get version | |
| id: version | |
| run: echo "version=$(node -p 'require(\"./package.json\").version')" >> "$GITHUB_OUTPUT" | |
| - name: Find .app bundle | |
| id: find_app | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| APP_BUNDLE="$(find "release/${VERSION}" -maxdepth 4 -name "*.app" -type d | head -n1)" | |
| if [[ -z "$APP_BUNDLE" ]]; then | |
| echo "::error::No .app bundle found in release/${VERSION}/" | |
| find "release/${VERSION}" -maxdepth 4 -print || true | |
| exit 1 | |
| fi | |
| echo "app_bundle=$APP_BUNDLE" >> "$GITHUB_OUTPUT" | |
| - name: Verify .app code signature | |
| if: steps.signing.outputs.enabled == 'true' | |
| run: codesign --verify --deep --strict "${{ steps.find_app.outputs.app_bundle }}" | |
| - name: Create DMG | |
| id: dmg | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| ARCH="${{ matrix.arch }}" | |
| DMG_NAME="Openscreen-Mac-${ARCH}-${VERSION}.dmg" | |
| RELEASE_DIR="release/${VERSION}" | |
| DMG_OUTPUT="${RELEASE_DIR}/${DMG_NAME}" | |
| STAGING="${RELEASE_DIR}/dmg-staging" | |
| rm -rf "$STAGING" | |
| rm -f "$DMG_OUTPUT" | |
| mkdir -p "$STAGING" | |
| cp -R "${{ steps.find_app.outputs.app_bundle }}" "$STAGING/" | |
| ln -s /Applications "$STAGING/Applications" | |
| hdiutil create \ | |
| -srcfolder "$STAGING" \ | |
| -volname "Openscreen" \ | |
| -fs HFS+ \ | |
| -fsargs "-c c=64,a=16,e=16" \ | |
| -format UDBZ \ | |
| "$DMG_OUTPUT" | |
| rm -rf "$STAGING" | |
| echo "dmg_path=$DMG_OUTPUT" >> "$GITHUB_OUTPUT" | |
| - name: Sign DMG | |
| if: steps.signing.outputs.enabled == 'true' | |
| run: | | |
| codesign --force \ | |
| --sign "${{ secrets.MAC_CSC_NAME }}" \ | |
| --timestamp \ | |
| "${{ steps.dmg.outputs.dmg_path }}" | |
| - name: Notarize DMG | |
| if: steps.signing.outputs.enabled == 'true' | |
| run: | | |
| xcrun notarytool submit "${{ steps.dmg.outputs.dmg_path }}" \ | |
| --apple-id "${{ secrets.APPLE_ID }}" \ | |
| --team-id "${{ secrets.APPLE_TEAM_ID }}" \ | |
| --password "${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}" \ | |
| --wait | |
| timeout-minutes: 15 | |
| - name: Staple notarization ticket | |
| if: steps.signing.outputs.enabled == 'true' | |
| run: xcrun stapler staple "${{ steps.dmg.outputs.dmg_path }}" | |
| - name: Validate stapled DMG | |
| if: steps.signing.outputs.enabled == 'true' | |
| run: | | |
| xcrun stapler validate "${{ steps.dmg.outputs.dmg_path }}" | |
| spctl -a -vv -t install "${{ steps.dmg.outputs.dmg_path }}" | |
| - name: Upload macOS DMG | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: openscreen-mac-${{ matrix.arch }} | |
| path: ${{ steps.dmg.outputs.dmg_path }} | |
| if-no-files-found: error | |
| retention-days: 30 | |
| - name: Cleanup keychain | |
| if: always() && steps.signing.outputs.enabled == 'true' | |
| run: security delete-keychain "$RUNNER_TEMP/build.keychain-db" || true | |
| build-linux: | |
| name: Linux packages | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Install pacman build dependencies | |
| run: sudo apt-get update && sudo apt-get install -y libarchive-tools | |
| - name: Cache caption assets | |
| uses: actions/cache@v4 | |
| with: | |
| path: caption-assets | |
| key: caption-assets-${{ hashFiles('scripts/fetch-caption-model.mjs') }} | |
| - name: Build Linux app | |
| run: npm run build:linux | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Upload Linux packages | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: openscreen-linux | |
| path: | | |
| release/**/*.AppImage | |
| release/**/*.zsync | |
| release/**/*.deb | |
| release/**/*.pacman | |
| if-no-files-found: error | |
| retention-days: 30 | |
| publish-release: | |
| name: Publish GitHub release | |
| runs-on: ubuntu-latest | |
| needs: | |
| - build-windows | |
| - build-macos | |
| - build-linux | |
| if: ${{ (github.event_name == 'push' && github.ref_type == 'tag') || (github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag != '') }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Resolve release tag | |
| id: release | |
| env: | |
| INPUT_TAG: ${{ github.event.inputs.release_tag }} | |
| run: | | |
| if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then | |
| TAG="${GITHUB_REF_NAME}" | |
| else | |
| TAG="${INPUT_TAG}" | |
| fi | |
| if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "::error::Release tag must look like v1.5.0; got '${TAG}'" | |
| exit 1 | |
| fi | |
| VERSION="${TAG#v}" | |
| PACKAGE_VERSION="$(node -p 'require("./package.json").version')" | |
| if [[ "$PACKAGE_VERSION" != "$VERSION" ]]; then | |
| echo "::error::package.json version ${PACKAGE_VERSION} does not match release tag ${TAG}" | |
| exit 1 | |
| fi | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Download installers | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Publish release assets | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| TAG: ${{ steps.release.outputs.tag }} | |
| run: | | |
| mapfile -t FILES < <(find artifacts -type f | sort) | |
| if [[ "${#FILES[@]}" -eq 0 ]]; then | |
| echo "::error::No installer artifacts were downloaded" | |
| exit 1 | |
| fi | |
| if gh release view "$TAG" >/dev/null 2>&1; then | |
| gh release upload "$TAG" "${FILES[@]}" --clobber | |
| else | |
| gh release create "$TAG" "${FILES[@]}" \ | |
| --target "$GITHUB_SHA" \ | |
| --title "$TAG" \ | |
| --generate-notes | |
| fi |