Files
5chan/.github/workflows/release.yml
T
plebeius 10cd99545e fix(ci): prevent partial releases when a build job fails
Restructure the release workflow so build jobs upload workflow artifacts
instead of creating GitHub releases directly. A single finalize-release
job downloads all artifacts and creates the release only when every
platform build succeeds. Also removes the unused gradle bundle step
from the Android job and adds retry logic to gradlew assembleRelease.
2026-03-10 12:21:37 +08:00

324 lines
11 KiB
YAML

# 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*'
env:
HUSKY: '0'
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
cache: 'yarn'
- name: Install dependencies
run: |
for i in 1 2 3; do
echo "yarn install attempt $i"
yarn install --frozen-lockfile --ignore-engines --network-timeout 100000 --network-concurrency 1 && 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
cache: 'yarn'
- name: Install dependencies
run: |
for i in 1 2 3; do
echo "yarn install attempt $i"
yarn install --frozen-lockfile --ignore-engines --network-timeout 100000 --network-concurrency 1 && 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
cp out/make/AppImage/**/*.AppImage release-assets/
- name: Create static HTML release archive
if: matrix.arch == 'x64'
run: |
VERSION=$(node -e "console.log(require('./package.json').version)")
HTML_ARCHIVE_DIR="5chan-html-$VERSION"
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
cache: 'yarn'
- 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 --frozen-lockfile --ignore-engines --network-timeout 100000 --network-concurrency 1 && 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: Rename DMG with architecture suffix
run: |
for f in out/make/*.dmg; do
[ -f "$f" ] || continue
base=$(basename "$f" .dmg)
mv "$f" "out/make/${base}-${{ matrix.arch }}.dmg"
done
- name: Stage release artifacts
run: |
mkdir -p release-assets
cp out/make/*.dmg release-assets/ 2>/dev/null || true
find out/make/zip -name '*.zip' -exec cp {} release-assets/ \; 2>/dev/null || true
- 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
cache: 'yarn'
- name: Install dependencies
shell: bash
run: |
for i in 1 2 3; do
echo "yarn install attempt $i"
yarn install --frozen-lockfile --ignore-engines --network-timeout 100000 --network-concurrency 1 && 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
cp out/make/squirrel.windows/x64/*.exe release-assets/
- 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
cache: 'yarn'
- 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 --frozen-lockfile --ignore-engines --network-timeout 100000 --network-concurrency 1 && 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
cache: 'yarn'
- name: Install dependencies
run: |
for i in 1 2 3; do
echo "yarn install attempt $i"
yarn install --frozen-lockfile --ignore-engines --network-timeout 100000 --network-concurrency 1 && 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"
token: ${{ secrets.GITHUB_TOKEN }}