| name | dev-build-run-ci |
| description | Use when running or scripting CI checks (pub get, dart format check, dart analyze, dart/flutter test, web build) on a Dart or Flutter package or on a whole repository tree with package:dev_build: the run_ci executable (dart run dev_build:run_ci, --recursive, --no-test, --analyze, --fix, --pub-downgrade, --offline, --prj-info, --ignore-errors, --vm-test), the packageRunCi / PackageRunCiOptions / recursivePackagesRun / SinglePackageCiRunner API, the tool/run_ci.dart and tool/run_ci_override.dart conventions, .local/.skip_run_ci, dart_test.yaml platforms and pub workspaces. |
dev_build run_ci: validate a package or a tree
package:dev_build runs the standard validation steps of a Dart or Flutter
package: pub get, format check, analyze, tests on every supported platform,
and an optional web build. It works on a single package or recursively on
every package found under a folder. Same code drives the run_ci executable
and the packageRunCi() API. VM only (it spawns dart/flutter processes).
// tool/run_ci.dart
import 'package:dev_build/package.dart';
Future<void> main() async {
await packageRunCi('.');
}
dart run dev_build:run_ci
Guidelines
Setup
- Add
dev_build to dev_dependencies. Import
package:dev_build/package.dart for packageRunCi, PackageRunCiOptions,
recursivePackagesRun, recursivePubPath, SinglePackageCiRunner.
- Put the script in
tool/run_ci.dart (repository convention) and run it
with dart run tool/run_ci.dart. For a one-off from the command line use
dart run dev_build:run_ci [<path>] [<flags>], or activate it once with
dart pub global activate dev_build and run run_ci.
What a run does, in order
dart pub get (or flutter pub get when the package, or any package of
its pub workspace, depends on flutter). --offline adds --offline.
- Format check:
dart format --set-exit-if-changed <top level dirs that contain dart files> (lib test bin tool example ...). Nested packages,
hidden dirs, build, deploy, node_modules, .dart_tool are excluded.
- Analyze:
dart analyze --fatal-warnings --fatal-infos . for Dart,
flutter analyze --no-pub . for Flutter. Infos fail the build: keep
analysis_options.yaml clean.
- Test:
flutter test --no-pub for Flutter. For Dart, dart test is run
once per platform: vm always, chrome when build_web_compilers is a
dependency, node when build_node_compilers is a dependency and node
is installed. Skipped entirely when there is no test/ folder.
- Build:
flutter build web --no-pub when web/index.html and
lib/main.dart exist; dart pub global run webdev build for a Dart
package that depends on build_web_compilers + build_runner and has
web/index.html.
- A
dart_test.yaml platforms: list restricts the platforms above,
compilers: (dart2js, dart2wasm) adds --compiler for browser runs.
Options (PackageRunCiOptions / flags)
- Skip a step:
noPubGet, noFormat, noAnalyze, noTest, noBuild
(--no-pub-get, --no-format, --no-analyze, --no-test,
--no-build).
- Run only one step:
formatOnly, analyzeOnly, testOnly, buildOnly,
pubGetOnly, pubUpgradeOnly, pubDowngradeOnly, fixOnly (--format,
--analyze, --test, --build, --pub-get, --pub-upgrade,
--pub-downgrade, --fix). An "only" option turns every other step off;
analyzeOnly still runs pub get first unless noPubGet: true.
- Test platforms:
vmTestOnly (--vm-test), noVmTest, noBrowserTest,
chromeJsTestOnly (--chrome-js-test, dart2js only), noNodeTest,
noNpmInstall.
fixOnly runs dart format <dirs> then dart fix --apply and stops.
recursive: true (--recursive, the CLI default; API default is
false) finds every package below path, links followed, and runs them
through a pool of poolSize (default 4 in the API, -j/--concurrency
default 1 in the CLI).
ignoreErrors (--ignore-errors, -i): a failing package is reported
and the run continues with the next one, instead of throwing.
dryRun (--dry-run) prints the shell lines instead of running them.
prjInfo (--prj-info) prints the sdk constraint and flutter tag,
noRunCi (--no-run-ci) stops after that, printPath (--print-path)
only prints the package path. verbose (-v).
filterDartProjectOptions: FilterDartProjectOptions(minSdk:, maxSdk:, ignoreSdkConstraints:) (--min-sdk '>=3.0.0', --max-sdk,
--ignore-sdk-constraints) selects which packages are considered. By
default a package whose environment: sdk: does not match the running
dart is skipped.
noOverride (--no-override) ignores tool/run_ci_override.dart and
.local/.skip_run_ci.
Repository conventions the runner honours
tool/run_ci_override.dart in a package: run with dart run instead of
the standard steps (use noOverride/--no-override from inside it or
the run recurses). Use it for a package that needs custom steps.
.local/.skip_run_ci (empty file) skips the package in recursive runs.
Create it with run_ci config --skip-run-ci [<path>]; git-ignore .local.
- A package whose name starts with
_ only gets pub get.
- A pub workspace root gets
pub get once for the whole workspace, then is
skipped for format/analyze/test; members are run individually.
- Everything under a hidden folder,
build, deploy or node_modules is
never visited.
Custom actions over a tree
recursivePubPath(['.']) returns every package folder (sorted, links
resolved). dependencies: ['sembast'] keeps packages depending on it,
with direct:/dev:/override: prefixes to narrow the kind, and
readConfig: true also matches transitive dependencies through
.dart_tool/package_config.json.
recursivePackagesRun(['.'], action: (dir) async {...}, poolSize: 1)
runs a callback per package. Combine with Shell(workingDirectory: dir)
from package:dev_build/shell.dart.
SinglePackageCiRunner(PubIoPackage(path), options) (PubIoPackage from
package:dev_build/menu/menu_run_ci.dart) exposes the individual steps:
init(), then format(), analyze(), runTest(testOptions: [...]),
fix(). Use it to pass extra dart test arguments.
runCiInitPubWorkspacesCache() is called by packageRunCi so a
workspace gets one pub get; call it yourself before looping with
singlePackageRunCi-style code.
Examples
tool/run_ci.dart for a repository
import 'package:dev_build/package.dart';
Future<void> main() async {
// Every package under the repo root, one at a time.
await packageRunCi('..', options: PackageRunCiOptions(recursive: true));
}
Analyze only, without touching the network
import 'package:dev_build/package.dart';
Future<void> main() async {
await packageRunCi(
'.',
options: PackageRunCiOptions(
recursive: true,
analyzeOnly: true,
noPubGet: true,
),
);
}
Check minimum dependency versions
import 'package:dev_build/package.dart';
Future<void> main() async {
// pub downgrade + analyze on every package, ignoring tool/run_ci_override.dart
await packageRunCi(
'.',
options: PackageRunCiOptions(
recursive: true,
pubDowngradeOnly: true,
analyzeOnly: true,
noOverride: true,
),
);
}
Fix formatting and lints recursively
import 'package:dev_build/package.dart';
Future<void> main() async {
await packageRunCi(
'.',
options: PackageRunCiOptions(recursive: true, fixOnly: true),
);
}
tool/run_ci_override.dart with custom steps
import 'package:dev_build/package.dart';
import 'package:dev_build/shell.dart';
Future<void> main() async {
// noOverride is required, otherwise this file runs itself again.
await packageRunCi(
'.',
options: PackageRunCiOptions(noOverride: true, noTest: true),
);
await Shell().run('dart test --platform vm --exclude-tags slow');
}
Extra test arguments with SinglePackageCiRunner
import 'package:dev_build/menu/menu_run_ci.dart' show PubIoPackage;
import 'package:dev_build/package.dart';
Future<void> main() async {
var package = PubIoPackage('.');
var runner = SinglePackageCiRunner(package, PackageRunCiOptions());
await runner.init();
await package.pubGet();
await runner.analyze();
await runner.runTest(testOptions: ['--reporter', 'expanded']);
}
Run a shell command in every package that depends on a package
import 'package:dev_build/package.dart';
import 'package:dev_build/shell.dart';
Future<void> main() async {
await recursivePackagesRun(
['.'],
dependencies: ['direct:sembast'],
poolSize: 1,
action: (dir) async {
await Shell(workingDirectory: dir).run('dart pub upgrade sembast');
},
);
}
Command line
dart run dev_build:run_ci
dart run dev_build:run_ci --no-test ../other
dart run dev_build:run_ci --analyze --no-pub-get
dart run dev_build:run_ci --pub-downgrade --analyze --no-override --recursive
dart run dev_build:run_ci --fix --recursive
dart run dev_build:run_ci --vm-test --offline
dart run dev_build:run_ci --prj-info --no-run-ci
dart run dev_build:run_ci config --skip-run-ci packages/legacy
dart run dev_build:run_ci menu
Common mistakes
- Expecting
packageRunCi('.') to recurse: the API default is
recursive: false; the executable defaults to --recursive.
- Writing
tool/run_ci_override.dart that calls packageRunCi without
noOverride: true.
- Relying on
--no-analyze to hide lints: the standard analyze uses
--fatal-infos, fix the code or the analysis_options.yaml.
- Adding
build_web_compilers to a package that has no browser tests: it
turns on dart test --platform chrome for every run.
- Using
packageRunCi from a web or Flutter app: it needs dart:io and
the dart/flutter executables on the PATH.
More
See references/cli-options.md for the complete
flag list of the run_ci executable and the exact commands executed per
package type.