Skip to content

Commit

Permalink
feat(output-message): add output message and dynamic instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
tomassebestik committed Dec 1, 2023
1 parent 0d2b992 commit 0a1323f
Show file tree
Hide file tree
Showing 6 changed files with 398 additions and 17 deletions.
22 changes: 22 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,23 @@ inputs:
description: 'Enabled rule for check of PR target branch is default branch'
required: false

instructions-enabled:
description: 'Enabled output instructions under issue table in the PR comment'
required: false

instructions-contributions-guide-file:
description: 'Custom name of Contributions Guide file in repository'
required: false

instructions-is-gitlab-mirror:
description: 'Enabled info about this is only Gitlab mirror'
required: false

instructions-cla-link:
description: 'Link to project Contributor License Agreement'
required: false


runs:
using: 'docker'
image: 'Dockerfile'
Expand All @@ -67,3 +84,8 @@ runs:
ENABLE_CHECK_PR_SOURCE_BRANCH_NAME: ${{ inputs.pr-source-branch-rule-enabled }}

ENABLE_CHECK_PR_TARGET_BRANCH: ${{ inputs.pr-target-branch-rule-enabled }}

ENABLE_OUTPUT_INSTRUCTIONS: ${{ inputs.instructions-enabled }}
CONTRIBUTING_GUIDE_FILE: ${{ inputs.instructions-contributions-guide-file }}
IS_GITLAB_MIRROR: ${{ inputs.instructions-is-gitlab-mirror}}
CLA_LINK: ${{ inputs.instructions-cla-link}}
31 changes: 17 additions & 14 deletions src/configParameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
* If an environment variable for a parameter is not set, the system will use the default value from this object.
*/
const defaults = {
instructions: {
enabled: true,
isGitlabMirror: false,
contributingGuideFile: '',
claLink: '',
},
numberOfCommits: { enabled: true, maxCommitsInfo: 2, maxCommitsWarning: 5 },
prDescription: { enabled: true, minLength: 50, ignoredSections: 'related,release,breaking' },
commitMessages: {
Expand All @@ -17,11 +23,6 @@ const defaults = {
prSize: { enabled: true, maxChangedLines: 1000 },
sourceBranchName: { enabled: true },
targetBranch: { enabled: true },
// updatedChangelog: {
// enabled: true,
// filename: 'CHANGELOG.md',
// triggers: 'change,feat,fix,remove,revert',
// },
};

/**
Expand All @@ -30,6 +31,12 @@ const defaults = {
* If an environment variable is not set, the system will fall back to the default value from the `defaults` object.
*/
const config = {
instructions: {
enabled: getEnvBool(process.env.ENABLE_OUTPUT_INSTRUCTIONS) ?? defaults.instructions.enabled,
isGitlabMirror: getEnvBool(process.env.IS_GITLAB_MIRROR) ?? defaults.instructions.isGitlabMirror,
contributingGuideFile: process.env.CONTRIBUTING_GUIDE_FILE || defaults.instructions.contributingGuideFile,
claLink: process.env.CLA_LINK || defaults.instructions.claLink,
},
numberOfCommits: {
enabled: getEnvBool(process.env.ENABLE_CHECK_PR_TOO_MANY_COMMITS) ?? defaults.numberOfCommits.enabled,
maxCommitsInfo: Number(process.env.MAX_COMMITS) || defaults.numberOfCommits.maxCommitsInfo,
Expand Down Expand Up @@ -57,11 +64,6 @@ const config = {
targetBranch: {
enabled: getEnvBool(process.env.ENABLE_CHECK_PR_TARGET_BRANCH) ?? defaults.targetBranch.enabled,
},
// updatedChangelog: {
// enabled: getEnvBool(process.env.ENABLE_CHECK_UPDATED_CHANGELOG) ?? defaults.updatedChangelog.enabled,
// filename: process.env.CHANGELOG_FILENAME || defaults.updatedChangelog.filename,
// triggers: process.env.CHANGELOG_UPDATE_TRIGGERS || defaults.updatedChangelog.triggers,
// },
};

/**
Expand All @@ -76,15 +78,16 @@ const parametersForTable = [
{ ciVar: 'ENABLE_CHECK_PR_SOURCE_BRANCH_NAME', value: config.sourceBranchName.enabled, defaultValue: defaults.sourceBranchName.enabled },
{ ciVar: 'ENABLE_CHECK_PR_TARGET_BRANCH', value: config.targetBranch.enabled, defaultValue: defaults.targetBranch.enabled },
{ ciVar: 'ENABLE_CHECK_PR_TOO_MANY_COMMITS', value: config.numberOfCommits.enabled, defaultValue: defaults.numberOfCommits.enabled },
// { ciVar: 'ENABLE_CHECK_UPDATED_CHANGELOG', value: config.updatedChangelog.enabled, defaultValue: defaults.updatedChangelog.enabled },
// { ciVar: 'CHANGELOG_FILENAME', value: config.updatedChangelog.filename, defaultValue: defaults.updatedChangelog.filename },
// { ciVar: 'CHANGELOG_UPDATE_TRIGGERS', value: config.updatedChangelog.triggers, defaultValue: defaults.updatedChangelog.triggers },
{ ciVar: 'ENABLE_OUTPUT_INSTRUCTIONS', value: config.instructions.enabled, defaultValue: defaults.instructions.enabled },
{ ciVar: 'CLA_LINK', value: config.instructions.claLink, defaultValue: defaults.instructions.claLink },
{ ciVar: 'COMMIT_MESSAGE_ALLOWED_TYPES', value: config.commitMessages.allowedTypes, defaultValue: defaults.commitMessages.allowedTypes },
{ ciVar: 'CONTRIBUTING_GUIDE_FILE', value: config.instructions.contributingGuideFile, defaultValue: defaults.instructions.contributingGuideFile },
{ ciVar: 'IGNORED_SECTIONS_DESCRIPTION', value: config.prDescription.ignoredSections, defaultValue: defaults.prDescription.ignoredSections },
{ ciVar: 'IS_GITLAB_MIRROR', value: config.instructions.isGitlabMirror, defaultValue: defaults.instructions.isGitlabMirror },
{ ciVar: 'MAX_COMMIT_MESSAGE_BODY_LINE', value: config.commitMessages.maxBodyLineLength, defaultValue: defaults.commitMessages.maxBodyLineLength },
{ ciVar: 'MAX_COMMIT_MESSAGE_SUMMARY', value: config.commitMessages.maxSummaryLength, defaultValue: defaults.commitMessages.maxSummaryLength },
{ ciVar: 'MAX_COMMITS', value: config.numberOfCommits.maxCommitsInfo, defaultValue: defaults.numberOfCommits.maxCommitsInfo },
{ ciVar: 'MAX_COMMITS_WARN', value: config.numberOfCommits.maxCommitsWarning, defaultValue: defaults.numberOfCommits.maxCommitsWarning },
{ ciVar: 'MAX_COMMITS', value: config.numberOfCommits.maxCommitsInfo, defaultValue: defaults.numberOfCommits.maxCommitsInfo },
{ ciVar: 'MAX_PR_LINES', value: config.prSize.maxChangedLines, defaultValue: defaults.prSize.maxChangedLines },
{ ciVar: 'MIN_COMMIT_MESSAGE_SUMMARY', value: config.commitMessages.minSummaryLength, defaultValue: defaults.commitMessages.minSummaryLength },
{ ciVar: 'MIN_PR_DESCRIPTION_LENGTH', value: config.prDescription.minLength, defaultValue: defaults.prDescription.minLength },
Expand Down
4 changes: 4 additions & 0 deletions src/dangerfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ruleNumberOfCommits from './ruleNumberOfCommits';
import rulePrDescription from './rulePrDescription';
import ruleSourceBranchName from './ruleSourceBranchName';
import ruleTargetBranch from './ruleTargetBranch';
import outputInstructions from './outputInstructions';

declare const results: DangerResults;
declare const message: (message: string, results?: DangerResults) => void;
Expand All @@ -25,6 +26,9 @@ async function runDangerRules(): Promise<void> {
// Show DangerJS individual checks statuses - visible in CI job tracelog
displayAllOutputStatuses();

// Show DangerJS dynamic output message and instructions - visible in feedback comment
await outputInstructions();

// Show success message if no issues are found
const foundIssues: boolean = Boolean(results.fails.length + results.warnings.length + results.messages.length);
if (!foundIssues) return message('🎉 Good Job! All checks are passing!');
Expand Down
103 changes: 103 additions & 0 deletions src/outputInstructions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { DangerDSLType, DangerResults } from 'danger';
import { Octokit } from '@octokit/rest';
import { config } from './configParameters';

declare const danger: DangerDSLType;
declare const results: DangerResults;
declare const markdown: (message: string, results?: DangerResults) => void;

/**
* Generates a greeting message and a set of instructions for the author of a Pull Request (PR).
*
* This function creates a custom message for the MR author, providing guidance on how to handle
* the automated feedback from DangerJS.
* It includes a set of recommended actions for resolving warnings and information messages, when issues are found,
* and instructions on how to retry DangerJS checks if necessary.
* Message is dynamically generated based on the type of Danger issues found in the MR.
*/
const prAuthorUsername = danger.github.pr.user.login;
const repositoryOwner = danger.github.pr.base.repo.owner.login;
const repositoryName = danger.github.pr.base.repo.name;
const dangerProjectUrl: string = 'https://github.com/espressif/shared-github-dangerjs';

export default async function (): Promise<void> {
// Basic instructions (ALWAYS SHOWN)
let instructions: string = '';
instructions += `👏 <strong>Hi ${prAuthorUsername}, thank you for your Pull Request</strong> and contribution to this project!<br>`;

// Contributors guide link, if exists in the repository
if (config.instructions.contributingGuideFile) {
const contributionsGuideLink = `https://github.com/${repositoryOwner}/${repositoryName}/blob/${await getDefaultBranch()}/${
config.instructions.contributingGuideFile
}`;
instructions += `<hr>`;
instructions += `📘 Please check <a href="${contributionsGuideLink}}">project Contributions Guide</a> of the project for the contribution checklist, information regarding code and documentation style, testing and other topics.<br>`;
}

// Contributor License Agreement, if provided link to it
if (config.instructions.claLink) {
instructions += `<hr>`;
instructions += `🖊️ Please also make sure you have <strong>read and signed</strong> the <a href="${config.instructions.claLink}}">Contributor License Agreement</a> for this project.<br>`;
}

// Basic instructions (ALWAYS SHOWN)
instructions += `<hr>`;
instructions += `<details><summary>Click to see more instructions ...</summary><p>`; // START collapsible section INSTRUCTIONS
instructions += `<br>This automated output is generated by the <a href="${dangerProjectUrl}">PR linter DangerJS</a>, which checks if your Pull Request meets the project's requirements and helps you fix potential issues.<br><br>`;
instructions += `DangerJS is triggered with each "push" event to a Pull Request and modify the contents of this comment.<br><br>`;
instructions += `<strong>Please consider the following:</strong><br>`;
instructions += `- Danger mainly focuses on the PR structure and formatting and can't understand the meaning behind your code or changes.<br>`;
instructions += `- Danger is <strong>not a substitute for human code reviews</strong>; it's still important to request a code review from your colleagues.<br>`;

// If 'warning' or 'error' issues exist, add this to Instructions DangerJS
if (results.fails.length + results.warnings.length) {
instructions += '- <strong>Resolve all warnings (⚠️ )</strong> before requesting a review from human reviewers - they will appreciate it.<br>';
}

// If info issues exist, add this to Instructions DangerJS
if (results.messages.length) {
instructions += `- Addressing info messages (📖) is strongly recommended; they're less critical but valuable.<br>`;
}

// Add (always) retry link as last line of Instructions DangerJS
const retryLinkUrl: string = `https://github.com/${repositoryOwner}/${repositoryName}/actions`;
instructions += `- To manually <a href="${retryLinkUrl}">retry these Danger checks</a>, please navigate to the <kbd>Actions</kbd> tab and re-run last Danger workflow.<br>`;
instructions += `</p></details>`; // END collapsible section INSTRUCTIONS

// Instructions about pull request Review and Merge process
instructions += `<details><summary>Review and merge process you can expect ...</summary><p>`; // START collapsible section REVIEW PROCESS
instructions += `<br>`;
instructions += `<strong>We do welcome contributions in the form of bug reports, feature requests and pull requests via this public GitHub repository.</strong><br><br>`;
if (config.instructions.isGitlabMirror) {
instructions += `<strong>This GitHub project is public mirror of our internal git repository</strong><br><br>`;
instructions += `<strong>1.</strong> An internal issue has been created for the PR, we assign it to the relevant engineer.<br>`;
instructions += `<strong>2.</strong> They review the PR and either approve it or ask you for changes or clarifications.<br>`;
instructions += `<strong>3.</strong> Once the GitHub PR is approved, we synchronize it into our internal git repository.<br>`;
instructions += `<strong>4.</strong> In the internal git repository we do the final review, collect approvals from core owners and make sure all the automated tests are passing.<br>`;
instructions += ` - At this point we may do some adjustments to the proposed change, or extend it by adding tests or documentation.<br>`;
instructions += `<strong>5.</strong> If the change is approved and passes the tests it is merged into the default branch.<br>`;
instructions += `<strong>5.</strong> On next sync from the internal git repository merged change will appear in this public GitHub repository.<br>`;
} else {
instructions += `<strong>1.</strong> An internal issue has been created for the PR, we assign it to the relevant engineer.<br>`;
instructions += `<strong>2.</strong> They review the PR and either approve it or ask you for changes or clarifications.<br>`;
instructions += `<strong>3.</strong> Once the GitHub PR is approved we do the final review, collect approvals from core owners and make sure all the automated tests are passing.<br>`;
instructions += ` - At this point we may do some adjustments to the proposed change, or extend it by adding tests or documentation.<br>`;
instructions += `<strong>4.</strong> If the change is approved and passes the tests it is merged into the default branch.<br>`;
}
instructions += `</p></details>`; // END collapsible section REVIEW PROCESS
instructions += `\n`;

// Create output message
return markdown(instructions);
}

/**
* Fetches the default branch of the repository.
*/
async function getDefaultBranch(): Promise<string> {
const repositoryOwner: string = danger.github.pr.base.repo.owner.login;
const repositoryName: string = danger.github.pr.base.repo.name;
const octokit = new Octokit();
const { data: repo } = await octokit.repos.get({ owner: repositoryOwner, repo: repositoryName });
return repo.default_branch;
}
6 changes: 3 additions & 3 deletions src/ruleTargetBranch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ export default async function (): Promise<void> {

if (prTargetBranch !== defaultBranch) {
warn(`
The **target branch** for this Pull Request **must be the default branch** of the project (\`${defaultBranch}\`).\n
If you would like to add this feature to a different branch, please state this in the PR description and we will consider it.
`);
The **target branch** for this Pull Request **must be the default branch** of the project (\`${defaultBranch}\`).\n
If you would like to add this feature to a different branch, please state this in the PR description and we will consider it.
`);
return recordRuleExitStatus(ruleName, 'Failed');
}

Expand Down
Loading

0 comments on commit 0a1323f

Please sign in to comment.