diff --git a/tooling/codelab_rebuild/bin/codelab_rebuild.dart b/tooling/codelab_rebuild/bin/codelab_rebuild.dart index f6753d0bac..cd9c053628 100644 --- a/tooling/codelab_rebuild/bin/codelab_rebuild.dart +++ b/tooling/codelab_rebuild/bin/codelab_rebuild.dart @@ -21,5 +21,7 @@ void main(List arguments) { final source = arguments.single; final blueprint = Blueprint.load(File(source)); - blueprint.rebuild(File(source).parent); + var basedir = File(source).parent; + blueprint.rebuild(basedir); + blueprint.regenerateCodelabRebuildMd(basedir); } diff --git a/tooling/codelab_rebuild/lib/src/blueprint.dart b/tooling/codelab_rebuild/lib/src/blueprint.dart index 88d39244b2..e0271c2602 100644 --- a/tooling/codelab_rebuild/lib/src/blueprint.dart +++ b/tooling/codelab_rebuild/lib/src/blueprint.dart @@ -9,6 +9,7 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:logging/logging.dart'; import 'rebuild_blueprint.dart'; +import 'regenerate_codelab_rebuild_md.dart'; part 'blueprint.g.dart'; @@ -16,12 +17,21 @@ final _logger = Logger('blueprint'); @JsonSerializable(anyMap: true, checked: true, disallowUnrecognizedKeys: true) class Blueprint { + /// The name of the codelab. @JsonKey(required: true) final String name; + + @JsonKey(name: 'generate-markdown') + final bool generateMarkdown; + @JsonKey(required: true) final List steps; - Blueprint({required this.name, required this.steps}) { + Blueprint({ + required this.name, + required this.steps, + this.generateMarkdown = false, + }) { if (name.isEmpty) { throw ArgumentError.value(name, 'name', 'Cannot be empty.'); } @@ -64,6 +74,10 @@ class Blueprint { /// Rebuild a blueprint in a target directory. Future rebuild(Directory cwd) => rebuildFromBlueprint(cwd, this); + /// Regenerate the `codelab_rebuild.md` from the blueprint. + Future regenerateCodelabRebuildMd(Directory cwd) => + regenerateCodelabRebuildMdFromBlueprint(cwd, this); + Map toJson() => _$BlueprintToJson(this); @override @@ -76,6 +90,11 @@ class BlueprintStep { final String name; final List steps; + @JsonKey(name: 'markdown-content') + final String? markdownContent; + @JsonKey(name: 'markdown-ignore') + final bool markdownIgnore; + final String? path; @JsonKey(name: 'base64-contents') @@ -175,6 +194,8 @@ class BlueprintStep { this.iphoneosDeploymentTarget, this.macosxDeploymentTarget, this.protoc, + this.markdownContent, + this.markdownIgnore = false, }) { if (name.isEmpty) { throw ArgumentError.value(name, 'name', 'Cannot be empty.'); @@ -190,6 +211,9 @@ class BlueprintStep { // Stop is for debugging only. if (stop != null && stop == true) return true; + // Markdown content is stand alone valid + if (markdownContent != null) return true; + // If there aren't sub-steps, then there should be something else to do. if (steps.isEmpty && patch == null && diff --git a/tooling/codelab_rebuild/lib/src/blueprint.g.dart b/tooling/codelab_rebuild/lib/src/blueprint.g.dart index 4f9eed86e9..5bb44ef374 100644 --- a/tooling/codelab_rebuild/lib/src/blueprint.g.dart +++ b/tooling/codelab_rebuild/lib/src/blueprint.g.dart @@ -10,7 +10,7 @@ Blueprint _$BlueprintFromJson(Map json) => $checkedCreate('Blueprint', json, ($checkedConvert) { $checkKeys( json, - allowedKeys: const ['name', 'steps'], + allowedKeys: const ['name', 'generate-markdown', 'steps'], requiredKeys: const ['name', 'steps'], ); final val = Blueprint( @@ -22,12 +22,17 @@ Blueprint _$BlueprintFromJson(Map json) => .map((e) => BlueprintStep.fromJson(e as Map)) .toList(), ), + generateMarkdown: $checkedConvert( + 'generate-markdown', + (v) => v as bool? ?? false, + ), ); return val; - }); + }, fieldKeyMap: const {'generateMarkdown': 'generate-markdown'}); Map _$BlueprintToJson(Blueprint instance) => { 'name': instance.name, + 'generate-markdown': instance.generateMarkdown, 'steps': instance.steps, }; @@ -40,6 +45,8 @@ BlueprintStep _$BlueprintStepFromJson(Map json) => $checkedCreate( allowedKeys: const [ 'name', 'steps', + 'markdown-content', + 'markdown-ignore', 'path', 'base64-contents', 'patch', @@ -156,6 +163,11 @@ BlueprintStep _$BlueprintStepFromJson(Map json) => $checkedCreate( 'protoc', (v) => v == null ? null : Protoc.fromJson(v as Map), ), + markdownContent: $checkedConvert('markdown-content', (v) => v as String?), + markdownIgnore: $checkedConvert( + 'markdown-ignore', + (v) => v as bool? ?? false, + ), ); return val; }, @@ -172,6 +184,8 @@ BlueprintStep _$BlueprintStepFromJson(Map json) => $checkedCreate( 'macOsMainMenuXib': 'full-screen-macos-main-menu-xib', 'iphoneosDeploymentTarget': 'iphoneos-deployment-target', 'macosxDeploymentTarget': 'macosx-deployment-target', + 'markdownContent': 'markdown-content', + 'markdownIgnore': 'markdown-ignore', }, ); @@ -179,6 +193,8 @@ Map _$BlueprintStepToJson(BlueprintStep instance) => { 'name': instance.name, 'steps': instance.steps, + 'markdown-content': instance.markdownContent, + 'markdown-ignore': instance.markdownIgnore, 'path': instance.path, 'base64-contents': instance.base64Contents, 'patch': instance.patch, diff --git a/tooling/codelab_rebuild/lib/src/rebuild_blueprint.dart b/tooling/codelab_rebuild/lib/src/rebuild_blueprint.dart index f16a9721a3..36712b869a 100644 --- a/tooling/codelab_rebuild/lib/src/rebuild_blueprint.dart +++ b/tooling/codelab_rebuild/lib/src/rebuild_blueprint.dart @@ -37,6 +37,11 @@ Future _buildBlueprintStep(Directory cwd, BlueprintStep step) async { exit(0); } + final markdownContent = step.markdownContent; + if (markdownContent != null) { + return; + } + final platforms = step.platforms; if (platforms != null) { if (!platforms.contains(Platform.operatingSystem)) { diff --git a/tooling/codelab_rebuild/lib/src/regenerate_codelab_rebuild_md.dart b/tooling/codelab_rebuild/lib/src/regenerate_codelab_rebuild_md.dart new file mode 100644 index 0000000000..33e6a4d603 --- /dev/null +++ b/tooling/codelab_rebuild/lib/src/regenerate_codelab_rebuild_md.dart @@ -0,0 +1,63 @@ +import 'dart:io'; + +import 'package:path/path.dart' as path; + +import 'blueprint.dart'; + +Future regenerateCodelabRebuildMdFromBlueprint( + Directory cwd, + Blueprint blueprint, +) async { + if (blueprint.generateMarkdown != true) return; + + final buff = StringBuffer(); + + buff.writeln('# ${blueprint.name}'); + buff.writeln(); + + for (final step in blueprint.steps) { + _handleStep(step, buff, 2); + } + + await File( + path.join(cwd.path, 'codelab_rebuild.md'), + ).writeAsString(buff.toString(), flush: true); +} + +void _handleStep(BlueprintStep step, StringBuffer buff, int depth) { + // Skip certain steps + if (step.rmdir != null || step.stripLinesContaining != null) return; + + buff.writeln('${'#' * depth} ${step.name}'); + + if (step.steps.length > 1) { + for (final subStep in step.steps) { + _handleStep(subStep, buff, depth + 1); + } + return; + } + + final flutter = step.flutter; + if (flutter != null) { + buff.write(''' +Run the following Flutter command: + +```console +\$ flutter $flutter +``` + '''); + return; + } + + final dart = step.dart; + if (dart != null) { + buff.write(''' +Run the following Dart command: + +```console +\$ dart $dart +``` + '''); + return; + } +}