diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a775914899..88daf0b736 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -101,6 +101,21 @@ jobs: - name: Check for broken internal links run: dart run flutter_site check-links + site-variable-scanner: + name: Check if text can be replaced with site variables + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 + with: + submodules: recursive + - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 + with: + sdk: stable + - name: Fetch Dart packages + run: dart pub get + - name: Check if text can be replaced with site variables + run: dart run dart_site check-site-variable + firebase-validate: name: Validate Firebase configuration runs-on: ubuntu-latest @@ -119,7 +134,7 @@ jobs: deploy: name: Deploy to production - needs: [test, excerpts, linkcheck, firebase-validate] + needs: [test, excerpts, linkcheck, site-variable-scanner, firebase-validate] runs-on: ubuntu-latest if: | github.event_name == 'push' && diff --git a/tool/config/site_variable_scanner.yaml b/tool/config/site_variable_scanner.yaml new file mode 100644 index 0000000000..143b3178f0 --- /dev/null +++ b/tool/config/site_variable_scanner.yaml @@ -0,0 +1,75 @@ +yaml_variable_scanner: + + # File path for YAML variables + yamlFilePath: + - path: "src/_data/site.yml" # YAML path (Glob syntax) + variablePrefix: "site." # Variable prefix. e.g. `a.` -> `a.x.xx` + + # Ignore YAML file path + # + # (Glob Syntax) + ignoreYamlFilePath: + + # Ignore YAML Key + # + # e.g. "^a\.bb$" + # + # (RegExp Syntax) + ignoreYamlKey: + - ^site\.title$ + - ^site\.url$ + - ^site\.main-url$ + - ^site\.github_username$ + - ^site\.branch$ + - ^site\.dart\.sdk\.channel$ + - ^site\.sdk\.channel$ + - ^site\.appmin\. + - ^site\.devmin\. + - ^site\.targetmin\. + - ^site\.appnow\. + - ^site\.bili\.std-size$ + + # File path for check file contents + # + # (Glob Syntax) + checkFilePath: + - "src/content/**/*.md" + - "src/_includes/**/*.md" + + # Ignore file paths to check + # + # (Glob Syntax) + ignoreCheckFilePath: + - "src/content/release/release-notes/**" + - "src/content/tools/devtools/release-notes/**" + - "src/content/community/china-old.md" + - "src/content/community/china/**" + - "src/content/community/tutorials/**" + - "src/content/posts/**" + + # Ignore text that doesn't need to match + # + # e.g. + # - `r"^\s*---([\s\S]*?)---$"` + # - `r"^\s*{%-?\s*comment\s*-?%}([\s\S]*?){%-?\s*endcomment\s*-?%}$"` + # - `r"^\s*$"` + # + # (RegExp Syntax) + ignoreCheckText: + # xxx + - ^\s*([\s\S]*?)$ + + # --- xxx --- + - ^\s*---([\s\S]*?)---$ + + # {%- comment %} xxx {% endcomment -%} + - ^\s*{%-?\s*comment\s*-?%}([\s\S]*?){%-?\s*endcomment\s*-?%}$ + + # + - ^\s*$ + + # ``` xxx ``` + - ^\s*```([\s\S]*?)```$ + + # ` xxx ` + - '`([^`]*)`' diff --git a/tool/flutter_site/lib/flutter_site.dart b/tool/flutter_site/lib/flutter_site.dart index 3aadf4199b..418cee9035 100644 --- a/tool/flutter_site/lib/flutter_site.dart +++ b/tool/flutter_site/lib/flutter_site.dart @@ -9,6 +9,7 @@ import 'src/commands/build.dart'; import 'src/commands/check_all.dart'; import 'src/commands/check_link_references.dart'; import 'src/commands/check_links.dart'; +import 'src/commands/check_site_variable.dart'; import 'src/commands/format_dart.dart'; import 'src/commands/refresh_excerpts.dart'; import 'src/commands/serve.dart'; @@ -29,6 +30,7 @@ final class FlutterSiteCommandRunner extends CommandRunner { addCommand(CheckAllCommand()); addCommand(CheckLinksCommand()); addCommand(CheckLinkReferencesCommand()); + addCommand(CheckSiteVariableCommand()); addCommand(FormatDartCommand()); addCommand(RefreshExcerptsCommand()); addCommand(ServeSiteCommand()); diff --git a/tool/flutter_site/lib/src/commands/check_site_variable.dart b/tool/flutter_site/lib/src/commands/check_site_variable.dart new file mode 100644 index 0000000000..f6b194db98 --- /dev/null +++ b/tool/flutter_site/lib/src/commands/check_site_variable.dart @@ -0,0 +1,56 @@ +// Copyright 2024 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:args/command_runner.dart'; +import 'package:yaml_variable_scanner/yaml_variable_scanner.dart'; + +import '../utils.dart'; + +final class CheckSiteVariableCommand extends Command { + static const String _printModeFlag = 'print-mode'; + + CheckSiteVariableCommand() { + argParser.addOption( + _printModeFlag, + help: 'Configure the amount of information output.', + allowed: ['none', 'detail', 'stats', 'detailAndStats'], + allowedHelp: { + 'none': 'No content.', + 'detail': 'Detail to file lines and columns.', + 'stats': 'Total statistics.', + 'detailAndStats': 'detail & stats.', + }, + defaultsTo: 'detail', + ); + } + + @override + String get description => + 'Scan multiple files for text that can use site variables.'; + + @override + String get name => 'check-site-variable'; + + @override + Future run() async { + final printMode = argResults.get(_printModeFlag, 'detail'); + + try { + final checkResultAll = await YamlVariableScanner.run( + './tool/config/site_variable_scanner.yaml', + stdout, + printMode: PrintMode.values.byName(printMode), + ); + if (checkResultAll.isNotEmpty) return 2; + return 0; + } catch (e, stackTrace) { + stderr.writeln('Error: YamlVariableScanner failed to execute properly!'); + stderr.writeln(e); + stderr.writeln(stackTrace); + return 1; + } + } +} diff --git a/tool/flutter_site/pubspec.yaml b/tool/flutter_site/pubspec.yaml index dea3479eb4..5a2c36d4c7 100644 --- a/tool/flutter_site/pubspec.yaml +++ b/tool/flutter_site/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: args: ^2.4.2 io: ^1.0.4 linkcheck: ^3.0.0 + yaml_variable_scanner: ^0.0.5 path: ^1.9.0 dev_dependencies: