Skip to content

CI

CI #19

Workflow file for this run

name: CI
on:
pull_request:
workflow_dispatch:
inputs:
build_ios:
type: boolean
default: false
description: |
Whether or not to build for iOS.
This setting has no effect if `release` is true.
Note! iOS builds cost 10x more CI minutes since a mac runner is required.
release:
type: boolean
default: false
description: |
Create a new release.
This will create a preview release for non-main branches and a full release on `main`.
env:
FLUTTER_VERSION: 3.16.x
NODE_VERSION: 20
YQ_VERSION: v4.40.5
jobs:
calculate_version:
name: Calculate Version
runs-on: ubuntu-latest
outputs:
version_name: ${{ steps.version-name.outputs.version_name }}
version_code: ${{ steps.version-code.outputs.version_code }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- uses: chrisdickinson/setup-yq@latest
with:
yq-version: ${{ env.YQ_VERSION }}
- name: Add Current Branch to .releaserc.yml
if: ${{ !contains(fromJSON('["main"]'), github.ref_name) }}
run: |
yq --inplace '. *+ {"branches": [{"name": "${{ github.ref_name }}", "prerelease": "dev"}]}' .releaserc.yml
- name: Calculate Version Name
id: version-name
run: |
npm i semantic-release @semantic-release/commit-analyzer @semantic-release/release-notes-generator conventional-changelog-conventionalcommits @semantic-release/exec
yq --inplace '.plugins += [["@semantic-release/exec", {"verifyReleaseCmd": "echo \"version_name=${nextRelease.version}\" >> \"$GITHUB_OUTPUT\""}]]' .releaserc.yml
npx --yes semantic-release --dry-run ${{ runner.debug && '--debug' || '' }}
- name: Calculate Version Code
id: version-code
# Version code changes every minute.
# This should last us ~3960 years
run: echo "version_code=$(bc <<< "$(date -u +'%s') / 60 - 1705195260 / 60")" >> "$GITHUB_OUTPUT"
- name: Print Calculated Version
run: echo '${{ steps.version-name.outputs.version_name }}+${{ steps.version-code.outputs.version_code }}'
verify:
name: Verify
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: stable
cache: true
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Validate PR commits with commitlint
if: github.event_name == 'pull_request'
run: |
npm install commitlint conventional-changelog-conventionalcommits
npx commitlint --from ${{ github.event.pull_request.head.sha }}~${{ github.event.pull_request.commits }} --to ${{ github.event.pull_request.head.sha }} --verbose
- name: Validate code formatting
run: dart format --set-exit-if-changed .
- name: Perform code analysis
run: flutter analyze
- name: Run tests
run: flutter test
build_android:
name: Build Android
runs-on: ubuntu-latest
needs:
- calculate_version
- verify
env:
VERSION_NAME: ${{ needs.calculate_version.outputs.version_name }}
VERSION_CODE: ${{ needs.calculate_version.outputs.version_code }}
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: stable
cache: true
- uses: chrisdickinson/setup-yq@latest
with:
yq-version: ${{ env.YQ_VERSION }}
- name: Set Release Version
run: |
echo "Release Version: ${{ env.VERSION_NAME }}+${{ env.VERSION_CODE }}"
yq --inplace '.version = "${{ env.VERSION_NAME }}+${{ env.VERSION_CODE }}"' pubspec.yaml
- name: Create Production Build
run: |
flutter build appbundle --obfuscate --split-debug-info=build/app/debug-info
flutter build apk --obfuscate --split-per-abi --split-debug-info=build/app/debug-info
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
name: android-artifacts
path: |
build/app/debug-info/*
build/app/outputs/apk/release/*
build/app/outputs/bundle/release/*
build_ios:
name: Build iOS
# TODO(FelixZY): Need to configure code signing certificate before iOS can be built.
if: false && (inputs.release || inputs.build_ios)
runs-on: macos-latest
needs:
- calculate_version
- verify
env:
VERSION_NAME: ${{ needs.calculate_version.outputs.version_name }}
VERSION_CODE: ${{ needs.calculate_version.outputs.version_code }}
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: stable
cache: true
- uses: chrisdickinson/setup-yq@latest
with:
yq-version: ${{ env.YQ_VERSION }}
- name: Set Release Version
run: |
echo "Release Version: ${{ env.VERSION_NAME }}+${{ env.VERSION_CODE }}"
yq --inplace '.version = "${{ env.VERSION_NAME }}+${{ env.VERSION_CODE }}"' pubspec.yaml
- name: Install Dependencies
run: |
flutter precache --ios
flutter pub get
- name: Create Production Build
run: flutter build ipa --obfuscate --split-debug-info=build/ios/debug-info
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
name: ios-artifacts
path: build/ios
build_web:
name: Build Web
runs-on: ubuntu-latest
needs:
- calculate_version
- verify
env:
VERSION_NAME: ${{ needs.calculate_version.outputs.version_name }}
VERSION_CODE: ${{ needs.calculate_version.outputs.version_code }}
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: stable
cache: true
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Create Production Build
run: flutter build web
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
name: web-artifacts
path: build/web
release_web:
name: Create Web Deployment
if: inputs.release
runs-on: ubuntu-latest
needs:
- build_web
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
steps:
- name: Download Web Artifacts
uses: actions/download-artifact@v4
with:
name: web-artifacts
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install Vercel CLI
run: npm install vercel@latest
- name: Create Preview Deployment
if: github.ref_name != 'main'
run: |
vercel pull --yes --environment=preview --token=$VERCEL_TOKEN
vercel build --token=$VERCEL_TOKEN
vercel deploy --prebuilt --token=$VERCEL_TOKEN
- name: Create Production Deployment
if: github.ref_name == 'main'
run: |
vercel pull --yes --environment=production --token=$VERCEL_TOKEN
vercel build --token=$VERCEL_TOKEN
vercel deploy --prebuilt --prod --token=$VERCEL_TOKEN
release_github:
name: Create GitHub Release
if: inputs.release
runs-on: ubuntu-latest
needs:
- build_android
# TODO(FelixZY): Need to configure code signing certificate before iOS can be built.
#- build_ios
- release_web
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download Web Artifacts
uses: actions/download-artifact@v4
with:
name: web-artifacts
path: build/web
- name: Zip Web Artifacts
run: |
cd build
zip -r web.zip web/
- name: Download Android Artifacts
uses: actions/download-artifact@v4
with:
name: android-artifacts
path: build/android
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- uses: chrisdickinson/setup-yq@latest
with:
yq-version: ${{ env.YQ_VERSION }}
- name: Add Current Branch to .releaserc.yml
if: ${{ !contains(fromJSON('["main"]'), github.ref_name) }}
run: |
yq --inplace '. *+ {"branches": [{"name": "${{ github.ref_name }}", "prerelease": "dev"}]}' .releaserc.yml
- name: Create GitHub Release
id: semantic-release
run: |
npm i semantic-release @semantic-release/commit-analyzer @semantic-release/release-notes-generator conventional-changelog-conventionalcommits @semantic-release/github
yq --inplace '.plugins += [["@semantic-release/github", {"assets": [{"path": "build/web.zip", "label": "Web"}, {"path": "build/android/"}]}]]' .releaserc.yml
npx --yes semantic-release ${{ runner.debug && '--debug' || '' }}