Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/scripts/test-package.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash

# Per-package test runner invoked from `melos run test:changes`.
#
# Why this exists: in `melos exec`, `--include-dependents` runs AFTER all
# package filters (see `applyFilters` in melos's package.dart). When a change
# affects e.g. `stream_chat`, melos pulls in every transitive dependent from
# the unfiltered workspace — including `*_example` packages that have no
# `test/` directory and `sample_app/` which we don't test in this matrix.
# Filtering via `dirExists: test` or `--ignore="*example*"` doesn't help
# because those filters are bypassed for dependents.
#
# So the skip decision happens here, after melos has computed the affected
# set. `set -e` plus an explicit `exec` ensures real test failures propagate
# (the original inline `[ -d test ] && X || Y` workaround silently swallowed
# failures when `flutter test` exited non-zero).

set -e

if [[ "$MELOS_PACKAGE_NAME" == *_example ]]; then
echo "→ Skipping example: $MELOS_PACKAGE_NAME"
exit 0
fi

if [ ! -d test ]; then
echo "→ No test/ in $MELOS_PACKAGE_NAME, skipping"
exit 0
fi

exec flutter test --no-pub --coverage
44 changes: 30 additions & 14 deletions .github/workflows/stream_flutter_workflow.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
name: stream_flutter_workflow

env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
flutter_version: "3.x"

on:
Expand All @@ -27,8 +26,10 @@ jobs:
# filtering, which hangs forever) — see
# https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks
#
# `push` events to master always run the full suite, matching the
# previous `on.push.branches: [master]` behavior.
# `push` events always run (no draft/path gate), but downstream jobs still
# use the diff-aware `:changes` scripts. On a master push, `HEAD~1` is the
# previous tip, so the scope is exactly the merged commit's changes —
# matching what the PR ran.
gate:
runs-on: ubuntu-latest
outputs:
Expand Down Expand Up @@ -66,15 +67,21 @@ jobs:
- name: "Install Tools"
run: |
flutter pub global activate melos
# Fail fast on circular deps — runs in ~1s and saves the bootstrap cost
# on a doomed run. Only added in this job (one failure fails the
# workflow via downstream `needs:`).
- name: "Lint Cycles"
run: melos run lint:cycles
- name: "Bootstrap Workspace"
run: melos bootstrap --verbose
- name: "Dart Analyze"
run: |
melos run analyze
run: melos run analyze:changes
# Only on PRs targeting master — feature branches (v10, design-refresh,
# etc.) often carry unreleased version numbers that aren't ready for a
# publish dry-run yet.
- name: "Pub Check"
if: github.base_ref == 'master'
run: |
melos run lint:pub
run: melos run lint:pub:changes

format:
needs: gate
Expand All @@ -98,7 +105,7 @@ jobs:
- name: "Bootstrap Workspace"
run: melos bootstrap
- name: "Melos Format"
run: melos run format
run: melos run format:changes
- name: "Validate Formatting"
run: |
./.github/workflows/scripts/validate-formatting.sh
Expand Down Expand Up @@ -129,39 +136,48 @@ jobs:
- name: "Bootstrap Workspace"
run: melos bootstrap
- name: "Flutter Test"
run: melos run test:all
run: melos run test:changes
- name: "Collect Coverage"
run: melos run coverage:ignore-file --no-select
- name: "Upload Coverage"
uses: codecov/codecov-action@v5
with:
token: ${{secrets.CODECOV_TOKEN}}
files: packages/*/coverage/lcov.info
# Coverage thresholds skip packages whose tests didn't run on this PR —
# `hashFiles` returns an empty string when the path is missing. Without
# this guard, the action errors on missing files for unaffected packages.
- name: "Stream Chat Coverage Check"
if: hashFiles('packages/stream_chat/coverage/lcov.info') != ''
uses: VeryGoodOpenSource/very_good_coverage@v3.0.0
with:
path: packages/stream_chat/coverage/lcov.info
min_coverage: 70
- name: "Stream Chat Localizations Coverage Check"
if: hashFiles('packages/stream_chat_localizations/coverage/lcov.info') != ''
uses: VeryGoodOpenSource/very_good_coverage@v3.0.0
with:
path: packages/stream_chat_localizations/coverage/lcov.info
min_coverage: 100
- name: "Stream Chat Persistence Coverage Check"
if: hashFiles('packages/stream_chat_persistence/coverage/lcov.info') != ''
uses: VeryGoodOpenSource/very_good_coverage@v3.0.0
with:
path: packages/stream_chat_persistence/coverage/lcov.info
min_coverage: 95
- name: "Stream Chat Flutter Core Coverage Check"
if: hashFiles('packages/stream_chat_flutter_core/coverage/lcov.info') != ''
uses: VeryGoodOpenSource/very_good_coverage@v3.0.0
with:
path: packages/stream_chat_flutter_core/coverage/lcov.info
min_coverage: 30
- name: "Stream Chat Flutter Coverage Check"
if: hashFiles('packages/stream_chat_flutter/coverage/lcov.info') != ''
uses: VeryGoodOpenSource/very_good_coverage@v3.0.0
with:
path: packages/stream_chat_flutter/coverage/lcov.info
min_coverage: 44
# Upload last so we never push coverage that failed local thresholds.
- name: "Upload Coverage"
uses: codecov/codecov-action@v5
with:
token: ${{secrets.CODECOV_TOKEN}}
files: packages/*/coverage/lcov.info

