Skip to content
Merged
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
8 changes: 8 additions & 0 deletions .github/workflows/dart_skills_lint_workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ jobs:

- run: dart test

- name: Verify API boundary runner example
run: |
cd example/api_boundary_runner
dart pub get
dart format --output=none --set-exit-if-changed .
dart analyze --fatal-infos .
dart run bin/main.dart
coverage:
runs-on: ubuntu-latest
steps:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,18 @@ For each downstream consumer under evaluation:
ref: <LATEST_COMMIT_HASH>
```

2. **Resolve Dependencies & Run Verification Tests**
2. **Resolve Dependencies & Run Verification Tests (Legacy Check)**
- Execute dependency resolution according to the consumer environment (Flutter workspaces require `flutter pub get`; standard pure Dart repositories require `dart pub get`).
- Run the consumer's verification tests (typically targeting tests like `test/validate_skills_test.dart` or running `flutter test` / `dart test`).
- Run the consumer's verification tests against their existing code (typically targeting tests like `test/validate_skills_test.dart` or running `flutter test` / `dart test`).
- Confirm that all existing tests and static analyses compile and pass cleanly when using their established calling syntax. This ensures backward-compatibility deprecation shims function properly right alongside legacy calling conventions.

3. **Perform Diagnostic API Migration & Boundary Verification**
After verifying across each target consumer that legacy calls function properly without regressions in Step 2:
- **Migrate Consumer Calling Syntax**: For every repository in the set of downstream targets under evaluation (whether a single target, a requested subset, or all known consumers), update its codebase to remove any usage of deprecated getters, parameters, or constructors directly, replacing them with the new API surface introduced in `dart_skills_lint` (for example, transitioning `resolvedRules` arguments to `resolvedRuleConfigs`).
- **Verify Public Boundary Resolution**: Execute strict static analysis (`dart analyze --fatal-infos <modified_test_or_package_path>`) within the consumer's package directory after completing the migration.
- Confirm that all newly exposed classes and parameters resolve cleanly through the public library barrier (`import 'package:dart_skills_lint/dart_skills_lint.dart';`).
- Any syntax check reporting `Undefined class` or requiring internal implementation imports (`import 'package:dart_skills_lint/src/...';`) to compile indicates an explicit **public export deficit** inside `lib/dart_skills_lint.dart`.
- **Run Migrated Test Suite**: Re-run the complete downstream consumer test harness against the migrated code (`flutter test` / `dart test`) to guarantee exact behavioral alignment before accepting the upstream change.

---

Expand Down
4 changes: 2 additions & 2 deletions tool/dart_skills_lint/dart_skills_lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ dart_skills_lint:
rules:
check-trailing-whitespace: error
prevent-skills-sh-publishing: error
- path: "example/invalid"
- path: "example/skills/invalid"
rules:
prevent-skills-sh-publishing: error
- path: "example/valid"
- path: "example/skills/valid"
rules:
prevent-skills-sh-publishing: error
individual_skills:
Expand Down
14 changes: 7 additions & 7 deletions tool/dart_skills_lint/example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@ Two reference fixtures live in this directory:

| Fixture | Expected outcome |
| --- | --- |
| [`valid/`](valid/SKILL.md) | All rules pass; the CLI exits 0. |
| [`invalid/`](invalid/SKILL.md) | Multiple rules fail; the CLI exits 1. |
| [`valid/`](skills/valid/SKILL.md) | All rules pass; the CLI exits 0. |
| [`invalid/`](skills/invalid/SKILL.md) | Multiple rules fail; the CLI exits 1. |

Use them to take the linter for a spin without writing your own skill
first, and to see exactly what real diagnostic output looks like.

## Run the valid fixture

```bash
dart run dart_skills_lint --skill ./example/valid
dart run dart_skills_lint --skill ./example/skills/valid
```

You should see:

```
Evaluating directory: example/valid
Evaluating directory: example/skills/valid
--- Validating skill: valid ---
Skill is valid.
```
Expand All @@ -32,14 +32,14 @@ With default rule severities, only `invalid-skill-name` fires (the other
two violations are below their default threshold):

```bash
dart run dart_skills_lint --skill ./example/invalid
dart run dart_skills_lint --skill ./example/skills/invalid
```

Exit code: `1`. To see every violation surface as an error, escalate the
other two rules with explicit flags:

```bash
dart run dart_skills_lint --skill ./example/invalid \
dart run dart_skills_lint --skill ./example/skills/invalid \
--disallowed-field --check-absolute-paths
```

Expand All @@ -63,7 +63,7 @@ when the target file exists. To experiment, point it at a real local
file:

```bash
dart run dart_skills_lint --skill ./example/invalid --fix --dry-run
dart run dart_skills_lint --skill ./example/skills/invalid --fix --dry-run
```

`--dry-run` shows the proposed diff without writing; drop it to apply
Expand Down
69 changes: 69 additions & 0 deletions tool/dart_skills_lint/example/api_boundary_runner/bin/main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// ignore_for_file: avoid_print

import 'dart:io';

import 'package:dart_skills_lint/dart_skills_lint.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as p;

Future<void> main(List<String> args) async {
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) => print(record.message));

print('Running API boundary validation runner...');

String findPath(String relativeSuffix) {
final pathsToTry = <String>[
p.join('example', 'skills', relativeSuffix),
p.join('..', 'skills', relativeSuffix),
if (Platform.script.scheme == 'file')
p.join(p.dirname(Platform.script.toFilePath()), '..', '..', 'skills', relativeSuffix),
];
for (final path in pathsToTry) {
final String absolutePath = p.absolute(path);
if (Directory(absolutePath).existsSync()) {
return p.normalize(absolutePath);
}
}
throw StateError('Could not locate skills/$relativeSuffix directory.');
}

final String validSkillPath = findPath('valid');
final String invalidSkillPath = findPath('invalid');

print('Validating valid skill at: $validSkillPath');
final bool validResult = await validateSkills(
individualSkillPaths: [validSkillPath],
resolvedRuleConfigs: {
'check-absolute-paths': const RuleConfigPatch(severity: AnalysisSeverity.disabled),
},
);

if (!validResult) {
print('Error: Valid skill fixture failed validation!');
exitCode = 1;
return;
}
Comment on lines +46 to +50

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Calling exit(1) immediately terminates the Dart VM, which can truncate pending asynchronous operations or stdout/stderr streams. Setting exitCode = 1 and returning from main allows the VM to flush all event queues and terminate cleanly.

Suggested change
if (!validResult) {
print('Error: Valid skill fixture failed validation!');
exit(1);
}
if (!validResult) {
print('Error: Valid skill fixture failed validation!');
exitCode = 1;
return;
}

print('Success: Valid skill fixture validated cleanly.');

print('Validating invalid skill at: $invalidSkillPath');
// Since this is invalid, we expect it to fail under standard rules.
final bool invalidResult = await validateSkills(
individualSkillPaths: [invalidSkillPath],
printWarnings: false,
quiet: true,
);

if (invalidResult) {
print('Error: Invalid skill fixture unexpectedly passed validation!');
exitCode = 1;
return;
}
Comment on lines +61 to +65

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Setting exitCode = 1 and returning from main is preferred over calling exit(1) directly, as it allows the Dart VM to flush pending asynchronous stdout/stderr writes before terminating.

Suggested change
if (invalidResult) {
print('Error: Invalid skill fixture unexpectedly passed validation!');
exit(1);
}
if (invalidResult) {
print('Error: Invalid skill fixture unexpectedly passed validation!');
exitCode = 1;
return;
}

print('Success: Invalid skill fixture failed validation as expected.');

print('API boundary verification completed successfully.');
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: api_boundary_runner
description: An example code runner that validates the public API boundary.
environment:
sdk: ^3.11.0-0
dependencies:
path: ^1.9.0
logging: ^1.2.0
dart_skills_lint:
path: ../../
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ The broken link: [absolute link](/tmp/this/does/not/exist.md)
Run it with default rules:

```bash
dart run dart_skills_lint --skill ./example/invalid
dart run dart_skills_lint --skill ./example/skills/invalid
```

…and again with every rule turned up to error:

```bash
dart run dart_skills_lint --skill ./example/invalid \
dart run dart_skills_lint --skill ./example/skills/invalid \
--disallowed-field --check-absolute-paths
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ against. It deliberately does nothing useful — it's documentation.
Run it with:

