refactor: extract inline shell tasks to mise-tasks file-tasks; add shellcheck + shfmt #246
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: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| id-token: write | |
| pull-requests: write | |
| deployments: write | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} | |
| jobs: | |
| # Detect which files have changed | |
| changes: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| app: ${{ steps.filter.outputs.app }} | |
| functions: ${{ steps.filter.outputs.functions }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Check for file changes | |
| uses: dorny/paths-filter@v3 | |
| id: filter | |
| with: | |
| filters: | | |
| app: | |
| - 'lib/**' | |
| - 'web/**' | |
| - 'pubspec.yaml' | |
| - 'test/**' | |
| - 'test-e2e/**' | |
| - 'package.json' | |
| - 'playwright.config.ts' | |
| - 'android/**' | |
| - '.github/workflows/build-deploy.yml' | |
| - 'mise.toml' | |
| functions: | |
| - 'functions/**' | |
| # Run tests once before building | |
| test: | |
| needs: changes | |
| runs-on: ubuntu-latest | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.app == 'true' || | |
| needs.changes.outputs.functions == 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Flutter App | |
| uses: ./.github/actions/setup-flutter-app | |
| with: | |
| google-services-json: ${{ secrets.GOOGLE_SERVICES_JSON }} | |
| generate-mocks: 'true' | |
| - name: Check formatting | |
| run: | | |
| dart format --output=none --set-exit-if-changed . | |
| npm ci | |
| npx prettier --check "**/*.{js,ts,mjs}" | |
| - name: Analyze code | |
| run: flutter analyze --no-fatal-infos | |
| - name: Run tests with coverage | |
| run: flutter test --coverage | |
| - name: Install lcov | |
| run: sudo apt-get update && sudo apt-get install -y lcov | |
| - name: Report code coverage | |
| uses: zgosalvez/github-actions-report-lcov@v4 | |
| with: | |
| coverage-files: coverage/lcov.info | |
| # Updated: Coverage threshold increased from 25% to 70% | |
| # Current coverage: 72%+ with comprehensive test improvements | |
| minimum-coverage: 70 | |
| artifact-name: code-coverage-report | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| update-comment: true | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v5 | |
| with: | |
| files: coverage/lcov.info | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| fail_ci_if_error: false | |
| - name: Test Pages Functions | |
| run: | | |
| cd functions | |
| npm ci | |
| npm test | |
| # Build web after tests pass | |
| build-web: | |
| needs: [changes, test] | |
| runs-on: ubuntu-latest | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.app == 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Flutter App | |
| uses: ./.github/actions/setup-flutter-app | |
| with: | |
| google-services-json: ${{ secrets.GOOGLE_SERVICES_JSON }} | |
| - name: Get git version info | |
| id: git_version | |
| run: scripts/get_version_info.sh github >> $GITHUB_OUTPUT | |
| - name: Build web | |
| run: | | |
| flutter build web --release --base-href "/" \ | |
| --dart-define=GIT_TAG=${{ steps.git_version.outputs.git_tag }} \ | |
| --dart-define=GIT_COMMIT=${{ steps.git_version.outputs.git_commit }} \ | |
| --dart-define=GIT_BRANCH=${{ steps.git_version.outputs.git_branch }} \ | |
| --dart-define=BUILD_VERSION=${{ steps.git_version.outputs.version }} \ | |
| --dart-define=BUILD_TIME=${{ steps.git_version.outputs.build_time }} | |
| - name: Upload build artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: web-build | |
| path: build/web | |
| # Run E2E tests on web build | |
| test-e2e-web: | |
| needs: build-web | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| cache: 'npm' | |
| - name: Download web build artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: web-build | |
| path: build/web | |
| - name: Install npm dependencies | |
| run: npm ci | |
| - name: Install Playwright browsers | |
| run: npx playwright install --with-deps chromium | |
| - name: Start http-server in background | |
| run: | | |
| npx http-server build/web -p 8080 -c-1 -a 127.0.0.1 --proxy http://127.0.0.1:8080? > /dev/null 2>&1 & | |
| echo $! > .http-server.pid | |
| - name: Wait for server to be ready | |
| run: | | |
| timeout 30 bash -c 'until curl -s http://127.0.0.1:8080 > /dev/null; do sleep 1; done' || exit 1 | |
| # Additional wait to ensure Flutter app fully initializes | |
| sleep 3 | |
| # Verify server responds to festival route (tests SPA routing) | |
| curl -f -s http://127.0.0.1:8080/cbf2025 > /dev/null || (echo "SPA routing not working" && exit 1) | |
| - name: Run Playwright tests | |
| run: npx playwright test | |
| - name: Upload Playwright report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: playwright-report | |
| path: playwright-report/ | |
| retention-days: 7 | |
| - name: Stop http-server | |
| if: always() | |
| run: | | |
| if [ -f .http-server.pid ]; then | |
| kill $(cat .http-server.pid) || true | |
| rm .http-server.pid | |
| fi | |
| # Build Android after tests pass | |
| build-android: | |
| needs: [changes, test] | |
| runs-on: ubuntu-latest | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.app == 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Set up JDK 17 | |
| uses: actions/setup-java@v4 | |
| with: | |
| java-version: '17' | |
| distribution: 'temurin' | |
| - name: Cache Gradle dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.gradle/caches | |
| ~/.gradle/wrapper | |
| key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} | |
| restore-keys: | | |
| ${{ runner.os }}-gradle- | |
| - name: Setup Flutter App | |
| uses: ./.github/actions/setup-flutter-app | |
| with: | |
| google-services-json: ${{ secrets.GOOGLE_SERVICES_JSON }} | |
| - name: Get git version info | |
| id: git_version | |
| run: scripts/get_version_info.sh github >> $GITHUB_OUTPUT | |
| - name: Build release APK (debug-signed) | |
| # Build with release config (enabling R8 minification) so ProGuard/R8 | |
| # issues are caught on every PR. key.properties is absent here, so | |
| # build.gradle automatically falls back to the debug signing config. | |
| run: | | |
| flutter build apk --release \ | |
| --dart-define=GIT_TAG=${{ steps.git_version.outputs.git_tag }} \ | |
| --dart-define=GIT_COMMIT=${{ steps.git_version.outputs.git_commit }} \ | |
| --dart-define=GIT_BRANCH=${{ steps.git_version.outputs.git_branch }} \ | |
| --dart-define=BUILD_VERSION=${{ steps.git_version.outputs.version }} \ | |
| --dart-define=BUILD_TIME=${{ steps.git_version.outputs.build_time }} | |
| - name: Upload release APK | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: app-release-apk | |
| path: build/app/outputs/flutter-apk/app-release.apk | |
| if-no-files-found: error | |
| deploy-web-preview: | |
| needs: [changes, build-web, test-e2e-web] | |
| runs-on: ubuntu-latest | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| needs.changes.outputs.app == 'true' || | |
| needs.changes.outputs.functions == 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Download build artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: web-build | |
| path: build/web | |
| - name: Deploy to Cloudflare Pages (Staging/PR Previews) | |
| id: deploy | |
| uses: cloudflare/wrangler-action@v3 | |
| with: | |
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| command: pages deploy build/web --project-name=staging-cambeerfestival --branch=${{ github.head_ref || github.ref_name }} | |
| - name: Comment PR with Preview URL | |
| if: github.event_name == 'pull_request' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const aliasUrl = '${{ steps.deploy.outputs.pages-deployment-alias-url }}'; | |
| const deployUrl = '${{ steps.deploy.outputs.deployment-url }}'; | |
| const previewUrl = aliasUrl || deployUrl; | |
| const comment = `## 🚀 Cloudflare Pages Preview | |
| Your preview deployment is ready! | |
| **Preview URL**: ${previewUrl} | |
| This preview will be automatically updated when you push new commits to this PR.`; | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: comment | |
| }); |