Skip to content

Commit cf40966

Browse files
authored
[flutter_plugin_tools] Validate pubspec description (flutter#4396)
pub.dev deducts points for having a pubspec.yaml `description` that is too short or too long; several of our plugins are losing points on this. To ensure that we are following—and modeling—best practices, this adds a check that our `description` fields meet pub.dev expectations. Fixes our existing violations. Two are not published even though this only takes effect once published: - camera: We change this plugin pretty frequently, so this should go out soon without adding a release just for this trivial issue. - wifi_info_flutter: This is deprecated, so we don't plan to release it. It has to be fixed to allow the tool change to land though.
1 parent 63eb675 commit cf40966

File tree

15 files changed

+198
-12
lines changed

15 files changed

+198
-12
lines changed

packages/camera/camera/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## NEXT
2+
3+
* Updated package description.
4+
15
## 0.9.4+1
26

37
* Fixed Android implementation throwing IllegalStateException when switching to a different activity.

packages/camera/camera/pubspec.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: camera
2-
description: A Flutter plugin for getting information about and controlling the
3-
camera on Android, iOS and Web. Supports previewing the camera feed, capturing images, capturing video,
4-
and streaming image buffers to dart.
2+
description: A Flutter plugin for controlling the camera. Supports previewing
3+
the camera feed, capturing images and video, and streaming image buffers to
4+
Dart.
55
repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera
66
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
77
version: 0.9.4+1

packages/espresso/CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
## NEXT
1+
## 0.1.0+4
22

33
* Updated Android lint settings.
4+
* Updated package description.
45

56
## 0.1.0+3
67

packages/espresso/pubspec.yaml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
name: espresso
22
description: Java classes for testing Flutter apps using Espresso.
3+
Allows driving Flutter widgets from a native Espresso test.
34
repository: https://github.com/flutter/plugins/tree/master/packages/espresso
45
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+espresso%22
5-
version: 0.1.0+3
6+
version: 0.1.0+4
67

78
environment:
89
sdk: ">=2.12.0 <3.0.0"

packages/file_selector/file_selector/CHANGELOG.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
## NEXT
1+
## 0.8.2+1
22

33
* Minor code cleanup for new analysis rules.
4+
* Updated package description.
45

56
## 0.8.2
67

7-
* Update platform_plugin_interface version requirement.
8+
* Update `platform_plugin_interface` version requirement.
89

910
## 0.8.1
1011

packages/file_selector/file_selector/pubspec.yaml

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
name: file_selector
2-
description: Flutter plugin for opening and saving files.
2+
description: Flutter plugin for opening and saving files, or selecting
3+
directories, using native file selection UI.
34
repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector
45
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22
5-
version: 0.8.2
6+
version: 0.8.2+1
67

78
environment:
89
sdk: ">=2.12.0 <3.0.0"

packages/plugin_platform_interface/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.0.2
2+
3+
* Update package description.
4+
15
## 2.0.1
26

37
* Fix `federated flutter plugins` link in the README.md.

packages/plugin_platform_interface/pubspec.yaml

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
name: plugin_platform_interface
2-
description: Reusable base class for Flutter plugin platform interfaces.
2+
description: Reusable base class for platform interfaces of Flutter federated
3+
plugins, to help enforce best practices.
34
repository: https://github.com/flutter/plugins/tree/master/packages/plugin_platform_interface
45
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+plugin_platform_interface%22
56

@@ -14,7 +15,7 @@ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+
1415
# be done when absolutely necessary and after the ecosystem has already migrated to 1.X.Y version
1516
# that is forward compatible with 2.0.0 (ideally the ecosystem have migrated to depend on:
1617
# `plugin_platform_interface: >=1.X.Y <3.0.0`).
17-
version: 2.0.1
18+
version: 2.0.2
1819

1920
environment:
2021
sdk: ">=2.12.0 <3.0.0"

packages/wifi_info_flutter/wifi_info_flutter/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## NEXT
22

33
* Updated Android lint settings.
4+
* Updated package description.
45

56
## 2.0.2
67

packages/wifi_info_flutter/wifi_info_flutter/pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: wifi_info_flutter
2-
description: A new flutter plugin project.
2+
description: A Flutter plugin to get WiFi information such as connection status and network identifiers.
33
repository: https://github.com/flutter/plugins/tree/master/packages/wifi_info_flutter/wifi_info_flutter
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+wifi_info_flutter%22
55
version: 2.0.2

script/tool/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
major version change restriction.
1111
- Improved error handling and error messages in CHANGELOG version checks.
1212
- `license-check` now validates Kotlin files.
13+
- `pubspec-check` now checks that the description is of the pub-recommended
14+
length.
1315

1416
## 0.7.1
1517

script/tool/lib/src/common/repository_package.dart

+9
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ class RepositoryPackage {
5858
bool get isPlatformInterface =>
5959
directory.basename.endsWith('_platform_interface');
6060

61+
/// True if this appears to be a platform implementation package, according to
62+
/// repository conventions.
63+
bool get isPlatformImplementation =>
64+
// Any part of a federated plugin that isn't the platform interface and
65+
// isn't the app-facing package should be an implementation package.
66+
isFederated &&
67+
!isPlatformInterface &&
68+
directory.basename != directory.parent.basename;
69+
6170
/// Returns the Flutter example packages contained in the package, if any.
6271
Iterable<RepositoryPackage> getExamples() {
6372
final Directory exampleDirectory = directory.childDirectory('example');

script/tool/lib/src/pubspec_check_command.dart

+33
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,18 @@ class PubspecCheckCommand extends PackageLoopingCommand {
126126
'${indentation * 2}$_expectedIssueLinkFormat<package label>');
127127
passing = false;
128128
}
129+
130+
// Don't check descriptions for federated package components other than
131+
// the app-facing package, since they are unlisted, and are expected to
132+
// have short descriptions.
133+
if (!package.isPlatformInterface && !package.isPlatformImplementation) {
134+
final String? descriptionError =
135+
_checkDescription(pubspec, package: package);
136+
if (descriptionError != null) {
137+
printError('$indentation$descriptionError');
138+
passing = false;
139+
}
140+
}
129141
}
130142

131143
return passing;
@@ -180,6 +192,27 @@ class PubspecCheckCommand extends PackageLoopingCommand {
180192
return errorMessages;
181193
}
182194

195+
// Validates the "description" field for a package, returning an error
196+
// string if there are any issues.
197+
String? _checkDescription(
198+
Pubspec pubspec, {
199+
required RepositoryPackage package,
200+
}) {
201+
final String? description = pubspec.description;
202+
if (description == null) {
203+
return 'Missing "description"';
204+
}
205+
206+
if (description.length < 60) {
207+
return '"description" is too short. pub.dev recommends package '
208+
'descriptions of 60-180 characters.';
209+
}
210+
if (description.length > 180) {
211+
return '"description" is too long. pub.dev recommends package '
212+
'descriptions of 60-180 characters.';
213+
}
214+
}
215+
183216
bool _checkIssueLink(Pubspec pubspec) {
184217
return pubspec.issueTracker
185218
?.toString()

script/tool/test/common/repository_package_test.dart

+35
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,39 @@ void main() {
120120
plugin.childDirectory('example').childDirectory('example2').path);
121121
});
122122
});
123+
124+
group('federated plugin queries', () {
125+
test('all return false for a simple plugin', () {
126+
final Directory plugin = createFakePlugin('a_plugin', packagesDir);
127+
expect(RepositoryPackage(plugin).isFederated, false);
128+
expect(RepositoryPackage(plugin).isPlatformInterface, false);
129+
expect(RepositoryPackage(plugin).isFederated, false);
130+
});
131+
132+
test('handle app-facing packages', () {
133+
final Directory plugin =
134+
createFakePlugin('a_plugin', packagesDir.childDirectory('a_plugin'));
135+
expect(RepositoryPackage(plugin).isFederated, true);
136+
expect(RepositoryPackage(plugin).isPlatformInterface, false);
137+
expect(RepositoryPackage(plugin).isPlatformImplementation, false);
138+
});
139+
140+
test('handle platform interface packages', () {
141+
final Directory plugin = createFakePlugin('a_plugin_platform_interface',
142+
packagesDir.childDirectory('a_plugin'));
143+
expect(RepositoryPackage(plugin).isFederated, true);
144+
expect(RepositoryPackage(plugin).isPlatformInterface, true);
145+
expect(RepositoryPackage(plugin).isPlatformImplementation, false);
146+
});
147+
148+
test('handle platform implementation packages', () {
149+
// A platform interface can end with anything, not just one of the known
150+
// platform names, because of cases like webview_flutter_wkwebview.
151+
final Directory plugin = createFakePlugin(
152+
'a_plugin_foo', packagesDir.childDirectory('a_plugin'));
153+
expect(RepositoryPackage(plugin).isFederated, true);
154+
expect(RepositoryPackage(plugin).isPlatformInterface, false);
155+
expect(RepositoryPackage(plugin).isPlatformImplementation, true);
156+
});
157+
});
123158
}