```bash
dart run dart_skills_lint --skill ./example/valid
dart run dart_skills_lint --skill ./example/skills/valid
```

Expected output: `Skill is valid.` and exit code 0.
4 changes: 2 additions & 2 deletions tool/dart_skills_lint/test/example_fixtures_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import 'package:test_process/test_process.dart';
void main() {
group('example fixtures', () {
final String cliPath = p.normalize(p.absolute('bin/cli.dart'));
final String validPath = p.normalize(p.absolute('example/valid'));
final String invalidPath = p.normalize(p.absolute('example/invalid'));
final String validPath = p.normalize(p.absolute('example/skills/valid'));
final String invalidPath = p.normalize(p.absolute('example/skills/invalid'));

test('example/valid passes with default rules', () async {
final TestProcess process = await TestProcess.start('dart', [cliPath, '--skill', validPath]);
Expand Down
4 changes: 2 additions & 2 deletions tool/dart_skills_lint/test/recipe_drift_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ void main() {
group('README Recipes drift', () {
late _RecipeReader reader;
final String cliPath = p.normalize(p.absolute('bin/cli.dart'));
final String validFixture = p.normalize(p.absolute('example/valid'));
final String invalidFixture = p.normalize(p.absolute('example/invalid'));
final String validFixture = p.normalize(p.absolute('example/skills/valid'));
final String invalidFixture = p.normalize(p.absolute('example/skills/invalid'));

setUpAll(() {
reader = _RecipeReader.fromFile(p.normalize(p.absolute('README.md')));
Expand Down
Loading