Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ on:
branches: [master, main]
paths:
- 'app/**'
- 'packages/**'
- '.github/workflows/**'
pull_request:
branches: [master, main]
paths:
- 'app/**'
- 'packages/**'
- '.github/workflows/**'
workflow_dispatch:

Expand Down
21 changes: 21 additions & 0 deletions .github/workflows/release-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Release configuration checks

on:
pull_request:
paths: ['tools/**', '.github/workflows/**', 'app/pubspec.yaml', 'app/android/**', 'packages/**']
push:
branches: [master]
paths: ['tools/**', '.github/workflows/**', 'app/pubspec.yaml', 'app/android/**', 'packages/**']

permissions:
contents: read

jobs:
checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: python -m unittest discover -s tools/tests -v
180 changes: 103 additions & 77 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,83 +2,115 @@ name: Release

on:
push:
tags:
- 'v*'
branches: [master]
paths: ['app/pubspec.yaml']
workflow_dispatch:

permissions:
contents: read

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

env:
FLUTTER_VERSION: '3.41.2'
PAPYRUS_API_BASE_URL: ${{ vars.PAPYRUS_API_BASE_URL }}
POWERSYNC_SERVICE_URL: ${{ vars.POWERSYNC_SERVICE_URL }}

jobs:
version:
name: Extract version
if: github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
outputs:
release: ${{ steps.version.outputs.release }}
version: ${{ steps.version.outputs.version }}
build_number: ${{ steps.version.outputs.build_number }}
tag: ${{ steps.version.outputs.tag }}
steps:
- name: Extract version from tag
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: python -m unittest discover -s tools/tests -v
- name: Check committed version
id: version
env:
BEFORE: ${{ github.event.before }}
EVENT_NAME: ${{ github.event_name }}
run: |
TAG=${GITHUB_REF#refs/tags/v}
echo "version=$TAG" >> $GITHUB_OUTPUT
echo "build_number=${{ github.run_number }}" >> $GITHUB_OUTPUT
if [ "$EVENT_NAME" = workflow_dispatch ]; then
python tools/release_gate.py client --manual
else
python tools/release_gate.py client --base "$BEFORE"
fi

build-android:
name: Build Android
needs: version
if: needs.version.outputs.release == 'true'
environment: release
runs-on: ubuntu-latest
env:
PAPYRUS_API_BASE_URL: ${{ vars.PAPYRUS_API_BASE_URL }}
POWERSYNC_SERVICE_URL: ${{ vars.POWERSYNC_SERVICE_URL }}
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
defaults:
run:
working-directory: app
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Java
uses: actions/setup-java@v4
- name: Set temporary keystore path
run: printf 'ANDROID_KEYSTORE_PATH=%s/upload.jks\n' "$RUNNER_TEMP" >> "$GITHUB_ENV"
Comment on lines +63 to +64

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Check out before entering the app working directory

Whenever the version gate enables build-android, this is the first run step, but the job-level default runs it from app/ and checkout does not occur until the following step. On a fresh GitHub-hosted runner that directory does not exist yet, so the shell cannot start and every Android release fails before restoring the keystore. Move checkout ahead of this step or override this step's working directory.

Useful? React with 👍 / 👎.

- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
distribution: temurin
java-version: '17'
cache: 'gradle'

- name: Setup Flutter
uses: subosito/flutter-action@v2
cache: gradle
- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true

- name: Validate release configuration
- name: Validate endpoints and restore upload key
env:
KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
run: |
test -n "${{ env.PAPYRUS_API_BASE_URL }}" || (echo "PAPYRUS_API_BASE_URL repository variable is required" && exit 1)
test -n "${{ env.POWERSYNC_SERVICE_URL }}" || (echo "POWERSYNC_SERVICE_URL repository variable is required" && exit 1)

- name: Install dependencies
run: flutter pub get

- name: Build APK
python ../tools/release_gate.py client --endpoints
test -n "$KEYSTORE_BASE64" && test -n "$ANDROID_KEYSTORE_PASSWORD" && test -n "$ANDROID_KEY_ALIAS" && test -n "$ANDROID_KEY_PASSWORD" || { echo "Configure the Android upload-key secrets in the release environment"; exit 1; }
printf '%s' "$KEYSTORE_BASE64" | base64 --decode > "$ANDROID_KEYSTORE_PATH"
chmod 600 "$ANDROID_KEYSTORE_PATH"
- name: Install locked dependencies
run: flutter pub get --enforce-lockfile
- name: Build signed Google Play bundle
run: |
flutter build apk --release \
--dart-define=PAPYRUS_API_BASE_URL="${{ env.PAPYRUS_API_BASE_URL }}" \
--dart-define=POWERSYNC_SERVICE_URL="${{ env.POWERSYNC_SERVICE_URL }}" \
--build-name=${{ needs.version.outputs.version }} \
--build-number=${{ needs.version.outputs.build_number }}

- name: Rename APK
flutter build appbundle --release \
--dart-define=PAPYRUS_API_BASE_URL="$PAPYRUS_API_BASE_URL" \
--dart-define=POWERSYNC_SERVICE_URL="$POWERSYNC_SERVICE_URL"
- name: Check 16 KB native and bundle alignment
run: |
mv build/app/outputs/flutter-apk/app-release.apk \
papyrus-android-v${{ needs.version.outputs.version }}.apk

- name: Upload artifact
uses: actions/upload-artifact@v4
python ../tools/check_android_bundle.py build/app/outputs/bundle/release/app-release.aab
curl --fail --location --retry 3 https://github.com/google/bundletool/releases/download/1.18.3/bundletool-all-1.18.3.jar --output "$RUNNER_TEMP/bundletool.jar"
printf 'a099cfa1543f55593bc2ed16a70a7c67fe54b1747bb7301f37fdfd6d91028e29 %s/bundletool.jar\n' "$RUNNER_TEMP" | sha256sum --check
java -jar "$RUNNER_TEMP/bundletool.jar" dump config --bundle=build/app/outputs/bundle/release/app-release.aab > "$RUNNER_TEMP/bundle-config.json"
python -c 'import json, os; from pathlib import Path; config = json.loads((Path(os.environ["RUNNER_TEMP"]) / "bundle-config.json").read_text()); assert config["optimizations"]["uncompressNativeLibraries"]["alignment"] == "PAGE_ALIGNMENT_16K", "Bundle must request 16 KB APK alignment"'
- uses: actions/upload-artifact@v4
with:
name: android-release
path: app/papyrus-android-v${{ needs.version.outputs.version }}.apk
path: app/build/app/outputs/bundle/release/app-release.aab
if-no-files-found: error
- name: Remove upload key
if: always()
run: rm -f "$ANDROID_KEYSTORE_PATH"

build-web:
name: Build Web
needs: version
if: needs.version.outputs.release == 'true'
environment: release
env:
PAPYRUS_API_BASE_URL: ${{ vars.PAPYRUS_API_BASE_URL }}
POWERSYNC_SERVICE_URL: ${{ vars.POWERSYNC_SERVICE_URL }}
runs-on: ubuntu-latest
defaults:
run:
Expand All @@ -94,18 +126,16 @@ jobs:
cache: true

- name: Validate release configuration
run: |
test -n "${{ env.PAPYRUS_API_BASE_URL }}" || (echo "PAPYRUS_API_BASE_URL repository variable is required" && exit 1)
test -n "${{ env.POWERSYNC_SERVICE_URL }}" || (echo "POWERSYNC_SERVICE_URL repository variable is required" && exit 1)
run: python ../tools/release_gate.py client --endpoints

- name: Install dependencies
run: flutter pub get
run: flutter pub get --enforce-lockfile

- name: Build web
run: |
flutter build web --release \
--dart-define=PAPYRUS_API_BASE_URL="${{ env.PAPYRUS_API_BASE_URL }}" \
--dart-define=POWERSYNC_SERVICE_URL="${{ env.POWERSYNC_SERVICE_URL }}" \
--dart-define=PAPYRUS_API_BASE_URL="${PAPYRUS_API_BASE_URL}" \
--dart-define=POWERSYNC_SERVICE_URL="${POWERSYNC_SERVICE_URL}" \
--build-name=${{ needs.version.outputs.version }} \
--build-number=${{ needs.version.outputs.build_number }}

Expand All @@ -123,6 +153,11 @@ jobs:
build-linux:
name: Build Linux
needs: version
if: needs.version.outputs.release == 'true'
environment: release
env:
PAPYRUS_API_BASE_URL: ${{ vars.PAPYRUS_API_BASE_URL }}
POWERSYNC_SERVICE_URL: ${{ vars.POWERSYNC_SERVICE_URL }}
runs-on: ubuntu-latest
defaults:
run:
Expand All @@ -143,18 +178,16 @@ jobs:
cache: true

- name: Validate release configuration
run: |
test -n "${{ env.PAPYRUS_API_BASE_URL }}" || (echo "PAPYRUS_API_BASE_URL repository variable is required" && exit 1)
test -n "${{ env.POWERSYNC_SERVICE_URL }}" || (echo "POWERSYNC_SERVICE_URL repository variable is required" && exit 1)
run: python ../tools/release_gate.py client --endpoints

- name: Install dependencies
run: flutter pub get
run: flutter pub get --enforce-lockfile

- name: Build Linux
run: |
flutter build linux --release \
--dart-define=PAPYRUS_API_BASE_URL="${{ env.PAPYRUS_API_BASE_URL }}" \
--dart-define=POWERSYNC_SERVICE_URL="${{ env.POWERSYNC_SERVICE_URL }}" \
--dart-define=PAPYRUS_API_BASE_URL="${PAPYRUS_API_BASE_URL}" \
--dart-define=POWERSYNC_SERVICE_URL="${POWERSYNC_SERVICE_URL}" \
--build-name=${{ needs.version.outputs.version }} \
--build-number=${{ needs.version.outputs.build_number }}

Expand All @@ -172,6 +205,11 @@ jobs:
build-windows:
name: Build Windows
needs: version
if: needs.version.outputs.release == 'true'
environment: release
env:
PAPYRUS_API_BASE_URL: ${{ vars.PAPYRUS_API_BASE_URL }}
POWERSYNC_SERVICE_URL: ${{ vars.POWERSYNC_SERVICE_URL }}
runs-on: windows-latest
defaults:
run:
Expand All @@ -187,18 +225,16 @@ jobs:
cache: true

- name: Validate release configuration
run: |
if (-not "${{ env.PAPYRUS_API_BASE_URL }}") { throw "PAPYRUS_API_BASE_URL repository variable is required" }
if (-not "${{ env.POWERSYNC_SERVICE_URL }}") { throw "POWERSYNC_SERVICE_URL repository variable is required" }
run: python ../tools/release_gate.py client --endpoints

- name: Install dependencies
run: flutter pub get
run: flutter pub get --enforce-lockfile

- name: Build Windows
run: |
flutter build windows --release `
--dart-define=PAPYRUS_API_BASE_URL="${{ env.PAPYRUS_API_BASE_URL }}" `
--dart-define=POWERSYNC_SERVICE_URL="${{ env.POWERSYNC_SERVICE_URL }}" `
--dart-define=PAPYRUS_API_BASE_URL="$env:PAPYRUS_API_BASE_URL" `
--dart-define=POWERSYNC_SERVICE_URL="$env:POWERSYNC_SERVICE_URL" `
--build-name=${{ needs.version.outputs.version }} `
--build-number=${{ needs.version.outputs.build_number }}

Expand All @@ -213,29 +249,19 @@ jobs:
path: app/papyrus-windows-v${{ needs.version.outputs.version }}.zip

release:
name: Create GitHub Release
needs: [version, build-android, build-web, build-linux, build-windows]
if: needs.version.outputs.release == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Download all artifacts
uses: actions/download-artifact@v4
- uses: actions/download-artifact@v4
with:
path: artifacts

- name: Create GitHub Release
uses: softprops/action-gh-release@v1
- uses: softprops/action-gh-release@v2
with:
name: Papyrus v${{ needs.version.outputs.version }}
tag_name: ${{ needs.version.outputs.tag }}
target_commitish: ${{ github.sha }}
name: Papyrus ${{ needs.version.outputs.tag }}
generate_release_notes: true
files: |
artifacts/android-release/*
artifacts/web-release/*
artifacts/linux-release/*
artifacts/windows-release/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
files: artifacts/**/*
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ test/data/
.idea/
# Developer-only reader overrides; release dependencies stay Git-pinned.
app/pubspec_overrides.yaml
__pycache__/
*.pyc
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,9 @@ Keep actionable bug reports and technical decisions in GitHub issues and pull re
| [website](https://github.com/PapyrusReader/website) | Landing page |
| [docs](https://github.com/PapyrusReader/docs) | Documentation |
| [papyrus](https://github.com/PapyrusReader/papyrus) | Development workspace |

## Google Play testing builds

See [the release guide](docs/RELEASING.md) for signed Android App Bundles,
version-triggered GitHub builds, required endpoint/signing settings and the first
internal testing upload.
2 changes: 2 additions & 0 deletions app/android/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ GeneratedPluginRegistrant.java
key.properties
**/*.keystore
**/*.jks

/build/
Loading
Loading