script/tool/test/pubspec_check_command_test.dart

+93
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ void main() {
5656
bool includeHomepage = false,
5757
bool includeIssueTracker = true,
5858
bool publishable = true,
59+
String? description,
5960
}) {
6061
final String repositoryPath = repositoryPackagesDirRelativePath ?? name;
6162
final String repoLink = 'https://github.com/flutter/'
@@ -64,8 +65,11 @@ void main() {
6465
final String issueTrackerLink =
6566
'https://github.com/flutter/flutter/issues?'
6667
'q=is%3Aissue+is%3Aopen+label%3A%22p%3A+$name%22';
68+
description ??= 'A test package for validating that the pubspec.yaml '
69+
'follows repo best practices.';
6770
return '''
6871
name: $name
72+
description: $description
6973
${includeRepository ? 'repository: $repoLink' : ''}
7074
${includeHomepage ? 'homepage: $repoLink' : ''}
7175
${includeIssueTracker ? 'issue_tracker: $issueTrackerLink' : ''}
@@ -327,6 +331,95 @@ ${devDependenciesSection()}
327331
);
328332
});
329333

334+
test('fails when description is too short', () async {
335+
final Directory pluginDirectory =
336+
createFakePlugin('a_plugin', packagesDir.childDirectory('a_plugin'));
337+
338+
pluginDirectory.childFile('pubspec.yaml').writeAsStringSync('''
339+
${headerSection('plugin', isPlugin: true, description: 'Too short')}
340+
${environmentSection()}
341+
${flutterSection(isPlugin: true)}
342+
${dependenciesSection()}
343+
${devDependenciesSection()}
344+
''');
345+
346+
Error? commandError;
347+
final List<String> output = await runCapturingPrint(
348+
runner, <String>['pubspec-check'], errorHandler: (Error e) {
349+
commandError = e;
350+
});
351+
352+
expect(commandError, isA<ToolExit>());
353+
expect(
354+
output,
355+
containsAllInOrder(<Matcher>[
356+
contains('"description" is too short. pub.dev recommends package '
357+
'descriptions of 60-180 characters.'),
358+
]),
359+
);
360+
});
361+
362+
test(
363+
'allows short descriptions for non-app-facing parts of federated plugins',
364+
() async {
365+
final Directory pluginDirectory = createFakePlugin('plugin', packagesDir);
366+
367+
pluginDirectory.childFile('pubspec.yaml').writeAsStringSync('''
368+
${headerSection('plugin', isPlugin: true, description: 'Too short')}
369+
${environmentSection()}
370+
${flutterSection(isPlugin: true)}
371+
${dependenciesSection()}
372+
${devDependenciesSection()}
373+
''');
374+
375+
Error? commandError;
376+
final List<String> output = await runCapturingPrint(
377+
runner, <String>['pubspec-check'], errorHandler: (Error e) {
378+
commandError = e;
379+
});
380+
381+
expect(commandError, isA<ToolExit>());
382+
expect(
383+
output,
384+
containsAllInOrder(<Matcher>[
385+
contains('"description" is too short. pub.dev recommends package '
386+
'descriptions of 60-180 characters.'),
387+
]),
388+
);
389+
});
390+
391+
test('fails when description is too long', () async {
392+
final Directory pluginDirectory = createFakePlugin('plugin', packagesDir);
393+
394+
const String description = 'This description is too long. It just goes '
395+
'on and on and on and on and on. pub.dev will down-score it because '
396+
'there is just too much here. Someone shoul really cut this down to just '
397+
'the core description so that search results are more useful and the '
398+
'package does not lose pub points.';
399+
pluginDirectory.childFile('pubspec.yaml').writeAsStringSync('''
400+
${headerSection('plugin', isPlugin: true, description: description)}
401+
${environmentSection()}
402+
${flutterSection(isPlugin: true)}
403+
${dependenciesSection()}
404+
${devDependenciesSection()}
405+
''');
406+
407+
Error? commandError;
408+
final List<String> output = await runCapturingPrint(
409+
runner, <String>['pubspec-check'], errorHandler: (Error e) {
410+
commandError = e;
411+
});
412+
413+
expect(commandError, isA<ToolExit>());
414+
expect(
415+
output,
416+
containsAllInOrder(<Matcher>[
417+
contains('"description" is too long. pub.dev recommends package '
418+
'descriptions of 60-180 characters.'),
419+
]),
420+
);
421+
});
422+
330423
test('fails when environment section is out of order', () async {
331424
final Directory pluginDirectory = createFakePlugin('plugin', packagesDir);
332425

0 commit comments

Comments
 (0)