Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit f689280

Browse files
[flutter_plugin_tools] Validate code blocks in readme-check (#5436)
1 parent 656e8c4 commit f689280

File tree

12 files changed

+266
-20
lines changed

12 files changed

+266
-20
lines changed

.cirrus.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,13 @@ task:
101101
always:
102102
format_script: ./script/tool_runner.sh format --fail-on-change
103103
pubspec_script: ./script/tool_runner.sh pubspec-check
104-
readme_script: ./script/tool_runner.sh readme-check
104+
readme_script:
105+
- ./script/tool_runner.sh readme-check
106+
# Re-run with --require-excerpts, skipping packages that still need
107+
# to be converted. Once https://github.com/flutter/flutter/issues/102679
108+
# has been fixed, this can be removed and there can just be a single
109+
# run with --require-excerpts and no exclusions.
110+
- ./script/tool_runner.sh readme-check --require-excerpts --exclude=script/configs/temp_exclude_excerpt.yaml
105111
license_script: dart $PLUGIN_TOOL license-check
106112
- name: federated_safety
107113
# This check is only meaningful for PRs, as it validates changes

packages/camera/camera/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ If editing `Info.plist` as text, add:
4646

4747
Change the minimum Android sdk version to 21 (or higher) in your `android/app/build.gradle` file.
4848

49-
```
49+
```groovy
5050
minSdkVersion 21
5151
```
5252

packages/espresso/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,13 @@ void main() {
8585

8686
The following command line command runs the test locally:
8787

88-
```
88+
```sh
8989
./gradlew app:connectedAndroidTest -Ptarget=`pwd`/../test_driver/example.dart
9090
```
9191

9292
Espresso tests can also be run on [Firebase Test Lab](https://firebase.google.com/docs/test-lab):
9393

94-
```
94+
```sh
9595
./gradlew app:assembleAndroidTest
9696
./gradlew app:assembleDebug -Ptarget=<path_to_test>.dart
9797
gcloud auth activate-service-account --key-file=<PATH_TO_KEY_FILE>

packages/file_selector/file_selector/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ To use this plugin, add `file_selector` as a [dependency in your pubspec.yaml fi
1414
### macOS
1515

1616
You will need to [add an entitlement][entitlement] for either read-only access:
17-
```
17+
```xml
1818
<key>com.apple.security.files.user-selected.read-only</key>
1919
<true/>
2020
```
2121
or read/write access:
22-
```
22+
```xml
2323
<key>com.apple.security.files.user-selected.read-write</key>
2424
<true/>
2525
```

packages/file_selector/file_selector_macos/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ APIs directly.
1717
### Entitlements
1818

1919
You will need to [add an entitlement][4] for either read-only access:
20-
```
20+
```xml
2121
<key>com.apple.security.files.user-selected.read-only</key>
2222
<true/>
2323
```
2424
or read/write access:
25-
```
25+
```xml
2626
<key>com.apple.security.files.user-selected.read-write</key>
2727
<true/>
2828
```

packages/google_sign_in/google_sign_in_web/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Normally `flutter run` starts in a random port. In the case where you need to de
3737

3838
You can tell `flutter run` to listen for requests in a specific host and port with the following:
3939

40-
```
40+
```sh
4141
flutter run -d chrome --web-hostname localhost --web-port 7357
4242
```
4343

packages/url_launcher/url_launcher/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ See the example app for more complex examples.
4646
Add any URL schemes passed to `canLaunchUrl` as `LSApplicationQueriesSchemes` entries in your Info.plist file.
4747

4848
Example:
49-
```
49+
```xml
5050
<key>LSApplicationQueriesSchemes</key>
5151
<array>
5252
<string>https</string>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Packages that have not yet adopted code-excerpt.
2+
#
3+
# This only exists to allow incrementally adopting the new requirement.
4+
# Packages shoud never be added to this list.
5+
6+
# TODO(ecosystem): Remove everything from this list. See
7+
# https://github.com/flutter/flutter/issues/102679
8+
- camera_web
9+
- espresso
10+
- file_selector/file_selector
11+
- google_maps_flutter/google_maps_flutter
12+
- google_sign_in/google_sign_in
13+
- google_sign_in_web
14+
- image_picker/image_picker
15+
- image_picker_for_web
16+
- in_app_purchase/in_app_purchase
17+
- ios_platform_images
18+
- local_auth/local_auth
19+
- path_provider/path_provider
20+
- plugin_platform_interface
21+
- quick_actions/quick_actions
22+
- shared_preferences/shared_preferences
23+
- url_launcher/url_launcher
24+
- video_player/video_player
25+
- webview_flutter/webview_flutter
26+
- webview_flutter_android
27+
- webview_flutter_web

script/tool/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 0.8.4
2+
3+
- `readme-check` now validates that there's a info tag on code blocks to
4+
identify (and for supported languages, syntax highlight) the language.
5+
- `readme-check` now has a `--require-excerpts` flag to require that any Dart
6+
code blocks be managed by `code_excerpter`.
7+
18
## 0.8.3
29

310
- Adds a new `update-excerpts` command to maintain README files using the

script/tool/lib/src/readme_check_command.dart

Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ class ReadmeCheckCommand extends PackageLoopingCommand {
2626
processRunner: processRunner,
2727
platform: platform,
2828
gitDir: gitDir,
29-
);
29+
) {
30+
argParser.addFlag(_requireExcerptsArg,
31+
help: 'Require that Dart code blocks be managed by code-excerpt.');
32+
}
33+
34+
static const String _requireExcerptsArg = 'require-excerpts';
3035

3136
// Standardized capitalizations for platforms that a plugin can support.
3237
static const Map<String, String> _standardPlatformNames = <String, String>{
@@ -61,8 +66,15 @@ class ReadmeCheckCommand extends PackageLoopingCommand {
6166
final Pubspec pubspec = package.parsePubspec();
6267
final bool isPlugin = pubspec.flutter?['plugin'] != null;
6368

69+
final List<String> readmeLines = package.readmeFile.readAsLinesSync();
70+
71+
final String? blockValidationError = _validateCodeBlocks(readmeLines);
72+
if (blockValidationError != null) {
73+
errors.add(blockValidationError);
74+
}
75+
6476
if (isPlugin && (!package.isFederated || package.isAppFacing)) {
65-
final String? error = _validateSupportedPlatforms(package, pubspec);
77+
final String? error = _validateSupportedPlatforms(readmeLines, pubspec);
6678
if (error != null) {
6779
errors.add(error);
6880
}
@@ -73,23 +85,86 @@ class ReadmeCheckCommand extends PackageLoopingCommand {
7385
: PackageResult.fail(errors);
7486
}
7587

88+
/// Validates that code blocks (``` ... ```) follow repository standards.
89+
String? _validateCodeBlocks(List<String> readmeLines) {
90+
final RegExp codeBlockDelimiterPattern = RegExp(r'^\s*```\s*([^ ]*)\s*');
91+
final List<int> missingLanguageLines = <int>[];
92+
final List<int> missingExcerptLines = <int>[];
93+
bool inBlock = false;
94+
for (int i = 0; i < readmeLines.length; ++i) {
95+
final RegExpMatch? match =
96+
codeBlockDelimiterPattern.firstMatch(readmeLines[i]);
97+
if (match == null) {
98+
continue;
99+
}
100+
if (inBlock) {
101+
inBlock = false;
102+
continue;
103+
}
104+
inBlock = true;
105+
106+
final int humanReadableLineNumber = i + 1;
107+
108+
// Ensure that there's a language tag.
109+
final String infoString = match[1] ?? '';
110+
if (infoString.isEmpty) {
111+
missingLanguageLines.add(humanReadableLineNumber);
112+
continue;
113+
}
114+
115+
// Check for code-excerpt usage if requested.
116+
if (getBoolArg(_requireExcerptsArg) && infoString == 'dart') {
117+
const String excerptTagStart = '<?code-excerpt ';
118+
if (i == 0 || !readmeLines[i - 1].trim().startsWith(excerptTagStart)) {
119+
missingExcerptLines.add(humanReadableLineNumber);
120+
}
121+
}
122+
}
123+
124+
String? errorSummary;
125+
126+
if (missingLanguageLines.isNotEmpty) {
127+
for (final int lineNumber in missingLanguageLines) {
128+
printError('${indentation}Code block at line $lineNumber is missing '
129+
'a language identifier.');
130+
}
131+
printError(
132+
'\n${indentation}For each block listed above, add a language tag to '
133+
'the opening block. For instance, for Dart code, use:\n'
134+
'${indentation * 2}```dart\n');
135+
errorSummary = 'Missing language identifier for code block';
136+
}
137+
138+
if (missingExcerptLines.isNotEmpty) {
139+
for (final int lineNumber in missingExcerptLines) {
140+
printError('${indentation}Dart code block at line $lineNumber is not '
141+
'managed by code-excerpt.');
142+
}
143+
printError(
144+
'\n${indentation}For each block listed above, add <?code-excerpt ...> '
145+
'tag on the previous line, and ensure that a build.excerpt.yaml is '
146+
'configured for the source example.\n');
147+
errorSummary ??= 'Missing code-excerpt management for code block';
148+
}
149+
150+
return errorSummary;
151+
}
152+
76153
/// Validates that the plugin has a supported platforms table following the
77154
/// expected format, returning an error string if any issues are found.
78155
String? _validateSupportedPlatforms(
79-
RepositoryPackage package, Pubspec pubspec) {
80-
final List<String> contents = package.readmeFile.readAsLinesSync();
81-
156+
List<String> readmeLines, Pubspec pubspec) {
82157
// Example table following expected format:
83158
// | | Android | iOS | Web |
84159
// |----------------|---------|----------|------------------------|
85160
// | **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] |
86-
final int detailsLineNumber =
87-
contents.indexWhere((String line) => line.startsWith('| **Support**'));
161+
final int detailsLineNumber = readmeLines
162+
.indexWhere((String line) => line.startsWith('| **Support**'));
88163
if (detailsLineNumber == -1) {
89164
return 'No OS support table found';
90165
}
91166
final int osLineNumber = detailsLineNumber - 2;
92-
if (osLineNumber < 0 || !contents[osLineNumber].startsWith('|')) {
167+
if (osLineNumber < 0 || !readmeLines[osLineNumber].startsWith('|')) {
93168
return 'OS support table does not have the expected header format';
94169
}
95170

@@ -111,7 +186,7 @@ class ReadmeCheckCommand extends PackageLoopingCommand {
111186
final YamlMap platformSupportMaps = platformsEntry as YamlMap;
112187
final Set<String> actuallySupportedPlatform =
113188
platformSupportMaps.keys.toSet().cast<String>();
114-
final Iterable<String> documentedPlatforms = contents[osLineNumber]
189+
final Iterable<String> documentedPlatforms = readmeLines[osLineNumber]
115190
.split('|')
116191
.map((String entry) => entry.trim())
117192
.where((String entry) => entry.isNotEmpty);

script/tool/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: flutter_plugin_tools
22
description: Productivity utils for flutter/plugins and flutter/packages
33
repository: https://github.com/flutter/plugins/tree/main/script/tool
4-
version: 0.8.3
4+
version: 0.8.4
55

66
dependencies:
77
args: ^2.1.0

0 commit comments

Comments
 (0)