diff --git a/pkgs/firehose/CHANGELOG.md b/pkgs/firehose/CHANGELOG.md index 16ce4f6d..c87b7d1b 100644 --- a/pkgs/firehose/CHANGELOG.md +++ b/pkgs/firehose/CHANGELOG.md @@ -1,5 +1,7 @@ -## 0.3.12-dev +## 0.3.12 +- Don't have issues creating PR comments fail the job. +- Write a github workflow summary of the publishing status. - Handle un-publishable packages without a `version`. ## 0.3.11 diff --git a/pkgs/firehose/lib/firehose.dart b/pkgs/firehose/lib/firehose.dart index 374dca25..c71fb63a 100644 --- a/pkgs/firehose/lib/firehose.dart +++ b/pkgs/firehose/lib/firehose.dart @@ -10,7 +10,7 @@ import 'src/github.dart'; import 'src/pub.dart'; import 'src/utils.dart'; -const String _dependabotUser = 'dependabot[bot]'; +const String _botSuffix = '[bot]'; const String _githubActionsUser = 'github-actions[bot]'; @@ -29,7 +29,7 @@ class Firehose { /// - provide feedback on the PR (via a PR comment) about packages which are /// ready to publish Future validate() async { - var github = Github(verbose: true); + var github = Github(); // Do basic validation of our expected env var. if (!_expectEnv(github.githubAuthToken, 'GITHUB_TOKEN')) return; @@ -37,31 +37,49 @@ class Firehose { if (!_expectEnv(github.issueNumber, 'ISSUE_NUMBER')) return; if (!_expectEnv(github.sha, 'GITHUB_SHA')) return; - if (github.actor == _dependabotUser) { - print('Skipping package validation for dependabot PR.'); + if ((github.actor ?? '').endsWith(_botSuffix)) { + print('Skipping package validation for ${github.actor} PRs.'); return; } var results = await _validate(github); - var existingCommentId = await github.findCommentId( - github.repoSlug!, github.issueNumber!, - user: _githubActionsUser, searchTerm: _publishBotTag); - - if (results.hasSuccess) { - var text = '''$_publishBotTag - + var markdownTable = ''' | Package | Version | Status | Publish tag | | :--- | ---: | :--- | ---: | ${results.describeAsMarkdown} -See also the docs at https://github.com/dart-lang/ecosystem/wiki/Publishing-automation. +Documentation at https://github.com/dart-lang/ecosystem/wiki/Publishing-automation. '''; + // Write the publish info status to the job summary. + github.appendStepSummary(markdownTable); + + var existingCommentId = await allowFailure( + github.findCommentId( + github.repoSlug!, + github.issueNumber!, + user: _githubActionsUser, + searchTerm: _publishBotTag, + ), + logError: print, + ); + + if (results.hasSuccess) { + var commentText = '$_publishBotTag\n\n$markdownTable'; + if (existingCommentId == null) { - await github.createComment(github.repoSlug!, github.issueNumber!, text); + await allowFailure( + github.createComment( + github.repoSlug!, github.issueNumber!, commentText), + logError: print, + ); } else { - await github.updateComment(github.repoSlug!, existingCommentId, text); + await allowFailure( + github.updateComment( + github.repoSlug!, existingCommentId, commentText), + logError: print, + ); } } else { if (results.hasError && exitCode == 0) { @@ -69,7 +87,10 @@ See also the docs at https://github.com/dart-lang/ecosystem/wiki/Publishing-auto } if (existingCommentId != null) { - await github.deleteComment(github.repoSlug!, existingCommentId); + await allowFailure( + github.deleteComment(github.repoSlug!, existingCommentId), + logError: print, + ); } } @@ -93,9 +114,9 @@ See also the docs at https://github.com/dart-lang/ecosystem/wiki/Publishing-auto print('pubspec:'); var pubspecVersion = package.pubspec.version; if (pubspecVersion == null) { - var result = Result.info( + var result = Result.fail( package, - 'no version specified; not able to publish.', + "no version specified (perhaps you need a' publish_to: none' entry?)", ); print(result); results.addResult(result); @@ -139,7 +160,7 @@ See also the docs at https://github.com/dart-lang/ecosystem/wiki/Publishing-auto print('No issues found.'); var result = Result.success(package, - 'ready to publish; merge and tag to trigger publishing', repoTag); + '**ready to publish**; merge and tag to publish', repoTag); print(result); results.addResult(result); } @@ -270,8 +291,10 @@ class VerificationResults { return results.map((r) { var sev = r.severity == Severity.error ? '(error) ' : ''; + var tag = r.other == null ? '' : '`${r.other}`'; + return '| package:${r.package.name} | ${r.package.version} | ' - '$sev${r.message} | ${r.other ?? ''} |'; + '$sev${r.message} | $tag |'; }).join('\n'); } } diff --git a/pkgs/firehose/lib/src/github.dart b/pkgs/firehose/lib/src/github.dart index 265595c2..bae622f1 100644 --- a/pkgs/firehose/lib/src/github.dart +++ b/pkgs/firehose/lib/src/github.dart @@ -34,15 +34,33 @@ class Github { String? get actor => _env['GITHUB_ACTOR']; /// Whether we're running withing the context of a GitHub action. - bool get inGithubContext => Platform.environment['GITHUB_ACTIONS'] != null; + bool get inGithubContext => _env['GITHUB_ACTIONS'] != null; /// The short ref name of the branch or tag that triggered the workflow run. /// This value matches the branch or tag name shown on GitHub. For example, /// `feature-branch-1`. - String? get refName => Platform.environment['GITHUB_REF_NAME']; + String? get refName => _env['GITHUB_REF_NAME']; http.Client get httpClient => _httpClient ??= http.Client(); + /// Write the given [markdownSummary] content to the GitHub + /// `GITHUB_STEP_SUMMARY` file. This will cause the markdown output to be + /// appended to the GitHub job summary for the current PR. + /// + /// See also: + /// https://docs.github.com/en/actions/learn-github-actions/variables. + void appendStepSummary(String markdownSummary) { + var summaryPath = _env['GITHUB_STEP_SUMMARY']; + if (summaryPath == null) { + stderr.writeln("'GITHUB_STEP_SUMMARY' doesn't exist."); + return; + } + + var file = File(summaryPath); + file.writeAsStringSync('${markdownSummary.trimRight()}\n\n', + mode: FileMode.append); + } + Future callRestApiGet(Uri uri) async { var token = githubAuthToken!; diff --git a/pkgs/firehose/lib/src/utils.dart b/pkgs/firehose/lib/src/utils.dart index 4d7d9fc1..afd3d94a 100644 --- a/pkgs/firehose/lib/src/utils.dart +++ b/pkgs/firehose/lib/src/utils.dart @@ -64,3 +64,17 @@ class Tag { @override String toString() => tag; } + +/// Await the given [operation]; if there's a exception from the future, we +/// ignore the exception and return `null`. +Future allowFailure( + Future operation, { + required void Function(Object) logError, +}) async { + try { + return await operation; + } catch (e) { + logError(e); + return null; + } +} diff --git a/pkgs/firehose/pubspec.yaml b/pkgs/firehose/pubspec.yaml index 0c05fd3c..83a682de 100644 --- a/pkgs/firehose/pubspec.yaml +++ b/pkgs/firehose/pubspec.yaml @@ -1,6 +1,6 @@ name: firehose description: A tool to automate publishing of Pub packages from GitHub actions. -version: 0.3.12-dev +version: 0.3.12 repository: https://github.com/dart-lang/ecosystem/tree/main/pkgs/firehose environment: