Release #20
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| tags: | |
| # Numeric semver tags only (a trailing suffix like -beta is allowed). | |
| - 'v[0-9]+.[0-9]+.[0-9]+*' | |
| workflow_dispatch: | |
| # Two-stage gate: | |
| # verify — validations + advisory dry-runs. NO id-token, NO environment: it is | |
| # structurally incapable of publishing. Always runs on a tag. | |
| # publish — runs only after `verify` succeeds AND a required reviewer approves | |
| # the `pub.dev` environment (which is also what puts the `environment` | |
| # claim pub.dev requires into the OIDC token). | |
| # | |
| # `verify` runs strict `--dry-run` gates on a clean tree (no materialize), so a | |
| # real packaging error fails before approval. `publish` uses `dart pub publish | |
| # --force` because its publish-time mutations — materializing analysis_options | |
| # (for pana) and removing the local dependency override — leave a dirty tree | |
| # that `--dry-run` would reject (exit 65) but `--force` tolerates (no errors, | |
| # only the dirty-tree warning). | |
| jobs: | |
| verify: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Read Flutter version | |
| id: flutter_version | |
| run: echo "version=$(cat .flutter-version)" >> "$GITHUB_OUTPUT" | |
| - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0 | |
| with: | |
| flutter-version: ${{ steps.flutter_version.outputs.version }} | |
| cache: true | |
| pub-cache: true | |
| - name: Determine release version | |
| id: tag | |
| run: | | |
| if [[ "$GITHUB_REF_NAME" == v* ]]; then | |
| echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" | |
| else | |
| VERSION=$(grep '^version:' flutter_readium/pubspec.yaml | awk '{print $2}') | |
| if ! git rev-parse "refs/tags/v$VERSION" >/dev/null 2>&1; then | |
| echo "ERROR: Tag v$VERSION does not exist. Push the tag before running the release workflow manually." | |
| exit 1 | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Verify tag matches flutter_readium pubspec version | |
| run: | | |
| PUBSPEC_VERSION=$(grep '^version:' flutter_readium/pubspec.yaml | awk '{print $2}') | |
| if [ "$PUBSPEC_VERSION" != "${{ steps.tag.outputs.version }}" ]; then | |
| echo "ERROR: Tag version ${{ steps.tag.outputs.version }} does not match flutter_readium pubspec version $PUBSPEC_VERSION" | |
| exit 1 | |
| fi | |
| - name: Verify tag matches platform interface pubspec version | |
| run: | | |
| PUBSPEC_VERSION=$(grep '^version:' flutter_readium_platform_interface/pubspec.yaml | awk '{print $2}') | |
| if [ "$PUBSPEC_VERSION" != "${{ steps.tag.outputs.version }}" ]; then | |
| echo "ERROR: Tag version ${{ steps.tag.outputs.version }} does not match flutter_readium_platform_interface pubspec version $PUBSPEC_VERSION" | |
| exit 1 | |
| fi | |
| - name: Verify CHANGELOG entry in flutter_readium | |
| run: | | |
| if ! grep -q "## \[${{ steps.tag.outputs.version }}\]" flutter_readium/CHANGELOG.md; then | |
| echo "ERROR: No CHANGELOG entry for version ${{ steps.tag.outputs.version }} in flutter_readium/CHANGELOG.md" | |
| exit 1 | |
| fi | |
| - name: Verify CHANGELOG entry in platform interface | |
| run: | | |
| if ! grep -q "## \[${{ steps.tag.outputs.version }}\]" flutter_readium_platform_interface/CHANGELOG.md; then | |
| echo "ERROR: No CHANGELOG entry for version ${{ steps.tag.outputs.version }} in flutter_readium_platform_interface/CHANGELOG.md" | |
| exit 1 | |
| fi | |
| - name: Install dependencies (platform interface) | |
| run: flutter pub get | |
| working-directory: flutter_readium_platform_interface | |
| - name: Install dependencies (flutter_readium) | |
| run: flutter pub get | |
| working-directory: flutter_readium | |
| # Build gitignored web assets so the dry-run exercises the actual packaged | |
| # contents (catches missing JS/CSS before approval). | |
| - name: Build webview helper assets | |
| uses: ./.github/actions/build-webview-helpers | |
| - name: Build web reader bundle | |
| uses: ./.github/actions/build-web-reader-bundle | |
| - name: Verify built web assets exist | |
| run: | | |
| for f in \ | |
| flutter_readium/assets/helpers/flutterReadiumTools.js \ | |
| flutter_readium/assets/helpers/flutterReadiumTools.css \ | |
| flutter_readium/lib/helpers/readiumReader.js; do | |
| if [ ! -f "$f" ]; then | |
| echo "ERROR: Expected asset not found: $f" | |
| exit 1 | |
| fi | |
| done | |
| # Strict gate: packaging errors fail here, before the approval step. Runs on | |
| # the clean committed tree (no materialize), so the only non-error note is | |
| # the plugin's dependency-override hint, which does not fail a dry-run. | |
| - name: Dry-run publish platform interface | |
| run: dart pub publish --dry-run | |
| working-directory: flutter_readium_platform_interface | |
| - name: Dry-run publish flutter_readium | |
| run: dart pub publish --dry-run | |
| working-directory: flutter_readium | |
| publish: | |
| needs: verify | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| # Required-reviewer gate + the OIDC `environment` claim pub.dev requires. | |
| environment: pub.dev | |
| permissions: | |
| contents: write | |
| id-token: write # OIDC token for pub.dev automated publishing | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Read Flutter version | |
| id: flutter_version | |
| run: echo "version=$(cat .flutter-version)" >> "$GITHUB_OUTPUT" | |
| # Provisions the pub.dev OIDC token (requires the job's id-token: write). | |
| # `dart pub publish` does NOT fetch the token itself — without this step it | |
| # falls back to interactive OAuth and hangs in CI. Runs BEFORE flutter-action | |
| # so flutter's `dart` ends up first on PATH for the publish, while the token | |
| # configured here is still used (mirrors dart-lang's reusable publish.yml). | |
| - uses: dart-lang/setup-dart@v1.8.1 | |
| - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0 | |
| with: | |
| flutter-version: ${{ steps.flutter_version.outputs.version }} | |
| cache: true | |
| pub-cache: true | |
| - name: Determine release version | |
| id: tag | |
| run: | | |
| if [[ "$GITHUB_REF_NAME" == v* ]]; then | |
| echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" | |
| echo "tag_name=$GITHUB_REF_NAME" >> "$GITHUB_OUTPUT" | |
| else | |
| VERSION=$(grep '^version:' flutter_readium/pubspec.yaml | awk '{print $2}') | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "tag_name=v$VERSION" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Install dependencies (platform interface) | |
| run: flutter pub get | |
| working-directory: flutter_readium_platform_interface | |
| # The committed analysis_options.yaml shares config via a cross-package | |
| # `include: ../analysis_options.base.yaml`. That path is NOT in the published | |
| # tarball, so pub.dev's pana would lose the formatter/linter config and dock | |
| # the score. Inline the effective config just before publishing. This leaves | |
| # a dirty tree, which `--force` (below) tolerates. | |
| - name: Materialize self-contained analysis_options (platform interface) | |
| run: | | |
| gen="$(mktemp)" | |
| bin/materialize_analysis_options flutter_readium_platform_interface > "$gen" | |
| mv "$gen" flutter_readium_platform_interface/analysis_options.yaml | |
| # Authenticated via the job's OIDC token (id-token: write + environment). | |
| # Idempotent: skip if this version is already on pub.dev, so a re-run after | |
| # a partial failure (e.g. the plugin step) doesn't error on "already exists". | |
| - name: Publish platform interface | |
| run: | | |
| VERSION="${{ steps.tag.outputs.version }}" | |
| if curl -sf "https://pub.dev/api/packages/flutter_readium_platform_interface" | grep -q "\"version\":\"$VERSION\""; then | |
| echo "platform interface $VERSION already published — skipping" | |
| else | |
| dart pub publish --force | |
| fi | |
| working-directory: flutter_readium_platform_interface | |
| # Publish the plugin against the REAL published interface, exactly as | |
| # consumers resolve it. The committed pubspec_overrides.yaml points the | |
| # interface at the in-repo path — great for local dev, but if left here the | |
| # publish would resolve locally and never exercise the published `^X.Y.Z` | |
| # constraint. | |
| - name: Remove local dependency override (resolve interface from pub.dev) | |
| run: rm -f pubspec_overrides.yaml | |
| working-directory: flutter_readium | |
| # The just-published interface version can lag in pub's RESOLVER even after | |
| # it appears in the package API, so retry `pub get` until it resolves. | |
| - name: Install dependencies (flutter_readium, retry until interface resolves) | |
| run: | | |
| for i in $(seq 1 20); do | |
| if flutter pub get; then exit 0; fi | |
| echo "pub get failed — interface ${{ steps.tag.outputs.version }} not resolvable yet; retry $i…" | |
| sleep 15 | |
| done | |
| echo "ERROR: could not resolve flutter_readium_platform_interface after retries" | |
| exit 1 | |
| working-directory: flutter_readium | |
| - name: Build webview helper script | |
| uses: ./.github/actions/build-webview-helpers | |
| - name: Build web reader bundle | |
| uses: ./.github/actions/build-web-reader-bundle | |
| - name: Materialize self-contained analysis_options (flutter_readium) | |
| run: | | |
| gen="$(mktemp)" | |
| bin/materialize_analysis_options flutter_readium > "$gen" | |
| mv "$gen" flutter_readium/analysis_options.yaml | |
| - name: Publish flutter_readium | |
| run: | | |
| VERSION="${{ steps.tag.outputs.version }}" | |
| if curl -sf "https://pub.dev/api/packages/flutter_readium" | grep -q "\"version\":\"$VERSION\""; then | |
| echo "flutter_readium $VERSION already published — skipping" | |
| else | |
| dart pub publish --force | |
| fi | |
| working-directory: flutter_readium | |
| - name: Extract changelog section for this release | |
| id: changelog | |
| run: | | |
| VERSION="${{ steps.tag.outputs.version }}" | |
| # Extract block from "## [VERSION]" up to (but not including) the next "## [" | |
| BODY=$(awk "/^## \[$VERSION\]/{found=1; next} found && /^## \[/{exit} found{print}" flutter_readium/CHANGELOG.md) | |
| # Store multiline output | |
| { | |
| echo "body<<EOF" | |
| echo "$BODY" | |
| echo "EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Create draft GitHub release | |
| uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3 | |
| with: | |
| draft: true | |
| tag_name: ${{ steps.tag.outputs.tag_name }} | |
| name: ${{ steps.tag.outputs.tag_name }} | |
| body: ${{ steps.changelog.outputs.body }} |