Skip to content

Measure a worktree against where it lands, not the repo default (SPEC… #564

Measure a worktree against where it lands, not the repo default (SPEC…

Measure a worktree against where it lands, not the repo default (SPEC… #564

Workflow file for this run

name: Flutter Security + Lint CI
on:
push:
branches: [ main ]
paths:
- 'app/**'
- '.github/workflows/flutter-ci.yml'
pull_request:
branches: [ main ]
paths:
- 'app/**'
- '.github/workflows/flutter-ci.yml'
jobs:
lint-and-analyze:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.44.9'
channel: 'stable'
- name: Cache Flutter dependencies
uses: actions/cache@v5
with:
path: |
${{ runner.tool_cache }}/flutter/
~/.pub-cache/
key: ${{ runner.os }}-flutter-3.44.9-${{ hashFiles('app/pubspec.lock') }}
restore-keys: |
${{ runner.os }}-flutter-3.44.9-
- name: Install Flutter dependencies (lockfile-locked)
run: |
cd app
flutter pub get --enforce-lockfile
- name: Run analysis (strict linting)
run: |
cd app
flutter analyze --no-pub --fatal-infos
- name: Check code formatting
run: |
cd app
dart format --output=none --set-exit-if-changed lib test
- name: Check for vulnerable packages
run: |
cd app
flutter pub outdated || true
continue-on-error: true # Advisory only, doesn't block CI
- name: Verify pubspec.lock integrity
run: |
cd app
if ! git diff --exit-code pubspec.lock > /dev/null; then
echo "ERROR: pubspec.lock would be modified by pub get"
echo "This indicates a lockfile inconsistency or manual edit."
echo "Run: flutter pub get && git add pubspec.lock"
exit 1
fi
test:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.44.9'
channel: 'stable'
- name: Cache Flutter dependencies
uses: actions/cache@v5
with:
path: |
${{ runner.tool_cache }}/flutter/
~/.pub-cache/
key: ${{ runner.os }}-flutter-3.44.9-${{ hashFiles('app/pubspec.lock') }}
restore-keys: |
${{ runner.os }}-flutter-3.44.9-
- name: Install Flutter dependencies
run: |
cd app
flutter pub get --enforce-lockfile
- name: Run tests
run: |
cd app
flutter test --coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
files: ./app/coverage/lcov.info
flags: flutter
continue-on-error: true # Coverage upload is advisory; test failures block above
security-audit:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
# Full history: the cooldown gate diffs pubspec.lock against the
# PR base / previous commit.
fetch-depth: 0
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.44.9'
channel: 'stable'
- name: Cache Flutter dependencies
uses: actions/cache@v5
with:
path: |
${{ runner.tool_cache }}/flutter/
~/.pub-cache/
key: ${{ runner.os }}-flutter-3.44.9-${{ hashFiles('app/pubspec.lock') }}
restore-keys: |
${{ runner.os }}-flutter-3.44.9-
- name: Install Flutter dependencies
run: |
cd app
flutter pub get --enforce-lockfile
- name: Enforce pub release cooldown (SECURITY.md §8)
env:
# pub has no `minimumReleaseAge`; block lockfile versions published
# less than 3 days ago (SECURITY.md §8). Baseline = PR base, or the
# previous commit on a push. Empty on other event types (e.g. a
# future `schedule`/`workflow_dispatch`), and all-zero on branch
# creation — both are resolved to a usable commit below, because an
# empty ref would make git read the *index* and pass silently.
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
run: |
cd app
base="$BASE_SHA"
if [ -z "$base" ] || [ "$base" = "0000000000000000000000000000000000000000" ] \
|| ! git rev-parse --verify --quiet "$base^{commit}" > /dev/null; then
echo "base '$base' unusable — falling back to HEAD~1"
base="HEAD~1"
fi
git rev-parse --verify --quiet "$base^{commit}" > /dev/null || {
echo "no usable baseline commit — cannot verify cooldown"; exit 1; }
COOLDOWN_BASE_REF="$base" dart run tool/pub_cooldown.dart
- name: Scan dependencies for known advisories (OSV)
run: |
cd app
# Pinned binary + SHA256 verify — no third-party action in the
# trust boundary. Bump OSV_VERSION/OSV_SHA256 together; get the
# hash from the release's osv-scanner_SHA256SUMS.
OSV_VERSION="v2.3.8"
OSV_SHA256="bc98e15319ed0d515e3f9235287ba53cdc5535d576d24fd573978ecfe9ab92dc"
curl -sSfL -o /tmp/osv-scanner \
"https://github.com/google/osv-scanner/releases/download/${OSV_VERSION}/osv-scanner_linux_amd64"
echo "${OSV_SHA256} /tmp/osv-scanner" | sha256sum -c -
chmod +x /tmp/osv-scanner
# Exits non-zero if any package in the lockfile has a known advisory.
/tmp/osv-scanner scan source --lockfile=pubspec.lock