build:
name: build (${{ matrix.platform }})
Expand Down
71 changes: 58 additions & 13 deletions melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,6 @@ scripts:
run: melos run analyze && melos run format
description: Run all static analysis checks

analyze:all:
run: melos run analyze && melos run metrics
description: Run all

analyze:
run: |
melos exec -c 5 --ignore="*example*" -- \
Expand All @@ -135,33 +131,72 @@ scripts:
Run `dart analyze` in all packages.
- Note: you can also rely on your IDEs Dart Analysis / Issues window.

# Scoped to packages touched by the current branch (plus their dependents),
# so PR CI only analyses what the PR could have broken. Examples are
# intentionally included — analysing them catches API drift in the sample
# code we ship.
#
# Why `HEAD~1...HEAD`: GitHub's `actions/checkout` checks out the PR's
# auto-generated merge commit (`refs/pull/N/merge`) by default. On that
# commit, `HEAD~1` is the first parent — i.e. the PR's BASE branch tip
# (master, v10, design-refresh, whatever). So `HEAD~1...HEAD` gives the
# PR's own changes regardless of which branch it targets. On `push` events
# `HEAD~1` is just the previous tip, so the same range gives the pushed
# commit's changes. No workflow env plumbing needed.
analyze:changes:
run: melos exec -c 5 --diff="HEAD~1...HEAD" --include-dependents -- "dart analyze --fatal-infos ."
description: Run `dart analyze` in packages changed since HEAD~1.

format:
run: dart format --set-exit-if-changed .
description: |
Run `dart format --set-exit-if-changed .` in all packages.

metrics:
run: |
melos exec -c 1 --ignore="*example*" -- \
flutter pub run dart_code_metrics:metrics analyze lib
description: |
Run `dart_code_metrics` in all packages.
- Note: you can also rely on your IDEs Dart Analysis / Issues window.
# Same scoping as `analyze:changes`, same reasoning for including examples.
format:changes:
run: melos exec -c 5 --diff="HEAD~1...HEAD" --include-dependents -- "dart format --set-exit-if-changed ."
description: Run `dart format` in packages changed since HEAD~1.

# Fails on circular package dependencies — cheap defensive check to run
# in CI before bootstrap, since a cycle here would silently break everyone.
lint:cycles:
run: melos list --cycles
description: Fail if any cycle exists in the workspace dependency graph.

lint:pub:
run: melos exec -c 1 --no-published --no-private --order-dependents -- "flutter pub publish -n"
description: Dry run `pub publish` in all packages.

# Mirrors the filter shape of `lint:pub` (no `--include-dependents`); just
# adds diff-scoping. `pub publish --dry-run` validates this package's own
# pubspec/files/metadata and never reads the parent's code, so dependent
# expansion would add CI time without catching anything new. `--no-private`
# filters examples cleanly here because we're not using --include-dependents
# (the filter-bypass quirk only triggers during dependent expansion).
lint:pub:changes:
run: melos exec -c 1 --diff="HEAD~1...HEAD" --no-published --no-private --order-dependents -- "flutter pub publish -n"
description: Dry run `pub publish` in publishable packages changed since HEAD~1.

generate:all:
run: melos run generate:dart && melos run generate:flutter
description: Build all generated files for Dart & Flutter packages in this project.

# `dart run build_runner` works for both Dart and Flutter packages; the
# `flutter pub run` form is deprecated since Flutter 3.7 and slower (it
# warms up the Flutter binary unnecessarily). One script body covers both
# packageFilters splits via the `flutter:` filter.
generate:dart:
run: melos exec -c 1 --depends-on="build_runner" --no-flutter -- "dart run build_runner build --delete-conflicting-outputs"
exec: dart run build_runner build --delete-conflicting-outputs
packageFilters:
flutter: false
dependsOn: build_runner
description: Build all generated files for Dart packages in this project.

generate:flutter:
run: melos exec -c 1 --depends-on="build_runner" --flutter -- "flutter pub run build_runner build --delete-conflicting-outputs"
exec: dart run build_runner build --delete-conflicting-outputs
packageFilters:
flutter: true
dependsOn: build_runner
description: Build all generated files for Flutter packages in this project.

version:update:
Expand All @@ -186,6 +221,16 @@ scripts:
flutter: true
dirExists: test

# Examples are in the workspace (needed for analyze/format coverage) but
# have no tests. `packageFilters` is applied before `--include-dependents`
# in melos, so we can't filter examples out via a filter — they re-enter
# the set through transitive-dependent expansion. The wrapper script does
# the skip per-package after melos has done its math. `--no-pub` is safe
# because the workflow runs `melos bootstrap` first.
test:changes:
run: melos exec -c 4 --fail-fast --diff="HEAD~1...HEAD" --include-dependents -- "bash \$MELOS_ROOT_PATH/.github/workflows/scripts/test-package.sh"
description: Run `flutter test` in packages changed since HEAD~1.

update:goldens:
run: melos exec -c 1 --depends-on="alchemist" -- "flutter test --tags golden --update-goldens"
description: Update golden files for all packages in this project.
Expand Down
Loading