# docs https://github.com/marketplace/actions/create-release # docs https://github.com/ncipollo/release-action # docs https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job#choosing-github-hosted-runners # # Architecture: build jobs upload workflow artifacts only. A single # finalize-release job creates the GitHub release with all assets + body. # If ANY build job fails, finalize-release is skipped and no release appears. name: release on: push: tags: - 'v*' jobs: preflight: runs-on: ubuntu-22.04 permissions: contents: read steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Node.js v22 uses: actions/setup-node@v4 with: node-version: 22 - run: corepack enable - name: Install dependencies run: | for i in 1 2 3; do echo "yarn install attempt $i" yarn install --immutable && break sleep 5 [ "$i" = "3" ] && exit 1 done - name: Run Vitest run: yarn test --run - name: Lint run: yarn lint - name: Type check run: yarn type-check - name: Build React app run: CI='' NODE_ENV=production yarn build - name: Run React Doctor run: yarn doctor - name: Install Chromium for smoke tests run: npx playwright install --with-deps chromium - name: Smoke test built web app run: node scripts/smoke-web-app.js --start-preview linux: strategy: fail-fast: false matrix: arch: [x64, arm64] runs-on: ubuntu-22.04 needs: preflight permissions: contents: read steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Node.js v22 uses: actions/setup-node@v4 with: node-version: 22 - run: corepack enable - name: Install dependencies run: | for i in 1 2 3; do echo "yarn install attempt $i" yarn install --immutable && break sleep 5 [ "$i" = "3" ] && exit 1 done - name: Download IPFS and set permissions run: BUILD_ARCH=${{ matrix.arch }} node electron/download-ipfs && sudo chmod +x bin/linux/ipfs - name: Build React app run: CI='' NODE_ENV=production yarn build - name: Build Electron app for Linux (arm64) if: matrix.arch == 'arm64' run: yarn electron:build:linux:arm64 - name: Build Electron app for Linux (x64) if: matrix.arch == 'x64' run: yarn electron:build:linux:x64 - name: Stage release artifacts run: | mkdir -p release-assets VERSION=$(node -e "console.log(require('./package.json').version)") APPIMAGE_PATH=$(find out/make/AppImage -name '*.AppImage' | head -n 1) [ -n "$APPIMAGE_PATH" ] || exit 1 cp "$APPIMAGE_PATH" "release-assets/5chan-${VERSION}-${{ matrix.arch }}.AppImage" - name: Create static HTML release archive if: matrix.arch == 'x64' run: | VERSION=$(node -e "console.log(require('./package.json').version)") HTML_ARCHIVE_DIR="5chan-$VERSION-html" rm -rf "$HTML_ARCHIVE_DIR" cp -R build "$HTML_ARCHIVE_DIR" zip -r "release-assets/${HTML_ARCHIVE_DIR}.zip" "$HTML_ARCHIVE_DIR" rm -rf "$HTML_ARCHIVE_DIR" - name: List release assets run: ls -la release-assets - uses: actions/upload-artifact@v4 with: name: release-linux-${{ matrix.arch }} path: release-assets/ mac: strategy: fail-fast: false matrix: include: - runner: macos-15-intel # Intel x64 arch: x64 - runner: macos-latest # Apple Silicon arm64 arch: arm64 runs-on: ${{ matrix.runner }} needs: preflight permissions: contents: read steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Node.js v22 uses: actions/setup-node@v4 with: node-version: 22 - run: corepack enable - name: Setup Python 3.12 uses: actions/setup-python@v5 with: python-version: '3.12' - run: pip install setuptools - name: Install dependencies run: | for i in 1 2 3; do echo "yarn install attempt $i" yarn install --immutable && break sleep 5 [ "$i" = "3" ] && exit 1 done - name: Download IPFS and set permissions run: node electron/download-ipfs && sudo chmod +x bin/mac/ipfs - name: Build React app run: CI='' NODE_ENV=production yarn build - name: Build Electron app for Mac env: CSC_IDENTITY_AUTO_DISCOVERY: 'false' run: | if [ "${{ matrix.arch }}" = "arm64" ]; then yarn electron:build:mac:arm64 else yarn electron:build:mac:x64 fi - name: Stage release artifacts run: | mkdir -p release-assets VERSION=$(node -e "console.log(require('./package.json').version)") DMG_PATH=$(find out/make -maxdepth 1 -name '*.dmg' | head -n 1) ZIP_PATH=$(find out/make/zip -name '*.zip' | head -n 1) [ -n "$DMG_PATH" ] || exit 1 [ -n "$ZIP_PATH" ] || exit 1 cp "$DMG_PATH" "release-assets/5chan-${VERSION}-${{ matrix.arch }}.dmg" cp "$ZIP_PATH" "release-assets/5chan-${VERSION}-${{ matrix.arch }}.zip" - name: List release assets run: ls -la release-assets - uses: actions/upload-artifact@v4 with: name: release-mac-${{ matrix.arch }} path: release-assets/ windows: runs-on: windows-2022 needs: preflight permissions: contents: read steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Node.js v22 uses: actions/setup-node@v4 with: node-version: 22 - run: corepack enable - name: Resolve Yarn cache path id: yarn-cache shell: bash run: echo "dir=$(corepack yarn config get cacheFolder)" >> "$GITHUB_OUTPUT" - name: Cache Yarn package tarballs uses: actions/cache@v4 with: path: ${{ steps.yarn-cache.outputs.dir }} key: ${{ runner.os }}-yarn-v1-${{ hashFiles('yarn.lock', 'package.json', '.yarnrc.yml') }} restore-keys: ${{ runner.os }}-yarn-v1- - name: Cache electron binaries uses: actions/cache@v4 with: path: ~/AppData/Local/electron/Cache key: ${{ runner.os }}-electron-v2-${{ hashFiles('**/yarn.lock') }} restore-keys: ${{ runner.os }}-electron-v2- - name: Install dependencies shell: bash run: | for i in 1 2 3; do echo "yarn install attempt $i" yarn install --immutable && break sleep 5 [ "$i" = "3" ] && exit 1 done - name: Build React app run: npx cross-env NODE_ENV=production yarn build - name: Build Electron app for Windows (x64) run: yarn electron:build:windows - name: Stage release artifacts shell: bash run: | mkdir -p release-assets VERSION=$(node -e "console.log(require('./package.json').version)") SETUP_EXE_PATH=$(find out/make -iname '*setup*.exe' | head -n 1) [ -n "$SETUP_EXE_PATH" ] || exit 1 cp "$SETUP_EXE_PATH" "release-assets/5chan-${VERSION}-x64.Setup.exe" - name: List release assets shell: bash run: ls -la release-assets - uses: actions/upload-artifact@v4 with: name: release-windows path: release-assets/ android: runs-on: ubuntu-22.04 needs: preflight permissions: contents: read steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: '17' - uses: actions/setup-node@v4 with: node-version: 22 - run: corepack enable - run: sudo apt-get install -y apksigner zipalign - name: Install dependencies run: | for i in 1 2 3; do echo "yarn install attempt $i" yarn install --immutable && break sleep 5 [ "$i" = "3" ] && exit 1 done - name: Build React app run: CI='' NODE_ENV=production yarn build - name: Set Android versionCode and versionName run: | sed -i "s/versionCode 1/versionCode $(git tag | wc -l)/" ./android/app/build.gradle sed -i "s/versionName \"1.0\"/versionName \"$(node -e "console.log(require('./package.json').version)")\"/" ./android/app/build.gradle cat ./android/app/build.gradle - name: Sync Capacitor run: npx cap sync android - name: Build APK run: | for i in 1 2 3; do echo "gradlew assembleRelease attempt $i" (cd android && ./gradlew assembleRelease --stacktrace) && break sleep 10 [ "$i" = "3" ] && exit 1 done - name: Optimize APK run: cd android/app/build/outputs/apk/release && zipalign 4 app-release-unsigned.apk app-release-unsigned-zip.apk - name: Sign APK run: cd android/app/build/outputs/apk/release && apksigner sign --ks ../../../../../plebbit.keystore --ks-pass pass:${{ secrets.PLEBBIT_REACT_KEYSTORE_PASSWORD }} --ks-key-alias release --out app-release-signed.apk app-release-unsigned-zip.apk - name: Stage release artifacts run: | mkdir -p release-assets VERSION=$(node -e "console.log(require('./package.json').version)") mv android/app/build/outputs/apk/release/app-release-signed.apk "release-assets/5chan-${VERSION}.apk" - name: List release assets run: ls -la release-assets - uses: actions/upload-artifact@v4 with: name: release-android path: release-assets/ finalize-release: runs-on: ubuntu-22.04 needs: [linux, mac, windows, android] permissions: contents: write steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Node.js v22 uses: actions/setup-node@v4 with: node-version: 22 - run: corepack enable - name: Install dependencies run: | for i in 1 2 3; do echo "yarn install attempt $i" yarn install --immutable && break sleep 5 [ "$i" = "3" ] && exit 1 done - name: Download all release artifacts uses: actions/download-artifact@v4 with: pattern: release-* path: release-assets merge-multiple: true - name: List all release assets run: ls -la release-assets - name: Populate dist for release-body script run: | mkdir -p dist cp release-assets/* dist/ - name: Generate release body env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_REPOSITORY: ${{ github.repository }} GITHUB_REF_NAME: ${{ github.ref_name }} run: node scripts/release-body > release-body.txt - name: Create GitHub release uses: ncipollo/release-action@v1 with: artifacts: 'release-assets/*' bodyFile: "release-body.txt" allowUpdates: true token: ${{ secrets.GITHUB_TOKEN }}