Skip to content

Commit 0f29002

Browse files
[tool] Add validation of auto-labeler (flutter#10300)
Adds repo tool validation that every package has an auto-labeler rule. Fixes issues it found: - Two missing packages - One package with a typo in its glob that prevented it from working
1 parent 169d555 commit 0f29002

File tree

3 files changed

+181
-77
lines changed

3 files changed

+181
-77
lines changed

.github/labeler.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
'p: google_identity_services':
7373
- changed-files:
7474
- any-glob-to-any-file:
75-
- packages/google_indentity_services_web/**/*
75+
- packages/google_identity_services_web/**/*
7676

7777
'p: google_fonts':
7878
- changed-files:
@@ -119,6 +119,11 @@
119119
- any-glob-to-any-file:
120120
- packages/multicast_dns/**/*
121121

122+
'p: mustache_template':
123+
- changed-files:
124+
- any-glob-to-any-file:
125+
- third_party/packages/mustache_template/**/*
126+
122127
'p: path_parsing':
123128
- changed-files:
124129
- any-glob-to-any-file:

script/tool/lib/src/repo_package_info_check_command.dart

Lines changed: 112 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand {
3434
/// Packages with entries in CODEOWNERS.
3535
final List<String> _ownedPackages = <String>[];
3636

37+
/// Packages with entries in labeler.yml.
38+
final List<String> _autoLabeledPackages = <String>[];
39+
3740
@override
3841
final String name = 'repo-package-info-check';
3942

@@ -42,7 +45,7 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand {
4245

4346
@override
4447
final String description =
45-
'Checks that all packages are listed correctly in the repo README.';
48+
'Checks that all packages are listed correctly in repo metadata.';
4649

4750
@override
4851
final bool hasLongOutput = false;
@@ -98,6 +101,23 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand {
98101
}
99102
_ownedPackages.add(name);
100103
}
104+
105+
// Extract all of the lebeler.yml package entries.
106+
// Validate the match rules rather than the label itself, as the labels
107+
// don't always correspond 1:1 to packages and package names.
108+
final RegExp packageGlobPattern =
109+
RegExp(r'^\s*-\s*(?:third_party/)?packages/([^*]*)/');
110+
for (final String line in _repoRoot
111+
.childDirectory('.github')
112+
.childFile('labeler.yml')
113+
.readAsLinesSync()) {
114+
final RegExpMatch? match = packageGlobPattern.firstMatch(line);
115+
if (match == null) {
116+
continue;
117+
}
118+
final String name = match.group(1)!;
119+
_autoLabeledPackages.add(name);
120+
}
101121
}
102122

103123
@override
@@ -115,92 +135,110 @@ class RepoPackageInfoCheckCommand extends PackageLoopingCommand {
115135
errors.add('Missing CODEOWNERS entry');
116136
}
117137

138+
// All packages should have an auto-applied label. For plugins, only the
139+
// group needs a rule, so check the app-facing package.
140+
if (!(package.isFederated && !package.isAppFacing) &&
141+
!_autoLabeledPackages.contains(packageName)) {
142+
printError('${indentation}Missing a rule in .github/labeler.yml.');
143+
errors.add('Missing auto-labeler entry');
144+
}
145+
118146
// The content of ci_config.yaml must be valid if there is one.
119147
if (package.ciConfigFile.existsSync()) {
120148
errors.addAll(
121149
_validateCiConfig(package.ciConfigFile, mainPackage: package));
122150
}
123151

124-
// Any published package should be in the README table.
152+
// All published packages should have a README.md entry.
153+
if (package.isPublishable()) {
154+
errors.addAll(_validateRootReadme(package));
155+
}
156+
157+
return errors.isEmpty
158+
? PackageResult.success()
159+
: PackageResult.fail(errors);
160+
}
161+
162+
List<String> _validateRootReadme(RepositoryPackage package) {
163+
final List<String> errors = <String>[];
164+
125165
// For federated plugins, only the app-facing package is listed.
126-
if (package.isPublishable() &&
127-
(!package.isFederated || package.isAppFacing)) {
128-
final List<String>? cells = _readmeTableEntries[packageName];
166+
if (package.isFederated && !package.isAppFacing) {
167+
return errors;
168+
}
129169

130-
if (cells == null) {
131-
printError('${indentation}Missing repo root README.md table entry');
132-
errors.add('Missing repo root README.md table entry');
133-
} else {
134-
// Extract the two parts of a "[label](link)" .md link.
135-
final RegExp mdLinkPattern = RegExp(r'^\[(.*)\]\((.*)\)$');
136-
// Possible link targets.
137-
for (final String cell in cells) {
138-
final RegExpMatch? match = mdLinkPattern.firstMatch(cell);
139-
if (match == null) {
170+
final String packageName = package.directory.basename;
171+
final List<String>? cells = _readmeTableEntries[packageName];
172+
if (cells == null) {
173+
printError('${indentation}Missing repo root README.md table entry');
174+
errors.add('Missing repo root README.md table entry');
175+
} else {
176+
// Extract the two parts of a "[label](link)" .md link.
177+
final RegExp mdLinkPattern = RegExp(r'^\[(.*)\]\((.*)\)$');
178+
// Possible link targets.
179+
for (final String cell in cells) {
180+
final RegExpMatch? match = mdLinkPattern.firstMatch(cell);
181+
if (match == null) {
182+
printError(
183+
'${indentation}Invalid repo root README.md table entry: "$cell"');
184+
errors.add('Invalid root README.md table entry');
185+
} else {
186+
final String encodedIssueTag =
187+
Uri.encodeComponent(_issueTagForPackage(packageName));
188+
final String encodedPRTag =
189+
Uri.encodeComponent(_prTagForPackage(packageName));
190+
final String anchor = match.group(1)!;
191+
final String target = match.group(2)!;
192+
193+
// The anchor should be one of:
194+
// - The package name (optionally with any underscores escaped)
195+
// - An image with a name-based link
196+
// - An image with a tag-based link
197+
final RegExp packageLink =
198+
RegExp(r'^!\[.*\]\(https://img.shields.io/pub/.*/'
199+
'$packageName'
200+
r'(?:\.svg)?\)$');
201+
final RegExp issueTagLink = RegExp(
202+
r'^!\[.*\]\(https://img.shields.io/github/issues/flutter/flutter/'
203+
'$encodedIssueTag'
204+
r'\?label=\)$');
205+
final RegExp prTagLink = RegExp(
206+
r'^!\[.*\]\(https://img.shields.io/github/issues-pr/flutter/packages/'
207+
'$encodedPRTag'
208+
r'\?label=\)$');
209+
if (!(anchor == packageName ||
210+
anchor == packageName.replaceAll('_', r'\_') ||
211+
packageLink.hasMatch(anchor) ||
212+
issueTagLink.hasMatch(anchor) ||
213+
prTagLink.hasMatch(anchor))) {
140214
printError(
141-
'${indentation}Invalid repo root README.md table entry: "$cell"');
142-
errors.add('Invalid root README.md table entry');
143-
} else {
144-
final String encodedIssueTag =
145-
Uri.encodeComponent(_issueTagForPackage(packageName));
146-
final String encodedPRTag =
147-
Uri.encodeComponent(_prTagForPackage(packageName));
148-
final String anchor = match.group(1)!;
149-
final String target = match.group(2)!;
150-
151-
// The anchor should be one of:
152-
// - The package name (optionally with any underscores escaped)
153-
// - An image with a name-based link
154-
// - An image with a tag-based link
155-
final RegExp packageLink =
156-
RegExp(r'^!\[.*\]\(https://img.shields.io/pub/.*/'
157-
'$packageName'
158-
r'(?:\.svg)?\)$');
159-
final RegExp issueTagLink = RegExp(
160-
r'^!\[.*\]\(https://img.shields.io/github/issues/flutter/flutter/'
161-
'$encodedIssueTag'
162-
r'\?label=\)$');
163-
final RegExp prTagLink = RegExp(
164-
r'^!\[.*\]\(https://img.shields.io/github/issues-pr/flutter/packages/'
165-
'$encodedPRTag'
166-
r'\?label=\)$');
167-
if (!(anchor == packageName ||
168-
anchor == packageName.replaceAll('_', r'\_') ||
169-
packageLink.hasMatch(anchor) ||
170-
issueTagLink.hasMatch(anchor) ||
171-
prTagLink.hasMatch(anchor))) {
172-
printError(
173-
'${indentation}Incorrect anchor in root README.md table: "$anchor"');
174-
errors.add('Incorrect anchor in root README.md table');
175-
}
176-
177-
// The link should be one of:
178-
// - a relative link to the in-repo package
179-
// - a pub.dev link to the package
180-
// - a github label link to the package's label
181-
final RegExp pubDevLink =
182-
RegExp('^https://pub.dev/packages/$packageName(?:/score)?\$');
183-
final RegExp gitHubIssueLink = RegExp(
184-
'^https://github.com/flutter/flutter/labels/$encodedIssueTag\$');
185-
final RegExp gitHubPRLink = RegExp(
186-
'^https://github.com/flutter/packages/labels/$encodedPRTag\$');
187-
if (!(target == './packages/$packageName/' ||
188-
target == './third_party/packages/$packageName/' ||
189-
pubDevLink.hasMatch(target) ||
190-
gitHubIssueLink.hasMatch(target) ||
191-
gitHubPRLink.hasMatch(target))) {
192-
printError(
193-
'${indentation}Incorrect link in root README.md table: "$target"');
194-
errors.add('Incorrect link in root README.md table');
195-
}
215+
'${indentation}Incorrect anchor in root README.md table: "$anchor"');
216+
errors.add('Incorrect anchor in root README.md table');
217+
}
218+
219+
// The link should be one of:
220+
// - a relative link to the in-repo package
221+
// - a pub.dev link to the package
222+
// - a github label link to the package's label
223+
final RegExp pubDevLink =
224+
RegExp('^https://pub.dev/packages/$packageName(?:/score)?\$');
225+
final RegExp gitHubIssueLink = RegExp(
226+
'^https://github.com/flutter/flutter/labels/$encodedIssueTag\$');
227+
final RegExp gitHubPRLink = RegExp(
228+
'^https://github.com/flutter/packages/labels/$encodedPRTag\$');
229+
if (!(target == './packages/$packageName/' ||
230+
target == './third_party/packages/$packageName/' ||
231+
pubDevLink.hasMatch(target) ||
232+
gitHubIssueLink.hasMatch(target) ||
233+
gitHubPRLink.hasMatch(target))) {
234+
printError(
235+
'${indentation}Incorrect link in root README.md table: "$target"');
236+
errors.add('Incorrect link in root README.md table');
196237
}
197238
}
198239
}
199240
}
200-
201-
return errors.isEmpty
202-
? PackageResult.success()
203-
: PackageResult.fail(errors);
241+
return errors;
204242
}
205243

206244
List<String> _validateCiConfig(File ciConfig,

0 commit comments

Comments
 (0)