Skip to content

Commit

Permalink
feat: configure pull request
Browse files Browse the repository at this point in the history
Add support for configuring the pull request created by the action.

Closes #57
  • Loading branch information
FantasticFiasco committed Jul 28, 2020
1 parent 417b74b commit 2138a68
Show file tree
Hide file tree
Showing 10 changed files with 668 additions and 62 deletions.
2 changes: 1 addition & 1 deletion .prettierrc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"printWidth": 140,
"printWidth": 120,
"singleQuote": true,
"tabWidth": 4,
"overrides": [
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p

## [Unreleased]

### :zap: Added

- [#57](https://github.com/FantasticFiasco/action-update-license-year/issues/57) Support for configuring pull request

## [1.2.2] - 2020-07-22

### :syringe: Fixed
Expand Down
54 changes: 54 additions & 0 deletions action.vNext.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Update license copyright year(s)
description: Updates the copyright year(s) in your license file and creates a pull request.
author: Mattias Kindborg <mattias.kindborg@gmail.com> (https://twitter.com/FantasticFiasco)
inputs:
token:
description: >
Personal access token (PAT) used to fetch the repository. The PAT is configured
with the local git config, which enables your scripts to run authenticated git
commands. The post-job step removes the PAT.
We recommend using a service account with the least permissions necessary.
Also when generating a new PAT, select the least scopes necessary.
[Learn more about creating and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
default: ${{ github.token }}
required: false
branchName:
description: The branch name template with support for substituting variable {{ currentYear }}.
default: license/copyright-to-{{ currentYear }}
required: false
commitMessage:
description: The git commit message
default: "docs(license): update copyright year(s)"
required: false
commitBody:
description: >
The git commit body that will be appended to commit message, separated by two line returns
default: ""
required: false
prTitle:
description: The title of the new pull request
default: Update license copyright year(s)
required: false
prBody:
description: The contents of the pull request
default: ""
required: false
assignees:
description: >
Comma-separated list with usernames of people to assign when pull request is created
default: ""
required: false
labels:
description: Comma-separated list of labels to add when pull request is created
default: ""
required: false
runs:
using: node12
main: ./dist/index.js
branding:
icon: sunrise
color: orange
11 changes: 10 additions & 1 deletion doc/Action flowchart.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ graph TB
IO_UPLOAD_LICENSE_TO_BRANCH[/Upload license to branch/]
IO_CREATE_BRANCH[/Create branch/]
IO_CREATE_PR[/Create PR/]
IO_ADD_ASSIGNEES[/Add assignees/]
IO_ADD_LABELS[/Add labels/]
%% Decision
D_BRANCH_EXISTS_1{Branch exist?}
D_BRANCH_EXISTS_2{Branch exist?}
D_LICENSE_CHANGED{License changed?}
D_PR_EXISTS{PR exists?}
D_CONFIG_ASSIGNEES{Assignees?}
D_CONFIG_LABELS{Labels?}
%% Process
P_UPDATE_LICENSE[Update license]
Expand All @@ -34,6 +38,11 @@ graph TB
D_BRANCH_EXISTS_2 --No--> IO_CREATE_BRANCH --> IO_UPLOAD_LICENSE_TO_BRANCH
D_BRANCH_EXISTS_2 --Yes--> IO_UPLOAD_LICENSE_TO_BRANCH
IO_UPLOAD_LICENSE_TO_BRANCH --> D_PR_EXISTS
D_PR_EXISTS --No--> IO_CREATE_PR --> END
D_PR_EXISTS --Yes--> END
D_PR_EXISTS --No--> IO_CREATE_PR
IO_CREATE_PR --> D_CONFIG_ASSIGNEES
D_CONFIG_ASSIGNEES --No--> D_CONFIG_LABELS
D_CONFIG_ASSIGNEES --Yes--> IO_ADD_ASSIGNEES --> D_CONFIG_LABELS
D_CONFIG_LABELS --No--> END
D_CONFIG_LABELS --Yes--> IO_ADD_LABELS --> END
```
58 changes: 50 additions & 8 deletions src/Repository.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class Repository {
}

/**
* @param {string} branchName The name of the branch
* @param {string} branchName The branch name
* @param {string} filePath The file path
*/
async getContent(branchName, filePath) {
Expand All @@ -84,10 +84,10 @@ class Repository {
}

/**
* @param {string} branchName The name of the branch
* @param {string} branchName The branch name
* @param {string} filePath The file path
* @param {string} sha The SHA of the file being updated
* @param {string} content The file content
* @param {string} sha The blob SHA of the file being replaced
* @param {string} content The new file content, using ASCII encoding
* @param {string} commitMessage The commit message
*/
async updateContent(branchName, filePath, sha, content, commitMessage) {
Expand All @@ -108,7 +108,7 @@ class Repository {
}

/**
* @param {string} sourceBranchName The name of the source branch
* @param {string} sourceBranchName The name of the branch where your changes are implemented
*/
async hasPullRequest(sourceBranchName) {
try {
Expand All @@ -126,15 +126,17 @@ class Repository {
}

/**
* @param {string} sourceBranchName The name of the source branch
* @param {string} title The title of the pull request
* @param {string} sourceBranchName The name of the branch where your changes are implemented
* @param {string} title The title of the new pull request
* @param {string} body The contents of the pull request
*/
async createPullRequest(sourceBranchName, title) {
async createPullRequest(sourceBranchName, title, body) {
try {
return await this.octokit.pulls.create({
owner: this.owner,
repo: this.name,
title,
body,
head: sourceBranchName,
base: MASTER,
});
Expand All @@ -143,6 +145,46 @@ class Repository {
throw err;
}
}

/**
* @param {number} issueNumber The issue number
* @param {string[]} assignees Usernames of people to assign this issue to. NOTE: Only users
* with push access can add assignees to an issue. Assignees are silently ignored
* otherwise.
*/
async addAssignees(issueNumber, assignees) {
try {
return await this.octokit.issues.addAssignees({
owner: this.owner,
repo: this.name,
issue_number: issueNumber,
assignees,
});
} catch (err) {
err.message = `Error when adding assignees to issue ${issueNumber}: ${err.message}`;
throw err;
}
}

/**
* @param {number} issueNumber The issue number
* @param {string[]} labels The name of the label to add to the issue. Must contain at least
* one label. Note: Alternatively, you can pass a single label as a string or an array of
* labels directly, but GitHub recommends passing an object with the labels key.
*/
async addLabels(issueNumber, labels) {
try {
return await this.octokit.issues.addLabels({
owner: this.owner,
repo: this.name,
issue_number: issueNumber,
labels,
});
} catch (err) {
err.message = `Error when adding labels to issue ${issueNumber}: ${err.message}`;
throw err;
}
}
}

module.exports = Repository;
60 changes: 48 additions & 12 deletions src/action-update-license-year.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,41 @@
const { getInput, setFailed, info } = require('@actions/core');
const { setFailed, info } = require('@actions/core');
const { context } = require('@actions/github');
const { parseConfig } = require('./config');
const { transformLicense } = require('./license');
const Repository = require('./Repository');

const FILENAME = 'LICENSE';
const MASTER = 'master';
const BRANCH_NAME = `license/copyright-to-${new Date().getFullYear()}`;

async function run() {
try {
const { owner, repo } = context.repo;
const token = getInput('token', { required: true });
const {
token,
branchName,
commitMessage,
commitBody,
pullRequestTitle,
pullRequestBody,
assignees,
labels,
} = parseConfig();

info(
`Configuration: ${JSON.stringify({
branchName,
commitMessage,
commitBody,
pullRequestTitle,
pullRequestBody,
assignees,
labels,
})}`
);

const repository = new Repository(owner, repo, token);
const hasBranch = await repository.hasBranch(BRANCH_NAME);
const licenseResponse = await repository.getContent(hasBranch ? BRANCH_NAME : MASTER, FILENAME);
const hasBranch = await repository.hasBranch(branchName);
const licenseResponse = await repository.getContent(hasBranch ? branchName : MASTER, FILENAME);
const license = Buffer.from(licenseResponse.data.content, 'base64').toString('ascii');

const currentYear = new Date().getFullYear();
Expand All @@ -25,21 +46,35 @@ async function run() {
}

if (!hasBranch) {
info(`Create new branch named ${BRANCH_NAME}`);
await repository.createBranch(BRANCH_NAME);
info(`Create new branch named ${branchName}`);
await repository.createBranch(branchName);
}

await repository.updateContent(
BRANCH_NAME,
branchName,
FILENAME,
licenseResponse.data.sha,
updatedLicense,
'docs(license): update copyright year(s)'
commitBody ? `${commitMessage}\n\n${commitBody}` : commitMessage
);

if (!(await repository.hasPullRequest(BRANCH_NAME))) {
if (!(await repository.hasPullRequest(branchName))) {
info('Create new pull request');
await repository.createPullRequest(BRANCH_NAME, 'Update license copyright year(s)');
const createPullRequestResponse = await repository.createPullRequest(
branchName,
pullRequestTitle,
pullRequestBody
);

if (assignees.length > 0) {
info('Add assignees');
await repository.addAssignees(createPullRequestResponse.data.id, assignees);
}

if (labels.length > 0) {
info('Add labels');
await repository.addLabels(createPullRequestResponse.data.id, labels);
}
}
} catch (err) {
setFailed(err.message);
Expand All @@ -48,5 +83,6 @@ async function run() {

module.exports = {
run,
BRANCH_NAME,
MASTER,
FILENAME,
};
76 changes: 76 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
const { getInput } = require('@actions/core');

const CURRENT_YEAR = new Date().getFullYear();

const DEFAULT_BRANCH_NAME = 'license/copyright-to-{ currentYear }';
const DEFAULT_COMMIT_MESSAGE = 'docs(license): update copyright year(s)';
const DEFAULT_COMMIT_BODY = '';
const DEFAULT_PR_TITLE = 'Update license copyright year(s)';
const DEFAULT_PR_BODY = '';
const DEFAULT_ASSIGNEES = '';
const DEFAULT_LABELS = '';

function parseConfig() {
const token = getInput('token', { required: true });
const branchName = substituteVariable(
getInput('branchName') || DEFAULT_BRANCH_NAME,
'currentYear',
CURRENT_YEAR.toString()
);
const commitMessage = getInput('commitMessage') || DEFAULT_COMMIT_MESSAGE;
const commitBody = getInput('commitBody') || DEFAULT_COMMIT_BODY;
const pullRequestTitle = getInput('prTitle') || DEFAULT_PR_TITLE;
const pullRequestBody = getInput('prBody') || DEFAULT_PR_BODY;
const assignees = splitCsv(getInput('assignees') || DEFAULT_ASSIGNEES);
const labels = splitCsv(getInput('labels') || DEFAULT_LABELS);

return {
token,
branchName,
commitMessage,
commitBody,
pullRequestTitle,
pullRequestBody,
assignees,
labels,
};
}

/**
* @param {string} text
* @param {string} variableName
* @param {string} variableValue
*/
function substituteVariable(text, variableName, variableValue) {
const variableRegExp = /{\s*(\w+)\s*}/;
const match = text.match(variableRegExp);
if (!match) {
return text;
}
if (match[1] !== variableName) {
throw new Error(`Configuration "${text}" contains unknown variable "${match[1]}"`);
}
return text.replace(variableRegExp, variableValue);
}

/**
* @param {string} values A comma-separated list of values
*/
function splitCsv(values) {
return values
.split(',')
.map((value) => value.trim()) // Allow whitespaces in comma-separated list of values
.filter((value) => value !== ''); // Remove empty entries
}

module.exports = {
parseConfig,
CURRENT_YEAR,
DEFAULT_BRANCH_NAME,
DEFAULT_COMMIT_MESSAGE,
DEFAULT_COMMIT_BODY,
DEFAULT_PR_TITLE,
DEFAULT_PR_BODY,
DEFAULT_ASSIGNEES,
DEFAULT_LABELS,
};
Loading

0 comments on commit 2138a68

Please sign in to comment.