From 0ee8514d21744611f09070826e9f7bdba95f136b Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Thu, 12 May 2022 16:49:33 -0700 Subject: [PATCH 01/11] feat: GitHub Action customizations --- .../lib/src/commands/github/github_yaml.dart | 61 ++++++-- mono_repo/lib/src/commands/github/step.dart | 7 +- mono_repo/lib/src/github_config.dart | 138 ++++++++++++++++++ mono_repo/lib/src/package_config.dart | 24 ++- mono_repo/test/generate_test.dart | 108 ++++++++++++++ mono_repo/test/mono_config_test.dart | 82 ++++++++++- mono_repo/test/shared.dart | 6 + 7 files changed, 407 insertions(+), 19 deletions(-) diff --git a/mono_repo/lib/src/commands/github/github_yaml.dart b/mono_repo/lib/src/commands/github/github_yaml.dart index 15972642..11bb5e19 100644 --- a/mono_repo/lib/src/commands/github/github_yaml.dart +++ b/mono_repo/lib/src/commands/github/github_yaml.dart @@ -4,6 +4,8 @@ import 'dart:collection'; +import 'package:path/path.dart'; + import '../../ci_shared.dart'; import '../../github_config.dart'; import '../../mono_config.dart'; @@ -308,15 +310,27 @@ extension on CIJobEntry { ), ); for (var i = 0; i < commands.length; i++) { + final task = job.tasks[i]; + var workingDirectory = task.action?.workingDirectory; + if (workingDirectory != null) { + workingDirectory = posix.normalize( + posix.join(package, workingDirectory), + ); + } commandEntries.add( _CommandEntry( - '$package; ${job.tasks[i].command}', - _commandForOs(job.tasks[i].command), - type: job.tasks[i].type, - // Run this regardless of the success of other steps other than the - // pub step. - ifCondition: "always() && steps.$pubStepId.conclusion == 'success'", - workingDirectory: package, + '$package; ${task.command}', + _commandForOs(task.command), + type: task.type, + id: task.action?.id, + ifCondition: task.action?.condition ?? + // Run this regardless of the success of other steps other than + // the pub step. + "always() && steps.$pubStepId.conclusion == 'success'", + workingDirectory: workingDirectory ?? package, + uses: task.action?.uses, + inputs: task.action?.inputs, + shell: task.action?.shell, ), ); } @@ -425,6 +439,10 @@ class _CommandEntry extends _CommandEntryBase { final String? id; final String? ifCondition; final String workingDirectory; + final String? uses; + final Map? env; + final Map? inputs; + final String? shell; _CommandEntry( super.name, @@ -433,17 +451,32 @@ class _CommandEntry extends _CommandEntryBase { this.type, this.id, this.ifCondition, + this.uses, + this.env, + this.inputs, + this.shell, }); @override Iterable get runContent => [ - Step.run( - id: id, - name: name, - ifContent: ifCondition, - workingDirectory: workingDirectory, - run: run, - ), + if (uses != null) + Step.uses( + id: id, + name: name, + uses: uses, + withContent: inputs, + ifContent: ifCondition, + ) + else + Step.run( + id: id, + name: name, + ifContent: ifCondition, + workingDirectory: workingDirectory, + run: run, + env: env, + shell: shell, + ), ...?type?.afterEachSteps(workingDirectory), ]; } diff --git a/mono_repo/lib/src/commands/github/step.dart b/mono_repo/lib/src/commands/github/step.dart index e5a1533c..cd8ebd97 100644 --- a/mono_repo/lib/src/commands/github/step.dart +++ b/mono_repo/lib/src/commands/github/step.dart @@ -29,6 +29,8 @@ class Step implements YamlLike { @JsonKey(name: 'with') final Map? withContent; + final String? shell; + Step._({ this.id, this.withContent, @@ -38,6 +40,7 @@ class Step implements YamlLike { this.ifContent, this.workingDirectory, this.env, + this.shell, }) { if (run == null) { if (uses == null) { @@ -71,6 +74,7 @@ class Step implements YamlLike { this.ifContent, this.workingDirectory, this.env, + this.shell, }) : uses = null, withContent = null; @@ -82,7 +86,8 @@ class Step implements YamlLike { this.ifContent, }) : run = null, env = null, - workingDirectory = null; + workingDirectory = null, + shell = null; factory Step.fromJson(Map json) => _$StepFromJson(json); diff --git a/mono_repo/lib/src/github_config.dart b/mono_repo/lib/src/github_config.dart index 6e49c5c5..f2d59df7 100644 --- a/mono_repo/lib/src/github_config.dart +++ b/mono_repo/lib/src/github_config.dart @@ -2,6 +2,8 @@ // for details. 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:convert'; + import 'package:json_annotation/json_annotation.dart'; import 'commands/github/job.dart'; @@ -141,6 +143,142 @@ class GitHubWorkflow { factory GitHubWorkflow.fromJson(Map json) => _$GitHubWorkflowFromJson(json); } +/// Extra configuration for customizing the GitHub action context in which a +/// task runs. +class ActionConfig { + ActionConfig({ + this.id, + this.uses, + this.condition, + this.inputs, + this.workingDirectory, + this.shell, + }); + + /// The step's identifier, which can be used to refer to the step and its + /// outputs in the [condition] property of this and other steps. + final String? id; + + /// The GitHub action identifier, e.g. `actions/checkout@v3`. + final String? uses; + + /// The inputs to the action. + /// + /// A map of key-value pairs which are passed to the action's `with` + /// parameter. + final Map? inputs; + + /// The condition on which to run this action. + final String? condition; + + /// The directory in which to run this action. + final String? workingDirectory; + + /// The shell override for this action. + final String? shell; + + factory ActionConfig.fromJson(Map json) { + // Create a copy of unmodifiable `json`. + json = Map.of(json); + + final Object? id = json.remove('id'); + if (id is! String?) { + throw CheckedFromJsonException( + json, + 'id', + 'ActionConfig', + 'Invalid `id` parameter. See GitHub docs for more info: ' + 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsid', + ); + } + final Object? uses = json.remove('uses'); + if (uses is! String?) { + throw CheckedFromJsonException( + json, + 'uses', + 'ActionConfig', + 'Invalid `uses` parameter. See GitHub docs for more info: ' + 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsuses', + ); + } + final Object? inputs = json.remove('with'); + if (inputs is! Map?) { + throw CheckedFromJsonException( + json, + 'with', + 'ActionConfig', + 'Invalid `with` parameter. See GitHub docs for more info: ' + 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepswith', + ); + } + // Transform -> instead of throwing so + // that, for example, numerical values are properly converted. + final mappedInputs = inputs?.map( + (key, value) { + if (value is! String) { + value = jsonEncode(value); + } + return MapEntry(key as String, value); + }, + ); + final Object? condition = json.remove('if'); + if (condition is! String?) { + throw CheckedFromJsonException( + json, + 'if', + 'ActionConfig', + 'Invalid `if` parameter. See GitHub docs for more info: ' + 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsif', + ); + } + final Object? workingDirectory = json.remove('working-directory'); + if (workingDirectory is! String?) { + throw CheckedFromJsonException( + json, + 'working-directory', + 'ActionConfig', + 'Invalid `working-directory` parameter. See GitHub docs for more info: ' + 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun', + ); + } + final Object? shell = json.remove('shell'); + if (shell is! String?) { + throw CheckedFromJsonException( + json, + 'shell', + 'ActionConfig', + 'Invalid `shell` parameter. See GitHub docs for more info: ' + 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsshell', + ); + } + if (json.isNotEmpty) { + throw CheckedFromJsonException( + json, + json.keys.join(','), + 'ActionConfig', + 'Invalid keys', + ); + } + return ActionConfig( + id: id, + uses: uses, + inputs: mappedInputs, + condition: condition, + workingDirectory: workingDirectory, + shell: shell, + ); + } + + Map toJson() => { + if (id != null) 'id': id!, + if (uses != null) 'uses': uses!, + if (inputs != null) 'with': inputs!, + if (condition != null) 'if': condition!, + if (workingDirectory != null) 'working-directory': workingDirectory!, + if (shell != null) 'shell': shell!, + }; +} + Map _parseOn(Map? on, String? cron) { if (on == null) { if (cron == null) { diff --git a/mono_repo/lib/src/package_config.dart b/mono_repo/lib/src/package_config.dart index 76a949fc..ed348e06 100644 --- a/mono_repo/lib/src/package_config.dart +++ b/mono_repo/lib/src/package_config.dart @@ -9,6 +9,7 @@ import 'package:pub_semver/pub_semver.dart'; import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:yaml/yaml.dart'; +import 'github_config.dart'; import 'package_flavor.dart'; import 'raw_config.dart'; import 'task_type.dart'; @@ -307,7 +308,10 @@ class Task { final String command; - Task(this.flavor, this.type, {this.args}) + /// The GitHub action context. + final ActionConfig? action; + + Task(this.flavor, this.type, {this.args, this.action}) : command = type.commandValue(flavor, args).join(' '); /// Parses an individual item under `stages`, which might be a `group` or an @@ -395,8 +399,22 @@ class Task { args = yamlValue[taskName] as String?; } + final actionYaml = yamlValue['action'] as Object?; + if (actionYaml is! Map?) { + throw CheckedFromJsonException( + yamlValue, + 'action', + 'Task', + 'Must be a map', + ); + } + ActionConfig? action; + if (actionYaml != null) { + action = ActionConfig.fromJson(actionYaml); + } + final extraConfig = Set.from(yamlValue.keys) - ..removeAll([taskName, 'os', 'sdk']); + ..removeAll([taskName, 'os', 'sdk', 'action']); // TODO(kevmoo): at some point, support custom configuration here if (extraConfig.isNotEmpty) { @@ -409,7 +427,7 @@ class Task { ); } try { - return Task(flavor, taskType, args: args); + return Task(flavor, taskType, args: args, action: action); } on InvalidTaskConfigException catch (e) { throw CheckedFromJsonException( yamlValue, diff --git a/mono_repo/test/generate_test.dart b/mono_repo/test/generate_test.dart index 0cd22eff..ead8c198 100644 --- a/mono_repo/test/generate_test.dart +++ b/mono_repo/test/generate_test.dart @@ -1227,6 +1227,114 @@ env: ); }); }); + + group('github actions config', () { + test('all parameters', () async { + await d.dir('pkg_a', [ + d.file('pubspec.yaml', ''' +name: pkg_a +'''), + d.file(monoPkgFileName, ''' +sdk: +- dev + +stages: + - custom_step: + - command: + - ./script_a + - ./script_b + - ./script_c + action: + id: custom-scripts + uses: ./.github/actions/my-action + with: + my-key: my-var + my-num: 123 + my-map: {'abc':123} + if: always() + working-directory: ./tool + shell: fish +''') + ]).create(); + + testGenerateBothConfig(printMatcher: anything); + + await d + .file( + defaultGitHubWorkflowFilePath, + contains(''' + - id: custom-scripts + name: "pkg_a; ./script_a && ./script_b && ./script_c" + uses: "./.github/actions/my-action" + with: + my-key: my-var + my-num: "123" + my-map: "{\\"abc\\":123}" + if: always() + working-directory: pkg_a/tool + run: "./script_a && ./script_b && ./script_c" + shell: fish +'''), + ) + .validate(); + }); + + test('merges `id`, `if`, `working-directory`', () async { + await d.dir('pkg_a', [ + d.file('pubspec.yaml', ''' +name: pkg_a +'''), + d.file(monoPkgFileName, ''' +sdk: +- dev + +stages: + - custom_step: + - command: ./script + action: + id: custom-script + if: always() + working-directory: ./tool +''') + ]).create(); + + testGenerateBothConfig(printMatcher: anything); + + await d + .file( + defaultGitHubWorkflowFilePath, + stringContainsInOrder([ + 'id: custom-script', + 'if: always()', + 'working-directory: pkg_a/tool', + ]), + ) + .validate(); + }); + + test('throws for invalid keys', () async { + await d.dir('pkg_a', [ + d.file('pubspec.yaml', ''' +name: pkg_a +'''), + d.file(monoPkgFileName, ''' +sdk: +- dev + +stages: + - custom_step: + - command: ./script + action: + some-key: some-value +''') + ]).create(); + + expect( + testGenerateBothConfig, + throwsACheckedFromJsonException(contains('Invalid')), + ); + }); + }); } String get _subPkgStandardOutput => ''' diff --git a/mono_repo/test/mono_config_test.dart b/mono_repo/test/mono_config_test.dart index 99cae8a6..a2bc5095 100644 --- a/mono_repo/test/mono_config_test.dart +++ b/mono_repo/test/mono_config_test.dart @@ -415,6 +415,14 @@ stages: - test: --preset travis --total-shards 5 --shard-index 0 - test: --preset travis --total-shards 5 --shard-index 1 - test #no args + - group: + - command: npm run build + action: + uses: actions/setup-node@v3 + with: + node-version: 16 + working-directory: ./src + - test: --platform node '''; List get _testConfig1expectedOutput => [ @@ -607,5 +615,77 @@ List get _testConfig1expectedOutput => [ {'flavor': 'dart', 'type': 'test'} ], 'flavor': 'dart' - } + }, + { + 'os': 'linux', + 'package': 'a', + 'sdk': '1.23.0', + 'stageName': 'unit_test', + 'tasks': [ + { + 'flavor': 'dart', + 'name': 'command', + 'args': 'npm run build', + 'action': { + 'uses': 'actions/setup-node@v3', + 'with': {'node-version': '16'}, + 'working-directory': './src', + }, + }, + { + 'flavor': 'dart', + 'name': 'test', + 'args': '--platform node', + } + ], + 'flavor': 'dart' + }, + { + 'os': 'linux', + 'package': 'a', + 'sdk': 'dev', + 'stageName': 'unit_test', + 'tasks': [ + { + 'flavor': 'dart', + 'name': 'command', + 'args': 'npm run build', + 'action': { + 'uses': 'actions/setup-node@v3', + 'with': {'node-version': '16'}, + 'working-directory': './src', + }, + }, + { + 'flavor': 'dart', + 'name': 'test', + 'args': '--platform node', + } + ], + 'flavor': 'dart' + }, + { + 'os': 'linux', + 'package': 'a', + 'sdk': 'stable', + 'stageName': 'unit_test', + 'tasks': [ + { + 'flavor': 'dart', + 'name': 'command', + 'args': 'npm run build', + 'action': { + 'uses': 'actions/setup-node@v3', + 'with': {'node-version': '16'}, + 'working-directory': './src', + }, + }, + { + 'flavor': 'dart', + 'name': 'test', + 'args': '--platform node', + } + ], + 'flavor': 'dart' + }, ]; diff --git a/mono_repo/test/shared.dart b/mono_repo/test/shared.dart index 1ba6390a..b36984fb 100644 --- a/mono_repo/test/shared.dart +++ b/mono_repo/test/shared.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:io'; import 'package:checked_yaml/checked_yaml.dart'; +import 'package:json_annotation/json_annotation.dart'; import 'package:mono_repo/src/ci_shared.dart'; import 'package:mono_repo/src/commands/ci_script/generate.dart'; import 'package:mono_repo/src/commands/generate.dart'; @@ -103,6 +104,11 @@ Matcher throwsAParsedYamlException(Object matcher) => throwsA( ), ); +Matcher throwsACheckedFromJsonException(Object message) => throwsA( + isA() + .having((e) => e.message, 'message', message), + ); + const testConfig2 = r''' sdk: - dev From cba611822e12dcacd38fc9527246c7f1b6acd335 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Thu, 12 May 2022 17:07:55 -0700 Subject: [PATCH 02/11] chore: Refine `inputs` type --- mono_repo/lib/src/commands/github/github_yaml.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mono_repo/lib/src/commands/github/github_yaml.dart b/mono_repo/lib/src/commands/github/github_yaml.dart index 11bb5e19..5018533e 100644 --- a/mono_repo/lib/src/commands/github/github_yaml.dart +++ b/mono_repo/lib/src/commands/github/github_yaml.dart @@ -441,7 +441,7 @@ class _CommandEntry extends _CommandEntryBase { final String workingDirectory; final String? uses; final Map? env; - final Map? inputs; + final Map? inputs; final String? shell; _CommandEntry( From 4f16960324a81d8c45a240952bdbc309bc9c2f50 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Fri, 13 May 2022 09:54:56 -0700 Subject: [PATCH 03/11] chore: Add pub step check to custom `if` --- mono_repo/lib/src/commands/github/github_yaml.dart | 6 +++++- mono_repo/test/generate_test.dart | 7 ++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/mono_repo/lib/src/commands/github/github_yaml.dart b/mono_repo/lib/src/commands/github/github_yaml.dart index 5018533e..fe266e81 100644 --- a/mono_repo/lib/src/commands/github/github_yaml.dart +++ b/mono_repo/lib/src/commands/github/github_yaml.dart @@ -317,13 +317,17 @@ extension on CIJobEntry { posix.join(package, workingDirectory), ); } + var condition = task.action?.condition; + if (condition != null) { + condition += " && steps.$pubStepId.conclusion == 'success'"; + } commandEntries.add( _CommandEntry( '$package; ${task.command}', _commandForOs(task.command), type: task.type, id: task.action?.id, - ifCondition: task.action?.condition ?? + ifCondition: condition ?? // Run this regardless of the success of other steps other than // the pub step. "always() && steps.$pubStepId.conclusion == 'success'", diff --git a/mono_repo/test/generate_test.dart b/mono_repo/test/generate_test.dart index ead8c198..3c32289f 100644 --- a/mono_repo/test/generate_test.dart +++ b/mono_repo/test/generate_test.dart @@ -1251,7 +1251,7 @@ stages: my-key: my-var my-num: 123 my-map: {'abc':123} - if: always() + if: \${{ github.event_name == 'pull_request' }} working-directory: ./tool shell: fish ''') @@ -1270,7 +1270,7 @@ stages: my-key: my-var my-num: "123" my-map: "{\\"abc\\":123}" - if: always() + if: "\${{ github.event_name == 'pull_request' }} && steps.pkg_a_pub_upgrade.conclusion == 'success'" working-directory: pkg_a/tool run: "./script_a && ./script_b && ./script_c" shell: fish @@ -1305,7 +1305,8 @@ stages: defaultGitHubWorkflowFilePath, stringContainsInOrder([ 'id: custom-script', - 'if: always()', + 'if: "always() && ' + r"steps.pkg_a_pub_upgrade.conclusion == 'success'", 'working-directory: pkg_a/tool', ]), ) From e325c69ee28bad2fd0668e291becda95d5b0dbbc Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Sun, 15 May 2022 11:48:45 -0700 Subject: [PATCH 04/11] Migrate to top-level `action` --- .../lib/src/commands/github/github_yaml.dart | 76 ++++---- .../lib/src/commands/github/overrides.dart | 38 ++++ mono_repo/lib/src/commands/github/step.dart | 51 ++++- mono_repo/lib/src/commands/github/step.g.dart | 19 +- mono_repo/lib/src/github_config.dart | 175 +++++++++++++----- mono_repo/lib/src/package_config.dart | 36 ++-- mono_repo/lib/src/task_type.dart | 18 ++ mono_repo/test/generate_test.dart | 53 ++++-- mono_repo/test/mono_config_test.dart | 20 +- 9 files changed, 353 insertions(+), 133 deletions(-) create mode 100644 mono_repo/lib/src/commands/github/overrides.dart diff --git a/mono_repo/lib/src/commands/github/github_yaml.dart b/mono_repo/lib/src/commands/github/github_yaml.dart index fe266e81..beee2f0a 100644 --- a/mono_repo/lib/src/commands/github/github_yaml.dart +++ b/mono_repo/lib/src/commands/github/github_yaml.dart @@ -17,6 +17,7 @@ import '../../user_exception.dart'; import '../../yaml.dart'; import 'action_info.dart'; import 'job.dart'; +import 'overrides.dart'; import 'step.dart'; const _onCompletionStage = '_on_completion'; @@ -305,36 +306,40 @@ extension on CIJobEntry { id: pubStepId, // Run this regardless of the success of other steps other than the // pub step. - ifCondition: "always() && steps.checkout.conclusion == 'success'", + ifContent: "always() && steps.checkout.conclusion == 'success'", workingDirectory: package, ), ); for (var i = 0; i < commands.length; i++) { final task = job.tasks[i]; - var workingDirectory = task.action?.workingDirectory; + final overrides = task.type.overrides; + var workingDirectory = overrides?.workingDirectory; if (workingDirectory != null) { workingDirectory = posix.normalize( posix.join(package, workingDirectory), ); } - var condition = task.action?.condition; - if (condition != null) { - condition += " && steps.$pubStepId.conclusion == 'success'"; + var ifCondition = overrides?.ifContent; + if (ifCondition != null) { + ifCondition += " && steps.$pubStepId.conclusion == 'success'"; } commandEntries.add( _CommandEntry( '$package; ${task.command}', _commandForOs(task.command), type: task.type, - id: task.action?.id, - ifCondition: condition ?? + id: overrides?.id, + ifContent: ifCondition ?? // Run this regardless of the success of other steps other than // the pub step. "always() && steps.$pubStepId.conclusion == 'success'", workingDirectory: workingDirectory ?? package, - uses: task.action?.uses, - inputs: task.action?.inputs, - shell: task.action?.shell, + uses: overrides?.uses, + withContent: overrides?.withContent, + shell: overrides?.shell, + env: overrides?.env, + timeoutMinutes: overrides?.timeoutMinutes, + continueOnError: overrides?.continueOnError, ), ); } @@ -438,49 +443,54 @@ class _CommandEntryBase { Iterable get runContent => [Step.run(name: name, run: run)]; } -class _CommandEntry extends _CommandEntryBase { +class _CommandEntry extends _CommandEntryBase implements GitHubActionOverrides { final TaskType? type; + + @override final String? id; - final String? ifCondition; + + @override + final String? ifContent; + + @override final String workingDirectory; + + @override final String? uses; + + @override final Map? env; - final Map? inputs; + + @override + final Map? withContent; + + @override final String? shell; + @override + final bool? continueOnError; + + @override + final int? timeoutMinutes; + _CommandEntry( super.name, super.run, { required this.workingDirectory, this.type, this.id, - this.ifCondition, + this.ifContent, this.uses, this.env, - this.inputs, + this.withContent, this.shell, + this.continueOnError, + this.timeoutMinutes, }); @override Iterable get runContent => [ - if (uses != null) - Step.uses( - id: id, - name: name, - uses: uses, - withContent: inputs, - ifContent: ifCondition, - ) - else - Step.run( - id: id, - name: name, - ifContent: ifCondition, - workingDirectory: workingDirectory, - run: run, - env: env, - shell: shell, - ), + Step.fromOverrides(this), ...?type?.afterEachSteps(workingDirectory), ]; } diff --git a/mono_repo/lib/src/commands/github/overrides.dart b/mono_repo/lib/src/commands/github/overrides.dart new file mode 100644 index 00000000..84a1fb90 --- /dev/null +++ b/mono_repo/lib/src/commands/github/overrides.dart @@ -0,0 +1,38 @@ +abstract class GitHubActionOverrides { + /// The step's identifier, which can be used to refer to the step and its + /// outputs in the [ifContent] property of this and other steps. + String? get id; + + /// The name of the step. + String? get name; + + /// The shell command to run for this step. + String? get run; + + /// The GitHub action identifier, e.g. `actions/checkout@v3`. + String? get uses; + + /// The inputs to the action. + /// + /// A map of key-value pairs which are passed to the action's `with` + /// parameter. + Map? get withContent; + + /// The condition on which to run this action. + String? get ifContent; + + /// The directory in which to run this action. + String? get workingDirectory; + + /// The shell override for this action. + String? get shell; + + /// The environment variables for the step. + Map? get env; + + /// Prevents a job from failing when a step fails. + bool? get continueOnError; + + /// The number of minutes to allow the step to run. + int? get timeoutMinutes; +} diff --git a/mono_repo/lib/src/commands/github/step.dart b/mono_repo/lib/src/commands/github/step.dart index cd8ebd97..82cb8017 100644 --- a/mono_repo/lib/src/commands/github/step.dart +++ b/mono_repo/lib/src/commands/github/step.dart @@ -5,6 +5,7 @@ import 'package:json_annotation/json_annotation.dart'; import '../../yaml.dart'; +import 'overrides.dart'; part 'step.g.dart'; @@ -13,24 +14,40 @@ part 'step.g.dart'; includeIfNull: false, constructor: '_', ) -class Step implements YamlLike { +class Step implements GitHubActionOverrides, YamlLike { + @override final String? id; + @override final String? name; + @override final String? run; + @override @JsonKey(name: 'if') final String? ifContent; + + @override @JsonKey(name: 'working-directory') final String? workingDirectory; - final Map? env; + @override + final Map? env; + @override final String? uses; + @override @JsonKey(name: 'with') - final Map? withContent; + final Map? withContent; + @override final String? shell; + @override + final bool? continueOnError; + + @override + final int? timeoutMinutes; + Step._({ this.id, this.withContent, @@ -41,7 +58,16 @@ class Step implements YamlLike { this.workingDirectory, this.env, this.shell, + this.continueOnError, + this.timeoutMinutes, }) { + if (name == null) { + throw ArgumentError.value( + name, + 'name', + '`name` must be defined.', + ); + } if (run == null) { if (uses == null) { throw ArgumentError.value( @@ -67,6 +93,21 @@ class Step implements YamlLike { } } + Step.fromOverrides(GitHubActionOverrides overrides) + : this._( + id: overrides.id, + name: overrides.name, + uses: overrides.uses, + withContent: overrides.withContent, + workingDirectory: overrides.workingDirectory, + run: overrides.run, + env: overrides.env, + shell: overrides.shell, + ifContent: overrides.ifContent, + continueOnError: overrides.continueOnError, + timeoutMinutes: overrides.timeoutMinutes, + ); + Step.run({ this.id, required String this.name, @@ -75,6 +116,8 @@ class Step implements YamlLike { this.workingDirectory, this.env, this.shell, + this.continueOnError, + this.timeoutMinutes, }) : uses = null, withContent = null; @@ -84,6 +127,8 @@ class Step implements YamlLike { required this.uses, this.withContent, this.ifContent, + this.continueOnError, + this.timeoutMinutes, }) : run = null, env = null, workingDirectory = null, diff --git a/mono_repo/lib/src/commands/github/step.g.dart b/mono_repo/lib/src/commands/github/step.g.dart index bdae7e96..43ef329a 100644 --- a/mono_repo/lib/src/commands/github/step.g.dart +++ b/mono_repo/lib/src/commands/github/step.g.dart @@ -14,14 +14,26 @@ Step _$StepFromJson(Map json) => $checkedCreate( ($checkedConvert) { final val = Step._( id: $checkedConvert('id', (v) => v as String?), - withContent: $checkedConvert('with', (v) => v as Map?), + withContent: $checkedConvert( + 'with', + (v) => (v as Map?)?.map( + (k, e) => MapEntry(k as String, e), + )), name: $checkedConvert('name', (v) => v as String?), uses: $checkedConvert('uses', (v) => v as String?), run: $checkedConvert('run', (v) => v as String?), ifContent: $checkedConvert('if', (v) => v as String?), workingDirectory: $checkedConvert('working-directory', (v) => v as String?), - env: $checkedConvert('env', (v) => v as Map?), + env: $checkedConvert( + 'env', + (v) => (v as Map?)?.map( + (k, e) => MapEntry(k as String, e as String), + )), + shell: $checkedConvert('shell', (v) => v as String?), + continueOnError: + $checkedConvert('continueOnError', (v) => v as bool?), + timeoutMinutes: $checkedConvert('timeoutMinutes', (v) => v as int?), ); return val; }, @@ -49,5 +61,8 @@ Map _$StepToJson(Step instance) { writeNotNull('env', instance.env); writeNotNull('uses', instance.uses); writeNotNull('with', instance.withContent); + writeNotNull('shell', instance.shell); + writeNotNull('continueOnError', instance.continueOnError); + writeNotNull('timeoutMinutes', instance.timeoutMinutes); return val; } diff --git a/mono_repo/lib/src/github_config.dart b/mono_repo/lib/src/github_config.dart index f2d59df7..301f4052 100644 --- a/mono_repo/lib/src/github_config.dart +++ b/mono_repo/lib/src/github_config.dart @@ -7,6 +7,7 @@ import 'dart:convert'; import 'package:json_annotation/json_annotation.dart'; import 'commands/github/job.dart'; +import 'commands/github/overrides.dart'; part 'github_config.g.dart'; @@ -143,44 +144,80 @@ class GitHubWorkflow { factory GitHubWorkflow.fromJson(Map json) => _$GitHubWorkflowFromJson(json); } -/// Extra configuration for customizing the GitHub action context in which a -/// task runs. -class ActionConfig { - ActionConfig({ +/// Configuration for a single step in a GitHub Actions task. +class GitHubActionConfig implements GitHubActionOverrides { + GitHubActionConfig({ this.id, + this.name, + this.run, this.uses, - this.condition, - this.inputs, + this.ifContent, + this.withContent, this.workingDirectory, this.shell, - }); - - /// The step's identifier, which can be used to refer to the step and its - /// outputs in the [condition] property of this and other steps. + this.env, + this.continueOnError, + this.timeoutMinutes, + this.otherConfig, + }) : assert( + run != null || uses != null, + 'Either `run` or `uses` must be specified', + ); + + @override final String? id; - /// The GitHub action identifier, e.g. `actions/checkout@v3`. + @override + final String? name; + + @override + final String? run; + + @override final String? uses; - /// The inputs to the action. - /// - /// A map of key-value pairs which are passed to the action's `with` - /// parameter. - final Map? inputs; + /// The command identifier for this step, used in caching. + String get command => (run ?? uses)!; + + @override + final Map? withContent; - /// The condition on which to run this action. - final String? condition; + @override + final String? ifContent; - /// The directory in which to run this action. + @override final String? workingDirectory; - /// The shell override for this action. + @override final String? shell; - factory ActionConfig.fromJson(Map json) { + @override + final Map? env; + + @override + final bool? continueOnError; + + @override + final int? timeoutMinutes; + + /// Configuration options not defined by one of the other keys. + final Map? otherConfig; + + factory GitHubActionConfig.fromJson(Map json) { // Create a copy of unmodifiable `json`. json = Map.of(json); + // Transform -> instead of throwing so + // that, for example, numerical values are properly converted. + Map? toEnvMap(Map? map) => map?.map( + (key, value) { + if (value is! String) { + value = jsonEncode(value); + } + return MapEntry(key as String, value); + }, + ); + final Object? id = json.remove('id'); if (id is! String?) { throw CheckedFromJsonException( @@ -191,6 +228,26 @@ class ActionConfig { 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsid', ); } + final Object? name = json.remove('name'); + if (name is! String?) { + throw CheckedFromJsonException( + json, + 'name', + 'ActionConfig', + 'Invalid `name` parameter. See GitHub docs for more info: ' + 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsname', + ); + } + final Object? run = json.remove('run'); + if (run is! String?) { + throw CheckedFromJsonException( + json, + 'run', + 'ActionConfig', + 'Invalid `run` parameter. See GitHub docs for more info: ' + 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun', + ); + } final Object? uses = json.remove('uses'); if (uses is! String?) { throw CheckedFromJsonException( @@ -201,8 +258,8 @@ class ActionConfig { 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsuses', ); } - final Object? inputs = json.remove('with'); - if (inputs is! Map?) { + final Object? withContent = json.remove('with'); + if (withContent is! Map?) { throw CheckedFromJsonException( json, 'with', @@ -211,18 +268,8 @@ class ActionConfig { 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepswith', ); } - // Transform -> instead of throwing so - // that, for example, numerical values are properly converted. - final mappedInputs = inputs?.map( - (key, value) { - if (value is! String) { - value = jsonEncode(value); - } - return MapEntry(key as String, value); - }, - ); - final Object? condition = json.remove('if'); - if (condition is! String?) { + final Object? ifContent = json.remove('if'); + if (ifContent is! String?) { throw CheckedFromJsonException( json, 'if', @@ -251,32 +298,60 @@ class ActionConfig { 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsshell', ); } - if (json.isNotEmpty) { + final Object? env = json.remove('env'); + if (env is! Map?) { + throw CheckedFromJsonException( + json, + 'env', + 'ActionConfig', + 'Invalid `env` parameter. See GitHub docs for more info: ' + 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsenv', + ); + } + final Object? continueOnError = json.remove('continue-on-error'); + if (continueOnError is! bool?) { throw CheckedFromJsonException( json, - json.keys.join(','), + 'continue-on-error', 'ActionConfig', - 'Invalid keys', + 'Invalid `continue-on-error` parameter. See GitHub docs for more info: ' + 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepscontinue-on-error', ); } - return ActionConfig( + final Object? timeoutMinutes = json.remove('timeout-minutes'); + if (timeoutMinutes is! num?) { + throw CheckedFromJsonException( + json, + 'timeout-minutes', + 'ActionConfig', + 'Invalid `timeout-minutes` parameter. See GitHub docs for more info: ' + 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepstimeout-minutes', + ); + } + + if (uses == null && run == null) { + throw CheckedFromJsonException( + json, + 'run,uses', + 'ActionConfig', + 'Either `run` or `uses` must be specified', + ); + } + + return GitHubActionConfig( id: id, uses: uses, - inputs: mappedInputs, - condition: condition, + run: run, + withContent: withContent?.cast(), + ifContent: ifContent, workingDirectory: workingDirectory, shell: shell, + env: toEnvMap(env), + continueOnError: continueOnError, + timeoutMinutes: timeoutMinutes?.toInt(), + otherConfig: json.cast(), ); } - - Map toJson() => { - if (id != null) 'id': id!, - if (uses != null) 'uses': uses!, - if (inputs != null) 'with': inputs!, - if (condition != null) 'if': condition!, - if (workingDirectory != null) 'working-directory': workingDirectory!, - if (shell != null) 'shell': shell!, - }; } Map _parseOn(Map? on, String? cron) { diff --git a/mono_repo/lib/src/package_config.dart b/mono_repo/lib/src/package_config.dart index ed348e06..4cbe6f97 100644 --- a/mono_repo/lib/src/package_config.dart +++ b/mono_repo/lib/src/package_config.dart @@ -308,10 +308,7 @@ class Task { final String command; - /// The GitHub action context. - final ActionConfig? action; - - Task(this.flavor, this.type, {this.args, this.action}) + Task(this.flavor, this.type, {this.args}) : command = type.commandValue(flavor, args).join(' '); /// Parses an individual item under `stages`, which might be a `group` or an @@ -374,6 +371,21 @@ class Task { } final taskName = taskNames.single; + + if (taskName == 'github_action') { + final configYaml = yamlValue['github_action'] as Object?; + if (configYaml is! Map) { + throw CheckedFromJsonException( + yamlValue, + 'github_action', + 'GitHubActionConfig', + 'Must be a map', + ); + } + final config = GitHubActionConfig.fromJson(configYaml); + return Task(flavor, TaskType.githubAction(config)); + } + final taskType = _taskTypeForName(taskName); String? args; @@ -399,20 +411,6 @@ class Task { args = yamlValue[taskName] as String?; } - final actionYaml = yamlValue['action'] as Object?; - if (actionYaml is! Map?) { - throw CheckedFromJsonException( - yamlValue, - 'action', - 'Task', - 'Must be a map', - ); - } - ActionConfig? action; - if (actionYaml != null) { - action = ActionConfig.fromJson(actionYaml); - } - final extraConfig = Set.from(yamlValue.keys) ..removeAll([taskName, 'os', 'sdk', 'action']); @@ -427,7 +425,7 @@ class Task { ); } try { - return Task(flavor, taskType, args: args, action: action); + return Task(flavor, taskType, args: args); } on InvalidTaskConfigException catch (e) { throw CheckedFromJsonException( yamlValue, diff --git a/mono_repo/lib/src/task_type.dart b/mono_repo/lib/src/task_type.dart index bf688cae..138fd0d6 100644 --- a/mono_repo/lib/src/task_type.dart +++ b/mono_repo/lib/src/task_type.dart @@ -3,11 +3,15 @@ // BSD-style license that can be found in the LICENSE file. import 'commands/github/action_info.dart'; +import 'commands/github/overrides.dart'; import 'commands/github/step.dart'; +import 'github_config.dart'; import 'package_flavor.dart'; abstract class TaskType implements Comparable { static const command = _CommandTask(); + const factory TaskType.githubAction(GitHubActionConfig config) = + GitHubActionTaskType; static const _values = [ _FormatTask(), @@ -38,6 +42,8 @@ abstract class TaskType implements Comparable { Iterable afterEachSteps(String packageDirectory) => const Iterable.empty(); + GitHubActionOverrides? get overrides => null; + static Iterable get allowedTaskNames sync* { for (var val in TaskType._values) { yield val.name; @@ -154,3 +160,15 @@ class _TestWithCoverageTask extends TaskType { ]; } } + +class GitHubActionTaskType extends TaskType { + const GitHubActionTaskType(this.overrides) : super._('github_action'); + + @override + final GitHubActionConfig overrides; + + @override + List commandValue(PackageFlavor flavor, String? args) { + throw UnimplementedError(); + } +} diff --git a/mono_repo/test/generate_test.dart b/mono_repo/test/generate_test.dart index 3c32289f..c8889bb3 100644 --- a/mono_repo/test/generate_test.dart +++ b/mono_repo/test/generate_test.dart @@ -1240,12 +1240,12 @@ sdk: stages: - custom_step: - - command: - - ./script_a - - ./script_b - - ./script_c - action: + - action: id: custom-scripts + run: | + ./script_a + ./script_b + ./script_c uses: ./.github/actions/my-action with: my-key: my-var @@ -1254,6 +1254,12 @@ stages: if: \${{ github.event_name == 'pull_request' }} working-directory: ./tool shell: fish + env: + my-key: my-var + my-num: 123 + my-map: {'abc':123} + continue-on-error: false + timeout-minutes: 30 ''') ]).create(); @@ -1264,7 +1270,6 @@ stages: defaultGitHubWorkflowFilePath, contains(''' - id: custom-scripts - name: "pkg_a; ./script_a && ./script_b && ./script_c" uses: "./.github/actions/my-action" with: my-key: my-var @@ -1272,8 +1277,17 @@ stages: my-map: "{\\"abc\\":123}" if: "\${{ github.event_name == 'pull_request' }} && steps.pkg_a_pub_upgrade.conclusion == 'success'" working-directory: pkg_a/tool - run: "./script_a && ./script_b && ./script_c" + run: | + ./script_a + ./script_b + ./script_c shell: fish + env: + my-key: my-var + my-num: "123" + my-map: "{\\"abc\\":123}" + continue-on-error: false + timeout-minutes: 30 '''), ) .validate(); @@ -1290,10 +1304,10 @@ sdk: stages: - custom_step: - - command: ./script - action: + - action: id: custom-script if: always() + run: ./script working-directory: ./tool ''') ]).create(); @@ -1313,7 +1327,7 @@ stages: .validate(); }); - test('throws for invalid keys', () async { + test('allows unknown keys', () async { await d.dir('pkg_a', [ d.file('pubspec.yaml', ''' name: pkg_a @@ -1324,16 +1338,23 @@ sdk: stages: - custom_step: - - command: ./script - action: + - action: + run: ./script some-key: some-value ''') ]).create(); - expect( - testGenerateBothConfig, - throwsACheckedFromJsonException(contains('Invalid')), - ); + testGenerateBothConfig(printMatcher: anything); + + await d + .file( + defaultGitHubWorkflowFilePath, + stringContainsInOrder([ + 'run: ./script', + 'some-key: some-value', + ]), + ) + .validate(); }); }); } diff --git a/mono_repo/test/mono_config_test.dart b/mono_repo/test/mono_config_test.dart index a2bc5095..8e04cb04 100644 --- a/mono_repo/test/mono_config_test.dart +++ b/mono_repo/test/mono_config_test.dart @@ -211,7 +211,7 @@ line 4, column 12: Unsupported value for "group". expected a list of tasks _expectParseThrows( monoYaml, r''' -line 9, column 6: Must have one key of `format`, `analyze`, `test`, `command`, `test_with_coverage`. +line 9, column 6: Must have one key of `format`, `analyze`, `test`, `command`, `test_with_coverage`, `action`. ╷ 9 │ "weird": "thing" │ ^^^^^^^ @@ -234,7 +234,7 @@ line 9, column 6: Must have one key of `format`, `analyze`, `test`, `command`, ` _expectParseThrows( monoYaml, r''' -line 10, column 6: Must have one and only one key of `format`, `analyze`, `test`, `command`, `test_with_coverage`. +line 10, column 6: Must have one and only one key of `format`, `analyze`, `test`, `command`, `test_with_coverage`, `action`. ╷ 10 │ "command": "other thing" │ ^^^^^^^^^ @@ -416,8 +416,8 @@ stages: - test: --preset travis --total-shards 5 --shard-index 1 - test #no args - group: - - command: npm run build - action: + - action: + run: npm run build uses: actions/setup-node@v3 with: node-version: 16 @@ -624,9 +624,9 @@ List get _testConfig1expectedOutput => [ 'tasks': [ { 'flavor': 'dart', - 'name': 'command', - 'args': 'npm run build', + 'name': 'action', 'action': { + 'run': 'npm run build', 'uses': 'actions/setup-node@v3', 'with': {'node-version': '16'}, 'working-directory': './src', @@ -648,9 +648,9 @@ List get _testConfig1expectedOutput => [ 'tasks': [ { 'flavor': 'dart', - 'name': 'command', - 'args': 'npm run build', + 'name': 'action', 'action': { + 'run': 'npm run build', 'uses': 'actions/setup-node@v3', 'with': {'node-version': '16'}, 'working-directory': './src', @@ -672,9 +672,9 @@ List get _testConfig1expectedOutput => [ 'tasks': [ { 'flavor': 'dart', - 'name': 'command', - 'args': 'npm run build', + 'name': 'action', 'action': { + 'run': 'npm run build', 'uses': 'actions/setup-node@v3', 'with': {'node-version': '16'}, 'working-directory': './src', From 248eee85774bc5ec4f2c15b8626b0dc1f27040f5 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Sun, 12 Jun 2022 08:47:34 -0700 Subject: [PATCH 05/11] Rename `action` to `github_action` --- mono_repo/test/generate_test.dart | 6 +++--- mono_repo/test/mono_config_test.dart | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mono_repo/test/generate_test.dart b/mono_repo/test/generate_test.dart index c8889bb3..63a673ee 100644 --- a/mono_repo/test/generate_test.dart +++ b/mono_repo/test/generate_test.dart @@ -1240,7 +1240,7 @@ sdk: stages: - custom_step: - - action: + - github_action: id: custom-scripts run: | ./script_a @@ -1304,7 +1304,7 @@ sdk: stages: - custom_step: - - action: + - github_action: id: custom-script if: always() run: ./script @@ -1338,7 +1338,7 @@ sdk: stages: - custom_step: - - action: + - github_action: run: ./script some-key: some-value ''') diff --git a/mono_repo/test/mono_config_test.dart b/mono_repo/test/mono_config_test.dart index 8e04cb04..5a04eb63 100644 --- a/mono_repo/test/mono_config_test.dart +++ b/mono_repo/test/mono_config_test.dart @@ -211,7 +211,7 @@ line 4, column 12: Unsupported value for "group". expected a list of tasks _expectParseThrows( monoYaml, r''' -line 9, column 6: Must have one key of `format`, `analyze`, `test`, `command`, `test_with_coverage`, `action`. +line 9, column 6: Must have one key of `format`, `analyze`, `test`, `command`, `test_with_coverage`, `github_action`. ╷ 9 │ "weird": "thing" │ ^^^^^^^ @@ -234,7 +234,7 @@ line 9, column 6: Must have one key of `format`, `analyze`, `test`, `command`, ` _expectParseThrows( monoYaml, r''' -line 10, column 6: Must have one and only one key of `format`, `analyze`, `test`, `command`, `test_with_coverage`, `action`. +line 10, column 6: Must have one and only one key of `format`, `analyze`, `test`, `command`, `test_with_coverage`, `github_action`. ╷ 10 │ "command": "other thing" │ ^^^^^^^^^ @@ -416,7 +416,7 @@ stages: - test: --preset travis --total-shards 5 --shard-index 1 - test #no args - group: - - action: + - github_action: run: npm run build uses: actions/setup-node@v3 with: @@ -624,8 +624,8 @@ List get _testConfig1expectedOutput => [ 'tasks': [ { 'flavor': 'dart', - 'name': 'action', - 'action': { + 'name': 'github_action', + 'githubAction': { 'run': 'npm run build', 'uses': 'actions/setup-node@v3', 'with': {'node-version': '16'}, @@ -648,8 +648,8 @@ List get _testConfig1expectedOutput => [ 'tasks': [ { 'flavor': 'dart', - 'name': 'action', - 'action': { + 'name': 'github_action', + 'githubAction': { 'run': 'npm run build', 'uses': 'actions/setup-node@v3', 'with': {'node-version': '16'}, @@ -672,8 +672,8 @@ List get _testConfig1expectedOutput => [ 'tasks': [ { 'flavor': 'dart', - 'name': 'action', - 'action': { + 'name': 'github_action', + 'githubAction': { 'run': 'npm run build', 'uses': 'actions/setup-node@v3', 'with': {'node-version': '16'}, From f200385b8ab57839c6f964b06fa10dcd00e1e358 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Sat, 16 Jul 2022 12:22:26 -0700 Subject: [PATCH 06/11] Fix tests --- .../lib/src/commands/github/action_info.dart | 4 +- .../lib/src/commands/github/github_yaml.dart | 2 +- .../lib/src/commands/github/overrides.dart | 2 +- mono_repo/lib/src/commands/github/step.dart | 53 +++++++------ mono_repo/lib/src/commands/github/step.g.dart | 14 ++-- mono_repo/lib/src/github_config.dart | 44 +++++------ mono_repo/lib/src/task_type.dart | 23 +++--- mono_repo/test/generate_test.dart | 77 ++++++++++++------- mono_repo/test/mono_config_test.dart | 39 ++-------- .../github_output_test_with_coverage.txt | 4 +- 10 files changed, 136 insertions(+), 126 deletions(-) diff --git a/mono_repo/lib/src/commands/github/action_info.dart b/mono_repo/lib/src/commands/github/action_info.dart index d69cfa36..19e7eb27 100644 --- a/mono_repo/lib/src/commands/github/action_info.dart +++ b/mono_repo/lib/src/commands/github/action_info.dart @@ -39,7 +39,7 @@ enum ActionInfo implements Comparable { Step usage({ required String name, String? id, - Map? withContent, + Map? withContent, }) { final step = Step.uses( uses: '$repo@$version', @@ -65,7 +65,7 @@ Job _coverageCompletionJob() => Job( withContent: { // https://docs.github.com/en/actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow 'github-token': r'${{ secrets.GITHUB_TOKEN }}', - 'parallel-finished': true + 'parallel-finished': 'true' }, ) ], diff --git a/mono_repo/lib/src/commands/github/github_yaml.dart b/mono_repo/lib/src/commands/github/github_yaml.dart index beee2f0a..4af288f2 100644 --- a/mono_repo/lib/src/commands/github/github_yaml.dart +++ b/mono_repo/lib/src/commands/github/github_yaml.dart @@ -462,7 +462,7 @@ class _CommandEntry extends _CommandEntryBase implements GitHubActionOverrides { final Map? env; @override - final Map? withContent; + final Map? withContent; @override final String? shell; diff --git a/mono_repo/lib/src/commands/github/overrides.dart b/mono_repo/lib/src/commands/github/overrides.dart index 84a1fb90..a91edff3 100644 --- a/mono_repo/lib/src/commands/github/overrides.dart +++ b/mono_repo/lib/src/commands/github/overrides.dart @@ -16,7 +16,7 @@ abstract class GitHubActionOverrides { /// /// A map of key-value pairs which are passed to the action's `with` /// parameter. - Map? get withContent; + Map? get withContent; /// The condition on which to run this action. String? get ifContent; diff --git a/mono_repo/lib/src/commands/github/step.dart b/mono_repo/lib/src/commands/github/step.dart index 82cb8017..3c688b02 100644 --- a/mono_repo/lib/src/commands/github/step.dart +++ b/mono_repo/lib/src/commands/github/step.dart @@ -37,15 +37,17 @@ class Step implements GitHubActionOverrides, YamlLike { final String? uses; @override @JsonKey(name: 'with') - final Map? withContent; + final Map? withContent; @override final String? shell; @override + @JsonKey(name: 'continue-on-error') final bool? continueOnError; @override + @JsonKey(name: 'timeout-minutes') final int? timeoutMinutes; Step._({ @@ -61,13 +63,6 @@ class Step implements GitHubActionOverrides, YamlLike { this.continueOnError, this.timeoutMinutes, }) { - if (name == null) { - throw ArgumentError.value( - name, - 'name', - '`name` must be defined.', - ); - } if (run == null) { if (uses == null) { throw ArgumentError.value( @@ -93,25 +88,35 @@ class Step implements GitHubActionOverrides, YamlLike { } } - Step.fromOverrides(GitHubActionOverrides overrides) - : this._( - id: overrides.id, - name: overrides.name, - uses: overrides.uses, - withContent: overrides.withContent, - workingDirectory: overrides.workingDirectory, - run: overrides.run, - env: overrides.env, - shell: overrides.shell, - ifContent: overrides.ifContent, - continueOnError: overrides.continueOnError, - timeoutMinutes: overrides.timeoutMinutes, - ); + factory Step.fromOverrides(GitHubActionOverrides overrides) { + if (overrides.uses != null) { + return Step._( + id: overrides.id, + name: overrides.name, + uses: overrides.uses, + withContent: overrides.withContent, + ifContent: overrides.ifContent, + continueOnError: overrides.continueOnError, + timeoutMinutes: overrides.timeoutMinutes, + ); + } + return Step._( + id: overrides.id, + name: overrides.name, + run: overrides.run, + workingDirectory: overrides.workingDirectory, + env: overrides.env, + shell: overrides.shell, + ifContent: overrides.ifContent, + continueOnError: overrides.continueOnError, + timeoutMinutes: overrides.timeoutMinutes, + ); + } Step.run({ this.id, required String this.name, - required this.run, + required String this.run, this.ifContent, this.workingDirectory, this.env, @@ -124,7 +129,7 @@ class Step implements GitHubActionOverrides, YamlLike { Step.uses({ this.id, required String this.name, - required this.uses, + required String this.uses, this.withContent, this.ifContent, this.continueOnError, diff --git a/mono_repo/lib/src/commands/github/step.g.dart b/mono_repo/lib/src/commands/github/step.g.dart index 43ef329a..2747aaa9 100644 --- a/mono_repo/lib/src/commands/github/step.g.dart +++ b/mono_repo/lib/src/commands/github/step.g.dart @@ -17,7 +17,7 @@ Step _$StepFromJson(Map json) => $checkedCreate( withContent: $checkedConvert( 'with', (v) => (v as Map?)?.map( - (k, e) => MapEntry(k as String, e), + (k, e) => MapEntry(k as String, e as String), )), name: $checkedConvert('name', (v) => v as String?), uses: $checkedConvert('uses', (v) => v as String?), @@ -32,15 +32,17 @@ Step _$StepFromJson(Map json) => $checkedCreate( )), shell: $checkedConvert('shell', (v) => v as String?), continueOnError: - $checkedConvert('continueOnError', (v) => v as bool?), - timeoutMinutes: $checkedConvert('timeoutMinutes', (v) => v as int?), + $checkedConvert('continue-on-error', (v) => v as bool?), + timeoutMinutes: $checkedConvert('timeout-minutes', (v) => v as int?), ); return val; }, fieldKeyMap: const { 'withContent': 'with', 'ifContent': 'if', - 'workingDirectory': 'working-directory' + 'workingDirectory': 'working-directory', + 'continueOnError': 'continue-on-error', + 'timeoutMinutes': 'timeout-minutes' }, ); @@ -62,7 +64,7 @@ Map _$StepToJson(Step instance) { writeNotNull('uses', instance.uses); writeNotNull('with', instance.withContent); writeNotNull('shell', instance.shell); - writeNotNull('continueOnError', instance.continueOnError); - writeNotNull('timeoutMinutes', instance.timeoutMinutes); + writeNotNull('continue-on-error', instance.continueOnError); + writeNotNull('timeout-minutes', instance.timeoutMinutes); return val; } diff --git a/mono_repo/lib/src/github_config.dart b/mono_repo/lib/src/github_config.dart index 301f4052..f4ae013a 100644 --- a/mono_repo/lib/src/github_config.dart +++ b/mono_repo/lib/src/github_config.dart @@ -158,7 +158,6 @@ class GitHubActionConfig implements GitHubActionOverrides { this.env, this.continueOnError, this.timeoutMinutes, - this.otherConfig, }) : assert( run != null || uses != null, 'Either `run` or `uses` must be specified', @@ -176,11 +175,8 @@ class GitHubActionConfig implements GitHubActionOverrides { @override final String? uses; - /// The command identifier for this step, used in caching. - String get command => (run ?? uses)!; - @override - final Map? withContent; + final Map? withContent; @override final String? ifContent; @@ -200,9 +196,6 @@ class GitHubActionConfig implements GitHubActionOverrides { @override final int? timeoutMinutes; - /// Configuration options not defined by one of the other keys. - final Map? otherConfig; - factory GitHubActionConfig.fromJson(Map json) { // Create a copy of unmodifiable `json`. json = Map.of(json); @@ -223,7 +216,7 @@ class GitHubActionConfig implements GitHubActionOverrides { throw CheckedFromJsonException( json, 'id', - 'ActionConfig', + 'GitHubActionConfig', 'Invalid `id` parameter. See GitHub docs for more info: ' 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsid', ); @@ -233,7 +226,7 @@ class GitHubActionConfig implements GitHubActionOverrides { throw CheckedFromJsonException( json, 'name', - 'ActionConfig', + 'GitHubActionConfig', 'Invalid `name` parameter. See GitHub docs for more info: ' 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsname', ); @@ -243,7 +236,7 @@ class GitHubActionConfig implements GitHubActionOverrides { throw CheckedFromJsonException( json, 'run', - 'ActionConfig', + 'GitHubActionConfig', 'Invalid `run` parameter. See GitHub docs for more info: ' 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun', ); @@ -253,7 +246,7 @@ class GitHubActionConfig implements GitHubActionOverrides { throw CheckedFromJsonException( json, 'uses', - 'ActionConfig', + 'GitHubActionConfig', 'Invalid `uses` parameter. See GitHub docs for more info: ' 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsuses', ); @@ -263,7 +256,7 @@ class GitHubActionConfig implements GitHubActionOverrides { throw CheckedFromJsonException( json, 'with', - 'ActionConfig', + 'GitHubActionConfig', 'Invalid `with` parameter. See GitHub docs for more info: ' 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepswith', ); @@ -273,7 +266,7 @@ class GitHubActionConfig implements GitHubActionOverrides { throw CheckedFromJsonException( json, 'if', - 'ActionConfig', + 'GitHubActionConfig', 'Invalid `if` parameter. See GitHub docs for more info: ' 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsif', ); @@ -283,7 +276,7 @@ class GitHubActionConfig implements GitHubActionOverrides { throw CheckedFromJsonException( json, 'working-directory', - 'ActionConfig', + 'GitHubActionConfig', 'Invalid `working-directory` parameter. See GitHub docs for more info: ' 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun', ); @@ -293,7 +286,7 @@ class GitHubActionConfig implements GitHubActionOverrides { throw CheckedFromJsonException( json, 'shell', - 'ActionConfig', + 'GitHubActionConfig', 'Invalid `shell` parameter. See GitHub docs for more info: ' 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsshell', ); @@ -303,7 +296,7 @@ class GitHubActionConfig implements GitHubActionOverrides { throw CheckedFromJsonException( json, 'env', - 'ActionConfig', + 'GitHubActionConfig', 'Invalid `env` parameter. See GitHub docs for more info: ' 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsenv', ); @@ -313,7 +306,7 @@ class GitHubActionConfig implements GitHubActionOverrides { throw CheckedFromJsonException( json, 'continue-on-error', - 'ActionConfig', + 'GitHubActionConfig', 'Invalid `continue-on-error` parameter. See GitHub docs for more info: ' 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepscontinue-on-error', ); @@ -323,7 +316,7 @@ class GitHubActionConfig implements GitHubActionOverrides { throw CheckedFromJsonException( json, 'timeout-minutes', - 'ActionConfig', + 'GitHubActionConfig', 'Invalid `timeout-minutes` parameter. See GitHub docs for more info: ' 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepstimeout-minutes', ); @@ -333,23 +326,30 @@ class GitHubActionConfig implements GitHubActionOverrides { throw CheckedFromJsonException( json, 'run,uses', - 'ActionConfig', + 'GitHubActionConfig', 'Either `run` or `uses` must be specified', ); } + if (json.isNotEmpty) { + throw CheckedFromJsonException( + json, + json.keys.join(','), + 'GitHubActionConfig', + 'Invalid keys', + ); + } return GitHubActionConfig( id: id, uses: uses, run: run, - withContent: withContent?.cast(), + withContent: toEnvMap(withContent), ifContent: ifContent, workingDirectory: workingDirectory, shell: shell, env: toEnvMap(env), continueOnError: continueOnError, timeoutMinutes: timeoutMinutes?.toInt(), - otherConfig: json.cast(), ); } } diff --git a/mono_repo/lib/src/task_type.dart b/mono_repo/lib/src/task_type.dart index 138fd0d6..51ff1b4f 100644 --- a/mono_repo/lib/src/task_type.dart +++ b/mono_repo/lib/src/task_type.dart @@ -11,7 +11,7 @@ import 'package_flavor.dart'; abstract class TaskType implements Comparable { static const command = _CommandTask(); const factory TaskType.githubAction(GitHubActionConfig config) = - GitHubActionTaskType; + _GitHubActionTaskType; static const _values = [ _FormatTask(), @@ -49,10 +49,13 @@ abstract class TaskType implements Comparable { yield val.name; yield* val.alternates; } + yield _GitHubActionTaskType._name; } - static final prettyTaskList = - TaskType._values.map((t) => '`${t.name}`').join(', '); + static final prettyTaskList = [ + ...TaskType._values.map((t) => '`${t.name}`'), + '`${_GitHubActionTaskType._name}`', + ].join(', '); static TaskType taskFromName(String input) => TaskType._values.singleWhere( (element) => @@ -154,21 +157,23 @@ class _TestWithCoverageTask extends TaskType { 'github-token': r'${{ secrets.GITHUB_TOKEN }}', 'path-to-lcov': '$packageDirectory/coverage/lcov.info', 'flag-name': 'coverage_$countString', - 'parallel': true, + 'parallel': 'true', }, ), ]; } } -class GitHubActionTaskType extends TaskType { - const GitHubActionTaskType(this.overrides) : super._('github_action'); +class _GitHubActionTaskType extends TaskType { + const _GitHubActionTaskType(this.overrides) : super._(_name); + + static const _name = 'github_action'; @override final GitHubActionConfig overrides; @override - List commandValue(PackageFlavor flavor, String? args) { - throw UnimplementedError(); - } + List commandValue(PackageFlavor flavor, String? args) => [ + if (overrides.run != null) overrides.run!, + ]; } diff --git a/mono_repo/test/generate_test.dart b/mono_repo/test/generate_test.dart index 63a673ee..35d719f7 100644 --- a/mono_repo/test/generate_test.dart +++ b/mono_repo/test/generate_test.dart @@ -1229,7 +1229,7 @@ env: }); group('github actions config', () { - test('all parameters', () async { + test('custom uses', () async { await d.dir('pkg_a', [ d.file('pubspec.yaml', ''' name: pkg_a @@ -1242,16 +1242,54 @@ stages: - custom_step: - github_action: id: custom-scripts - run: | - ./script_a - ./script_b - ./script_c uses: ./.github/actions/my-action with: my-key: my-var my-num: 123 my-map: {'abc':123} if: \${{ github.event_name == 'pull_request' }} + continue-on-error: false + timeout-minutes: 30 +''') + ]).create(); + + testGenerateBothConfig(printMatcher: anything); + + await d + .file( + defaultGitHubWorkflowFilePath, + contains(''' + if: "\${{ github.event_name == 'pull_request' }} && steps.pkg_a_pub_upgrade.conclusion == 'success'" + uses: "./.github/actions/my-action" + with: + my-key: my-var + my-num: "123" + my-map: "{\\"abc\\":123}" + continue-on-error: false + timeout-minutes: 30 +'''), + ) + .validate(); + }); + + test('custom run', () async { + await d.dir('pkg_a', [ + d.file('pubspec.yaml', ''' +name: pkg_a +'''), + d.file(monoPkgFileName, ''' +sdk: +- dev + +stages: + - custom_step: + - github_action: + id: custom-scripts + run: | + ./script_a + ./script_b + ./script_c + if: \${{ github.event_name == 'pull_request' }} working-directory: ./tool shell: fish env: @@ -1269,23 +1307,17 @@ stages: .file( defaultGitHubWorkflowFilePath, contains(''' - - id: custom-scripts - uses: "./.github/actions/my-action" - with: - my-key: my-var - my-num: "123" - my-map: "{\\"abc\\":123}" - if: "\${{ github.event_name == 'pull_request' }} && steps.pkg_a_pub_upgrade.conclusion == 'success'" - working-directory: pkg_a/tool run: | ./script_a ./script_b ./script_c - shell: fish + if: "\${{ github.event_name == 'pull_request' }} && steps.pkg_a_pub_upgrade.conclusion == 'success'" + working-directory: pkg_a/tool env: my-key: my-var my-num: "123" my-map: "{\\"abc\\":123}" + shell: fish continue-on-error: false timeout-minutes: 30 '''), @@ -1327,7 +1359,7 @@ stages: .validate(); }); - test('allows unknown keys', () async { + test('disallows unknown keys', () async { await d.dir('pkg_a', [ d.file('pubspec.yaml', ''' name: pkg_a @@ -1344,17 +1376,10 @@ stages: ''') ]).create(); - testGenerateBothConfig(printMatcher: anything); - - await d - .file( - defaultGitHubWorkflowFilePath, - stringContainsInOrder([ - 'run: ./script', - 'some-key: some-value', - ]), - ) - .validate(); + expect( + testGenerateBothConfig, + throwsACheckedFromJsonException('Invalid keys'), + ); }); }); } diff --git a/mono_repo/test/mono_config_test.dart b/mono_repo/test/mono_config_test.dart index 5a04eb63..52440717 100644 --- a/mono_repo/test/mono_config_test.dart +++ b/mono_repo/test/mono_config_test.dart @@ -622,19 +622,10 @@ List get _testConfig1expectedOutput => [ 'sdk': '1.23.0', 'stageName': 'unit_test', 'tasks': [ + {'flavor': 'dart', 'type': 'github_action'}, { 'flavor': 'dart', - 'name': 'github_action', - 'githubAction': { - 'run': 'npm run build', - 'uses': 'actions/setup-node@v3', - 'with': {'node-version': '16'}, - 'working-directory': './src', - }, - }, - { - 'flavor': 'dart', - 'name': 'test', + 'type': 'test', 'args': '--platform node', } ], @@ -646,19 +637,10 @@ List get _testConfig1expectedOutput => [ 'sdk': 'dev', 'stageName': 'unit_test', 'tasks': [ + {'flavor': 'dart', 'type': 'github_action'}, { 'flavor': 'dart', - 'name': 'github_action', - 'githubAction': { - 'run': 'npm run build', - 'uses': 'actions/setup-node@v3', - 'with': {'node-version': '16'}, - 'working-directory': './src', - }, - }, - { - 'flavor': 'dart', - 'name': 'test', + 'type': 'test', 'args': '--platform node', } ], @@ -670,19 +652,10 @@ List get _testConfig1expectedOutput => [ 'sdk': 'stable', 'stageName': 'unit_test', 'tasks': [ + {'flavor': 'dart', 'type': 'github_action'}, { 'flavor': 'dart', - 'name': 'github_action', - 'githubAction': { - 'run': 'npm run build', - 'uses': 'actions/setup-node@v3', - 'with': {'node-version': '16'}, - 'working-directory': './src', - }, - }, - { - 'flavor': 'dart', - 'name': 'test', + 'type': 'test', 'args': '--platform node', } ], diff --git a/mono_repo/test/script_integration_outputs/github_output_test_with_coverage.txt b/mono_repo/test/script_integration_outputs/github_output_test_with_coverage.txt index 2b0256f1..0feb6f71 100644 --- a/mono_repo/test/script_integration_outputs/github_output_test_with_coverage.txt +++ b/mono_repo/test/script_integration_outputs/github_output_test_with_coverage.txt @@ -52,7 +52,7 @@ jobs: github-token: "${{ secrets.GITHUB_TOKEN }}" path-to-lcov: pkg_a/coverage/lcov.info flag-name: coverage_00 - parallel: true + parallel: "true" job_002: name: test; macos; Dart dev; chrome tests runs-on: macos-latest @@ -91,6 +91,6 @@ jobs: uses: coverallsapp/github-action@master with: github-token: "${{ secrets.GITHUB_TOKEN }}" - parallel-finished: true + parallel-finished: "true" needs: - job_001 From 524502eb3a9395474d5a44989867745cc330756b Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Sat, 16 Jul 2022 12:28:13 -0700 Subject: [PATCH 07/11] Clean up --- mono_repo/lib/src/package_config.dart | 2 +- mono_repo/test/mono_config_test.dart | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/mono_repo/lib/src/package_config.dart b/mono_repo/lib/src/package_config.dart index 4cbe6f97..df2bd99f 100644 --- a/mono_repo/lib/src/package_config.dart +++ b/mono_repo/lib/src/package_config.dart @@ -412,7 +412,7 @@ class Task { } final extraConfig = Set.from(yamlValue.keys) - ..removeAll([taskName, 'os', 'sdk', 'action']); + ..removeAll([taskName, 'os', 'sdk']); // TODO(kevmoo): at some point, support custom configuration here if (extraConfig.isNotEmpty) { diff --git a/mono_repo/test/mono_config_test.dart b/mono_repo/test/mono_config_test.dart index 52440717..a99c7f19 100644 --- a/mono_repo/test/mono_config_test.dart +++ b/mono_repo/test/mono_config_test.dart @@ -417,11 +417,10 @@ stages: - test #no args - group: - github_action: - run: npm run build uses: actions/setup-node@v3 with: node-version: 16 - working-directory: ./src + - command: npm run build - test: --platform node '''; @@ -623,6 +622,7 @@ List get _testConfig1expectedOutput => [ 'stageName': 'unit_test', 'tasks': [ {'flavor': 'dart', 'type': 'github_action'}, + {'flavor': 'dart', 'type': 'command', 'args': 'npm run build'}, { 'flavor': 'dart', 'type': 'test', @@ -638,6 +638,7 @@ List get _testConfig1expectedOutput => [ 'stageName': 'unit_test', 'tasks': [ {'flavor': 'dart', 'type': 'github_action'}, + {'flavor': 'dart', 'type': 'command', 'args': 'npm run build'}, { 'flavor': 'dart', 'type': 'test', @@ -653,6 +654,7 @@ List get _testConfig1expectedOutput => [ 'stageName': 'unit_test', 'tasks': [ {'flavor': 'dart', 'type': 'github_action'}, + {'flavor': 'dart', 'type': 'command', 'args': 'npm run build'}, { 'flavor': 'dart', 'type': 'test', From b4af2ae4fc10d88e8b2d66413c2026a589101bc1 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Mon, 18 Jul 2022 06:23:57 -0700 Subject: [PATCH 08/11] Add missing license header --- mono_repo/lib/src/commands/github/overrides.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mono_repo/lib/src/commands/github/overrides.dart b/mono_repo/lib/src/commands/github/overrides.dart index a91edff3..b2e272fe 100644 --- a/mono_repo/lib/src/commands/github/overrides.dart +++ b/mono_repo/lib/src/commands/github/overrides.dart @@ -1,3 +1,7 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + abstract class GitHubActionOverrides { /// The step's identifier, which can be used to refer to the step and its /// outputs in the [ifContent] property of this and other steps. From 860bc7a9a7ef2c10ec059d9afb8606a29a18f195 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 18 Jul 2022 12:42:09 -0700 Subject: [PATCH 09/11] fix for CI --- .github/workflows/dart.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 3795334a..88526797 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -553,12 +553,12 @@ jobs: github-token: "${{ secrets.GITHUB_TOKEN }}" path-to-lcov: mono_repo/coverage/lcov.info flag-name: coverage_00 - parallel: true + parallel: "true" - name: Upload coverage to codecov.io uses: codecov/codecov-action@master with: files: mono_repo/coverage/lcov.info - fail_ci_if_error: true + fail_ci_if_error: "true" name: coverage_00 needs: - job_001 @@ -611,12 +611,12 @@ jobs: github-token: "${{ secrets.GITHUB_TOKEN }}" path-to-lcov: mono_repo/coverage/lcov.info flag-name: coverage_01 - parallel: true + parallel: "true" - name: Upload coverage to codecov.io uses: codecov/codecov-action@master with: files: mono_repo/coverage/lcov.info - fail_ci_if_error: true + fail_ci_if_error: "true" name: coverage_01 needs: - job_001 @@ -712,12 +712,12 @@ jobs: github-token: "${{ secrets.GITHUB_TOKEN }}" path-to-lcov: test_pkg/coverage/lcov.info flag-name: coverage_02 - parallel: true + parallel: "true" - name: Upload coverage to codecov.io uses: codecov/codecov-action@master with: files: test_pkg/coverage/lcov.info - fail_ci_if_error: true + fail_ci_if_error: "true" name: coverage_02 needs: - job_001 @@ -882,7 +882,7 @@ jobs: uses: coverallsapp/github-action@master with: github-token: "${{ secrets.GITHUB_TOKEN }}" - parallel-finished: true + parallel-finished: "true" needs: - job_015 - job_016 From db9505a779645d07f5913db62ef89307fcc81ed2 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 18 Jul 2022 12:45:33 -0700 Subject: [PATCH 10/11] revert type change to withContent --- .github/workflows/dart.yml | 14 +++++++------- mono_repo/lib/src/commands/github/action_info.dart | 4 ++-- mono_repo/lib/src/commands/github/github_yaml.dart | 2 +- mono_repo/lib/src/commands/github/overrides.dart | 2 +- mono_repo/lib/src/commands/github/step.dart | 2 +- mono_repo/lib/src/commands/github/step.g.dart | 2 +- mono_repo/lib/src/github_config.dart | 2 +- mono_repo/lib/src/task_type.dart | 5 +++-- .../github_output_test_with_coverage.txt | 4 ++-- 9 files changed, 19 insertions(+), 18 deletions(-) diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 88526797..3795334a 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -553,12 +553,12 @@ jobs: github-token: "${{ secrets.GITHUB_TOKEN }}" path-to-lcov: mono_repo/coverage/lcov.info flag-name: coverage_00 - parallel: "true" + parallel: true - name: Upload coverage to codecov.io uses: codecov/codecov-action@master with: files: mono_repo/coverage/lcov.info - fail_ci_if_error: "true" + fail_ci_if_error: true name: coverage_00 needs: - job_001 @@ -611,12 +611,12 @@ jobs: github-token: "${{ secrets.GITHUB_TOKEN }}" path-to-lcov: mono_repo/coverage/lcov.info flag-name: coverage_01 - parallel: "true" + parallel: true - name: Upload coverage to codecov.io uses: codecov/codecov-action@master with: files: mono_repo/coverage/lcov.info - fail_ci_if_error: "true" + fail_ci_if_error: true name: coverage_01 needs: - job_001 @@ -712,12 +712,12 @@ jobs: github-token: "${{ secrets.GITHUB_TOKEN }}" path-to-lcov: test_pkg/coverage/lcov.info flag-name: coverage_02 - parallel: "true" + parallel: true - name: Upload coverage to codecov.io uses: codecov/codecov-action@master with: files: test_pkg/coverage/lcov.info - fail_ci_if_error: "true" + fail_ci_if_error: true name: coverage_02 needs: - job_001 @@ -882,7 +882,7 @@ jobs: uses: coverallsapp/github-action@master with: github-token: "${{ secrets.GITHUB_TOKEN }}" - parallel-finished: "true" + parallel-finished: true needs: - job_015 - job_016 diff --git a/mono_repo/lib/src/commands/github/action_info.dart b/mono_repo/lib/src/commands/github/action_info.dart index c41cd8a8..f66f8589 100644 --- a/mono_repo/lib/src/commands/github/action_info.dart +++ b/mono_repo/lib/src/commands/github/action_info.dart @@ -53,7 +53,7 @@ enum ActionInfo implements Comparable { Step usage({ String? name, String? id, - Map? withContent, + Map? withContent, }) { name ??= this.name; final step = Step.uses( @@ -80,7 +80,7 @@ Job _coverageCompletionJob() => Job( withContent: { // https://docs.github.com/en/actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow 'github-token': r'${{ secrets.GITHUB_TOKEN }}', - 'parallel-finished': 'true' + 'parallel-finished': true }, ) ], diff --git a/mono_repo/lib/src/commands/github/github_yaml.dart b/mono_repo/lib/src/commands/github/github_yaml.dart index 992dabdc..40812735 100644 --- a/mono_repo/lib/src/commands/github/github_yaml.dart +++ b/mono_repo/lib/src/commands/github/github_yaml.dart @@ -462,7 +462,7 @@ class _CommandEntry extends _CommandEntryBase implements GitHubActionOverrides { final Map? env; @override - final Map? withContent; + final Map? withContent; @override final String? shell; diff --git a/mono_repo/lib/src/commands/github/overrides.dart b/mono_repo/lib/src/commands/github/overrides.dart index b2e272fe..c8e48866 100644 --- a/mono_repo/lib/src/commands/github/overrides.dart +++ b/mono_repo/lib/src/commands/github/overrides.dart @@ -20,7 +20,7 @@ abstract class GitHubActionOverrides { /// /// A map of key-value pairs which are passed to the action's `with` /// parameter. - Map? get withContent; + Map? get withContent; /// The condition on which to run this action. String? get ifContent; diff --git a/mono_repo/lib/src/commands/github/step.dart b/mono_repo/lib/src/commands/github/step.dart index 3c688b02..264a3eb6 100644 --- a/mono_repo/lib/src/commands/github/step.dart +++ b/mono_repo/lib/src/commands/github/step.dart @@ -37,7 +37,7 @@ class Step implements GitHubActionOverrides, YamlLike { final String? uses; @override @JsonKey(name: 'with') - final Map? withContent; + final Map? withContent; @override final String? shell; diff --git a/mono_repo/lib/src/commands/github/step.g.dart b/mono_repo/lib/src/commands/github/step.g.dart index 2747aaa9..074527d9 100644 --- a/mono_repo/lib/src/commands/github/step.g.dart +++ b/mono_repo/lib/src/commands/github/step.g.dart @@ -17,7 +17,7 @@ Step _$StepFromJson(Map json) => $checkedCreate( withContent: $checkedConvert( 'with', (v) => (v as Map?)?.map( - (k, e) => MapEntry(k as String, e as String), + (k, e) => MapEntry(k as String, e), )), name: $checkedConvert('name', (v) => v as String?), uses: $checkedConvert('uses', (v) => v as String?), diff --git a/mono_repo/lib/src/github_config.dart b/mono_repo/lib/src/github_config.dart index f4ae013a..23d6ad6f 100644 --- a/mono_repo/lib/src/github_config.dart +++ b/mono_repo/lib/src/github_config.dart @@ -176,7 +176,7 @@ class GitHubActionConfig implements GitHubActionOverrides { final String? uses; @override - final Map? withContent; + final Map? withContent; @override final String? ifContent; diff --git a/mono_repo/lib/src/task_type.dart b/mono_repo/lib/src/task_type.dart index 0db8c95f..bc5239f3 100644 --- a/mono_repo/lib/src/task_type.dart +++ b/mono_repo/lib/src/task_type.dart @@ -12,6 +12,7 @@ import 'package_flavor.dart'; abstract class TaskType implements Comparable { static const command = _CommandTask(); + const factory TaskType.githubAction(GitHubActionConfig config) = _GitHubActionTaskType; @@ -168,14 +169,14 @@ class _TestWithCoverageTask extends TaskType { 'github-token': r'${{ secrets.GITHUB_TOKEN }}', 'path-to-lcov': '$packageDirectory/coverage/lcov.info', 'flag-name': 'coverage_$countString', - 'parallel': 'true', + 'parallel': true, }, ), if (config.coverageProcessors.contains(CoverageProcessor.codecov)) ActionInfo.codecov.usage( withContent: { 'files': '$packageDirectory/coverage/lcov.info', - 'fail_ci_if_error': 'true', + 'fail_ci_if_error': true, 'name': 'coverage_$countString', }, ), diff --git a/mono_repo/test/script_integration_outputs/github_output_test_with_coverage.txt b/mono_repo/test/script_integration_outputs/github_output_test_with_coverage.txt index e45ff15c..b36c01e2 100644 --- a/mono_repo/test/script_integration_outputs/github_output_test_with_coverage.txt +++ b/mono_repo/test/script_integration_outputs/github_output_test_with_coverage.txt @@ -52,7 +52,7 @@ jobs: github-token: "${{ secrets.GITHUB_TOKEN }}" path-to-lcov: pkg_a/coverage/lcov.info flag-name: coverage_00 - parallel: "true" + parallel: true job_002: name: test; macos; Dart dev; chrome tests runs-on: macos-latest @@ -91,6 +91,6 @@ jobs: uses: coverallsapp/github-action@master with: github-token: "${{ secrets.GITHUB_TOKEN }}" - parallel-finished: "true" + parallel-finished: true needs: - job_001 From 9ad3d973bf8ac55bbaddfdb812d1535a4c705891 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Mon, 25 Jul 2022 13:17:06 -0700 Subject: [PATCH 11/11] Fix naming Two issues fixed: 1. `name` overrides were not being passed correctly to methods/ctors - whoops! 2. Default `name` should consider either `run` or `uses` depending on which is passed. --- mono_repo/lib/src/commands/github/github_yaml.dart | 2 +- mono_repo/lib/src/github_config.dart | 1 + mono_repo/lib/src/task_type.dart | 1 + mono_repo/test/generate_test.dart | 2 ++ 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mono_repo/lib/src/commands/github/github_yaml.dart b/mono_repo/lib/src/commands/github/github_yaml.dart index 40812735..a38df958 100644 --- a/mono_repo/lib/src/commands/github/github_yaml.dart +++ b/mono_repo/lib/src/commands/github/github_yaml.dart @@ -323,7 +323,7 @@ extension on CIJobEntry { } commandEntries.add( _CommandEntry( - '$package; ${task.command}', + overrides?.name ?? '$package; ${task.command}', _commandForOs(task.command), type: task.type, id: overrides?.id, diff --git a/mono_repo/lib/src/github_config.dart b/mono_repo/lib/src/github_config.dart index 23d6ad6f..443a4786 100644 --- a/mono_repo/lib/src/github_config.dart +++ b/mono_repo/lib/src/github_config.dart @@ -341,6 +341,7 @@ class GitHubActionConfig implements GitHubActionOverrides { return GitHubActionConfig( id: id, + name: name, uses: uses, run: run, withContent: toEnvMap(withContent), diff --git a/mono_repo/lib/src/task_type.dart b/mono_repo/lib/src/task_type.dart index bc5239f3..77f05542 100644 --- a/mono_repo/lib/src/task_type.dart +++ b/mono_repo/lib/src/task_type.dart @@ -194,6 +194,7 @@ class _GitHubActionTaskType extends TaskType { @override List commandValue(PackageFlavor flavor, String? args) => [ + if (overrides.uses != null) overrides.uses!, if (overrides.run != null) overrides.run!, ]; } diff --git a/mono_repo/test/generate_test.dart b/mono_repo/test/generate_test.dart index c5f506a8..405df751 100644 --- a/mono_repo/test/generate_test.dart +++ b/mono_repo/test/generate_test.dart @@ -1245,6 +1245,7 @@ stages: - custom_step: - github_action: id: custom-scripts + name: 'My Custom Scripts' uses: ./.github/actions/my-action with: my-key: my-var @@ -1262,6 +1263,7 @@ stages: .file( defaultGitHubWorkflowFilePath, contains(''' + name: My Custom Scripts if: "\${{ github.event_name == 'pull_request' }} && steps.pkg_a_pub_upgrade.conclusion == 'success'" uses: "./.github/actions/my-action" with: