Instruction file imported from Notalib/flutter_readium (
.github/instructions/ci.instructions.md). Copyright stays with the author.
CI/CD Conventions
Caching
All workflows that build iOS or Android must include the relevant caches:
- Android: Cache
~/.gradle/cachesand~/.gradle/wrapper, keyed on the relevantbuild.gradlefiles. Include arestore-keysprefix fallback. - iOS (CocoaPods): Cache
flutter_readium/example/ios/Podsand~/.cocoapods, keyed onPodfile.lock.~/.cocoapodsis required — it holds the trunk spec repo used to resolve Flutter ecosystem pods (e.g. webview_flutter). Without it, a coldpod installfails even though Readium pods use explicitpodspec:URLs. - iOS (Xcode derived data): Cache
~/Library/Developer/Xcode/DerivedData, keyed onPodfile.lock. - Android emulator AVD: Cache
~/.android/avd/*and~/.android/adb*, keyed on API level.
Cache keys must end with a trailing dash before the hash segment (e.g. gradle-${{ runner.os }}-) to prevent accidental prefix collisions between keys that share a common prefix.
CocoaPods --repo-update
Only pass --repo-update when the CocoaPods cache is cold (cache miss). On a cache hit the spec repo is already in ~/.cocoapods and the flag just wastes ~30s. Use the cache-hit output from the pods cache step.
Permissions
The release workflow needs permissions: contents: write on the job to allow softprops/action-gh-release to create releases. This is already the minimum — GitHub does not offer a narrower "releases only" scope. Also set fetch-depth: 0 on the checkout step so all tags are available (needed for the manual-dispatch tag verification).
Release workflow: manual dispatch
The release workflow supports both tag-push and workflow_dispatch triggers. On manual dispatch, GITHUB_REF_NAME is the branch name, not a tag. The version must be read from flutter_readium/pubspec.yaml and the corresponding tag verified to exist via git rev-parse.
Release preparation
Use bin/release_prep <version> before tagging. It:
- Bumps
version:in both pubspec files - Moves
## Unreleasedcontent into a dated## [x.y.z] - YYYY-MM-DDsection - Leaves a fresh
## Unreleasedheader
Do not automate this in the CI pipeline — the changelog rewrite should be in the tagged commit itself, not a commit pushed by the workflow after tagging.
dart format and pub get ordering
analysis_options.base.yaml sets formatter: page_width: 120. dart format only reads this setting if it can resolve the flutter_lints include — which requires pub get to have been run first. Without pub get, the formatter silently falls back to 80-char page width and produces different (narrower) output.
Always run flutter pub get before dart format, both locally and in CI. bin/format does this automatically. In CI workflows, the existing Install dependencies step runs flutter pub get before the Check formatting step — preserve that ordering.
Flutter version pinning
All workflows pin Flutter via .flutter-version (the fvm version file). Do not use flutter-version-file: in subosito/flutter-action — that input treats the version as a semver constraint and resolves to the latest matching stable, not an exact pin. Instead, read the file in a dedicated step and pass the result as flutter-version::
- name: Read Flutter version
id: flutter_version
run: echo "version=$(cat .flutter-version)" >> "$GITHUB_OUTPUT"
- uses: subosito/flutter-action@<sha> # v2.x
with:
flutter-version: ${{ steps.flutter_version.outputs.version }}
cache: true
pub-cache: true
Omit channel: when an exact version is passed — it's inferred and channel: stable in combination with flutter-version-file: was observed to override the pinned version with latest stable.
Integration vs build workflows
flutter test integration_test performs its own build internally (targeting the emulator/simulator) and cannot consume a pre-built APK/app from the build workflows. Don't couple the integration-test workflow to the build workflows — they serve different purposes. Share only caching patterns.