Merge pull request #118 from melody-universe/remove-dependencies
Remove runtime dependencies
This commit is contained in:
commit
08a8bfff16
|
@ -0,0 +1,20 @@
|
||||||
|
name: Confirm Pull Request
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
concurrency: end-to-end-test
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
end-to-end-tests:
|
||||||
|
name: Conduct end-to-end tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- run: npm ci
|
||||||
|
name: Install dependencies
|
||||||
|
- run: npm run end-to-end-test
|
||||||
|
env:
|
||||||
|
TEST_REPO: ${{ secrets.TEST_REPO }}
|
||||||
|
TEST_USER: ${{ secrets.TEST_USER }}
|
||||||
|
TEST_TOKEN: ${{ secrets.TEST_TOKEN }}
|
|
@ -17,10 +17,6 @@ jobs:
|
||||||
ref: ${{ github.ref }}
|
ref: ${{ github.ref }}
|
||||||
- name: 'cat package.json'
|
- name: 'cat package.json'
|
||||||
run: cat ./package.json
|
run: cat ./package.json
|
||||||
- name: 'Setup Node.js'
|
|
||||||
uses: 'actions/setup-node@v1'
|
|
||||||
with:
|
|
||||||
node-version: 14
|
|
||||||
- name: 'Automated Version Bump'
|
- name: 'Automated Version Bump'
|
||||||
id: version-bump
|
id: version-bump
|
||||||
uses: 'phips28/gh-action-bump-version@master'
|
uses: 'phips28/gh-action-bump-version@master'
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
node_modules
|
node_modules
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
/test-repo
|
||||||
|
/.env
|
35
Dockerfile
35
Dockerfile
|
@ -1,35 +0,0 @@
|
||||||
# Use the latest version of Node.js
|
|
||||||
#
|
|
||||||
# You may prefer the full image:
|
|
||||||
# FROM node
|
|
||||||
#
|
|
||||||
# or even an alpine image (a smaller, faster, less-feature-complete image):
|
|
||||||
# FROM node:alpine
|
|
||||||
#
|
|
||||||
# You can specify a version:
|
|
||||||
# FROM node:10-slim
|
|
||||||
FROM node:15.12.0-slim
|
|
||||||
|
|
||||||
# Labels for GitHub to read your action
|
|
||||||
LABEL "com.github.actions.name"="Automated version bump for npm packages."
|
|
||||||
LABEL "com.github.actions.description"="Automated version bump for npm packages."
|
|
||||||
# Here are all of the available icons: https://feathericons.com/
|
|
||||||
LABEL "com.github.actions.icon"="chevron-up"
|
|
||||||
# And all of the available colors: https://developer.github.com/actions/creating-github-actions/creating-a-docker-container/#label
|
|
||||||
LABEL "com.github.actions.color"="blue"
|
|
||||||
|
|
||||||
# Copy the package.json and package-lock.json
|
|
||||||
COPY package*.json ./
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
RUN apt-get update
|
|
||||||
RUN apt-get -y install git
|
|
||||||
|
|
||||||
RUN npm ci --only=production
|
|
||||||
|
|
||||||
|
|
||||||
# Copy the rest of your action's code
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Run `node /index.js`
|
|
||||||
ENTRYPOINT ["node", "/index.js"]
|
|
|
@ -1,8 +1,8 @@
|
||||||
name: Automated Version Bump
|
name: Automated Version Bump
|
||||||
description: Automated version bump for npm packages.
|
description: Automated version bump for npm packages.
|
||||||
runs:
|
runs:
|
||||||
using: docker
|
using: node12
|
||||||
image: Dockerfile
|
main: index.js
|
||||||
branding:
|
branding:
|
||||||
icon: chevron-up
|
icon: chevron-up
|
||||||
color: blue
|
color: blue
|
||||||
|
|
105
index.js
105
index.js
|
@ -1,5 +1,7 @@
|
||||||
const { Toolkit } = require('actions-toolkit');
|
const { execSync, spawn } = require('child_process');
|
||||||
const { execSync } = require('child_process');
|
const { existsSync } = require('fs');
|
||||||
|
const { EOL } = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
// Change working directory if user defined PACKAGEJSON_DIR
|
// Change working directory if user defined PACKAGEJSON_DIR
|
||||||
if (process.env.PACKAGEJSON_DIR) {
|
if (process.env.PACKAGEJSON_DIR) {
|
||||||
|
@ -7,10 +9,11 @@ if (process.env.PACKAGEJSON_DIR) {
|
||||||
process.chdir(process.env.GITHUB_WORKSPACE);
|
process.chdir(process.env.GITHUB_WORKSPACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run your GitHub Action!
|
const workspace = process.env.GITHUB_WORKSPACE;
|
||||||
Toolkit.run(async (tools) => {
|
|
||||||
const pkg = tools.getPackageJSON();
|
(async () => {
|
||||||
const event = tools.context.payload;
|
const pkg = getPackageJson();
|
||||||
|
const event = process.env.GITHUB_EVENT_PATH ? require(process.env.GITHUB_EVENT_PATH) : {};
|
||||||
|
|
||||||
if (!event.commits) {
|
if (!event.commits) {
|
||||||
console.log("Couldn't find any commits in this event, incrementing patch version...");
|
console.log("Couldn't find any commits in this event, incrementing patch version...");
|
||||||
|
@ -25,7 +28,7 @@ Toolkit.run(async (tools) => {
|
||||||
const isVersionBump = messages.find((message) => commitMessageRegex.test(message)) !== undefined;
|
const isVersionBump = messages.find((message) => commitMessageRegex.test(message)) !== undefined;
|
||||||
|
|
||||||
if (isVersionBump) {
|
if (isVersionBump) {
|
||||||
tools.exit.success('No action necessary because we found a previous bump!');
|
exitSuccess('No action necessary because we found a previous bump!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,14 +104,14 @@ Toolkit.run(async (tools) => {
|
||||||
|
|
||||||
// case: if nothing of the above matches
|
// case: if nothing of the above matches
|
||||||
if (version === null) {
|
if (version === null) {
|
||||||
tools.exit.success('No version keywords found, skipping bump.');
|
exitSuccess('No version keywords found, skipping bump.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// case: if user sets push to false, to skip pushing new tag/package.json
|
// case: if user sets push to false, to skip pushing new tag/package.json
|
||||||
const push = process.env['INPUT_PUSH']
|
const push = process.env['INPUT_PUSH'];
|
||||||
if ( push === "false" || push === false ) {
|
if (push === 'false' || push === false) {
|
||||||
tools.exit.success('User requested to skip pushing new tag and package.json. Finished.');
|
exitSuccess('User requested to skip pushing new tag and package.json. Finished.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,12 +119,8 @@ Toolkit.run(async (tools) => {
|
||||||
try {
|
try {
|
||||||
const current = pkg.version.toString();
|
const current = pkg.version.toString();
|
||||||
// set git user
|
// set git user
|
||||||
await tools.runInWorkspace('git', [
|
await runInWorkspace('git', ['config', 'user.name', `"${process.env.GITHUB_USER || 'Automated Version Bump'}"`]);
|
||||||
'config',
|
await runInWorkspace('git', [
|
||||||
'user.name',
|
|
||||||
`"${process.env.GITHUB_USER || 'Automated Version Bump'}"`,
|
|
||||||
]);
|
|
||||||
await tools.runInWorkspace('git', [
|
|
||||||
'config',
|
'config',
|
||||||
'user.email',
|
'user.email',
|
||||||
`"${process.env.GITHUB_EMAIL || 'gh-action-bump-version@users.noreply.github.com'}"`,
|
`"${process.env.GITHUB_EMAIL || 'gh-action-bump-version@users.noreply.github.com'}"`,
|
||||||
|
@ -141,26 +140,26 @@ Toolkit.run(async (tools) => {
|
||||||
console.log('currentBranch:', currentBranch);
|
console.log('currentBranch:', currentBranch);
|
||||||
// do it in the current checked out github branch (DETACHED HEAD)
|
// do it in the current checked out github branch (DETACHED HEAD)
|
||||||
// important for further usage of the package.json version
|
// important for further usage of the package.json version
|
||||||
await tools.runInWorkspace('npm', ['version', '--allow-same-version=true', '--git-tag-version=false', current]);
|
await runInWorkspace('npm', ['version', '--allow-same-version=true', '--git-tag-version=false', current]);
|
||||||
console.log('current:', current, '/', 'version:', version);
|
console.log('current:', current, '/', 'version:', version);
|
||||||
let newVersion = execSync(`npm version --git-tag-version=false ${version}`).toString().trim().replace(/^v/, '');
|
let newVersion = execSync(`npm version --git-tag-version=false ${version}`).toString().trim().replace(/^v/, '');
|
||||||
newVersion = `${tagPrefix}${newVersion}`;
|
newVersion = `${tagPrefix}${newVersion}`;
|
||||||
await tools.runInWorkspace('git', ['commit', '-a', '-m', commitMessage.replace(/{{version}}/g, newVersion)]);
|
await runInWorkspace('git', ['commit', '-a', '-m', commitMessage.replace(/{{version}}/g, newVersion)]);
|
||||||
|
|
||||||
// now go to the actual branch to perform the same versioning
|
// now go to the actual branch to perform the same versioning
|
||||||
if (isPullRequest) {
|
if (isPullRequest) {
|
||||||
// First fetch to get updated local version of branch
|
// First fetch to get updated local version of branch
|
||||||
await tools.runInWorkspace('git', ['fetch']);
|
await runInWorkspace('git', ['fetch']);
|
||||||
}
|
}
|
||||||
await tools.runInWorkspace('git', ['checkout', currentBranch]);
|
await runInWorkspace('git', ['checkout', currentBranch]);
|
||||||
await tools.runInWorkspace('npm', ['version', '--allow-same-version=true', '--git-tag-version=false', current]);
|
await runInWorkspace('npm', ['version', '--allow-same-version=true', '--git-tag-version=false', current]);
|
||||||
console.log('current:', current, '/', 'version:', version);
|
console.log('current:', current, '/', 'version:', version);
|
||||||
newVersion = execSync(`npm version --git-tag-version=false ${version}`).toString().trim().replace(/^v/, '');
|
newVersion = execSync(`npm version --git-tag-version=false ${version}`).toString().trim().replace(/^v/, '');
|
||||||
newVersion = `${tagPrefix}${newVersion}`;
|
newVersion = `${tagPrefix}${newVersion}`;
|
||||||
console.log(`::set-output name=newTag::${newVersion}`);
|
console.log(`::set-output name=newTag::${newVersion}`);
|
||||||
try {
|
try {
|
||||||
// to support "actions/checkout@v1"
|
// to support "actions/checkout@v1"
|
||||||
await tools.runInWorkspace('git', ['commit', '-a', '-m', commitMessage.replace(/{{version}}/g, newVersion)]);
|
await runInWorkspace('git', ['commit', '-a', '-m', commitMessage.replace(/{{version}}/g, newVersion)]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(
|
console.warn(
|
||||||
'git commit failed because you are using "actions/checkout@v2"; ' +
|
'git commit failed because you are using "actions/checkout@v2"; ' +
|
||||||
|
@ -170,15 +169,61 @@ Toolkit.run(async (tools) => {
|
||||||
|
|
||||||
const remoteRepo = `https://${process.env.GITHUB_ACTOR}:${process.env.GITHUB_TOKEN}@github.com/${process.env.GITHUB_REPOSITORY}.git`;
|
const remoteRepo = `https://${process.env.GITHUB_ACTOR}:${process.env.GITHUB_TOKEN}@github.com/${process.env.GITHUB_REPOSITORY}.git`;
|
||||||
if (process.env['INPUT_SKIP-TAG'] !== 'true') {
|
if (process.env['INPUT_SKIP-TAG'] !== 'true') {
|
||||||
await tools.runInWorkspace('git', ['tag', newVersion]);
|
await runInWorkspace('git', ['tag', newVersion]);
|
||||||
await tools.runInWorkspace('git', ['push', remoteRepo, '--follow-tags']);
|
await runInWorkspace('git', ['push', remoteRepo, '--follow-tags']);
|
||||||
await tools.runInWorkspace('git', ['push', remoteRepo, '--tags']);
|
await runInWorkspace('git', ['push', remoteRepo, '--tags']);
|
||||||
} else {
|
} else {
|
||||||
await tools.runInWorkspace('git', ['push', remoteRepo]);
|
await runInWorkspace('git', ['push', remoteRepo]);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
tools.log.fatal(e);
|
logError(e);
|
||||||
tools.exit.failure('Failed to bump version');
|
exitFailure('Failed to bump version');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
exitSuccess('Version bumped!');
|
||||||
|
})();
|
||||||
|
|
||||||
|
function getPackageJson() {
|
||||||
|
const pathToPackage = path.join(workspace, 'package.json');
|
||||||
|
if (!existsSync(pathToPackage)) throw new Error("package.json could not be found in your project's root.");
|
||||||
|
return require(pathToPackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exitSuccess(message) {
|
||||||
|
console.info(`✔ success ${message}`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exitFailure(message) {
|
||||||
|
logError(message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function logError(error) {
|
||||||
|
console.error(`✖ fatal ${error.stack || error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function runInWorkspace(command, args) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const child = spawn(command, args, { cwd: workspace });
|
||||||
|
let isDone = false;
|
||||||
|
const errorMessages = [];
|
||||||
|
child.on('error', (error) => {
|
||||||
|
if (!isDone) {
|
||||||
|
isDone = true;
|
||||||
|
reject(error);
|
||||||
}
|
}
|
||||||
tools.exit.success('Version bumped!');
|
|
||||||
});
|
});
|
||||||
|
child.stderr.on('data', (chunk) => errorMessages.push(chunk));
|
||||||
|
child.on('exit', (code) => {
|
||||||
|
if (!isDone) {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject(`${errorMessages.join('')}${EOL}${command} exited with code ${code}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
//return execa(command, args, { cwd: workspace });
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
|
@ -16,15 +16,19 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ./index.js",
|
"start": "node ./index.js",
|
||||||
"test": "jest"
|
"test": "npm run end-to-end-test",
|
||||||
},
|
"end-to-end-test": "jest --roots=tests/end-to-end --testTimeout=300000 --runInBand"
|
||||||
"dependencies": {
|
|
||||||
"actions-toolkit": "^2.2.0",
|
|
||||||
"yargs": "^15.3.1"
|
|
||||||
},
|
},
|
||||||
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jest": "^25.2.7",
|
"@types/jest": "^27.0.1",
|
||||||
|
"dotenv": "^10.0.0",
|
||||||
|
"execa": "^5.1.1",
|
||||||
|
"jest": "^25.5.4",
|
||||||
|
"js-yaml": "^4.1.0",
|
||||||
|
"node-fetch": "^2.6.1",
|
||||||
|
"prettier": "^2.3.0",
|
||||||
"standard": "^14.3.3",
|
"standard": "^14.3.3",
|
||||||
"prettier": "^2.3.0"
|
"tiny-glob": "^0.2.9"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
const { default: fetch } = require('node-fetch');
|
||||||
|
|
||||||
|
async function clearWorkflowRuns() {
|
||||||
|
const runs = await getWorkflowRuns();
|
||||||
|
const basePath = getActionsBasePath();
|
||||||
|
await Promise.all(runs.map((run) => api(`${basePath}/runs/${run.id}`, { method: 'DELETE' })));
|
||||||
|
}
|
||||||
|
exports.clearWorkflowRuns = clearWorkflowRuns;
|
||||||
|
|
||||||
|
async function getMostRecentWorkflowRun() {
|
||||||
|
const runs = await getWorkflowRuns();
|
||||||
|
if (runs.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const mostRecentRun = runs.reduce((previous, current) => {
|
||||||
|
const prevDate = new Date(previous.created_at);
|
||||||
|
const currDate = new Date(current.created_at);
|
||||||
|
if (prevDate < currDate) {
|
||||||
|
return current;
|
||||||
|
} else {
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return mostRecentRun;
|
||||||
|
}
|
||||||
|
exports.getMostRecentWorkflowRun = getMostRecentWorkflowRun;
|
||||||
|
|
||||||
|
async function getWorkflowRun(id) {
|
||||||
|
const basePath = getActionsBasePath();
|
||||||
|
const run = await api(`${basePath}/runs/${id}`);
|
||||||
|
return run;
|
||||||
|
}
|
||||||
|
exports.getWorkflowRun = getWorkflowRun;
|
||||||
|
|
||||||
|
async function getWorkflowRuns() {
|
||||||
|
const basePath = getActionsBasePath();
|
||||||
|
const result = await api(`${basePath}/runs`);
|
||||||
|
return result.workflow_runs || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getActionsBasePath() {
|
||||||
|
const repoUrl = process.env.TEST_REPO;
|
||||||
|
const match = /\/([^/]*)\/([^/]*)\.git$/.exec(repoUrl);
|
||||||
|
const owner = match[1];
|
||||||
|
const repo = match[2];
|
||||||
|
return `repos/${owner}/${repo}/actions`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const retryAttempts = 10;
|
||||||
|
const retryInterval = 10;
|
||||||
|
|
||||||
|
async function api(path, options) {
|
||||||
|
options = options || {};
|
||||||
|
const username = process.env.TEST_USER;
|
||||||
|
const token = process.env.TEST_TOKEN;
|
||||||
|
for (let attempts = 0; attempts < retryAttempts; attempts++) {
|
||||||
|
const response = await fetch(`https://api.github.com/${path}`, {
|
||||||
|
method: options.method || 'GET',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Basic ${Buffer.from(`${username}:${token}`, 'ascii').toString('base64')}`,
|
||||||
|
accept: 'application/vnd.github.v3+json',
|
||||||
|
'User-Agent': username,
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (response.ok) {
|
||||||
|
const responseText = await response.text();
|
||||||
|
if (responseText.length > 0) {
|
||||||
|
return JSON.parse(responseText);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (response.status === 404) {
|
||||||
|
console.log(
|
||||||
|
`Received a 404 error while executing request at ${path}. Waiting ${retryInterval} seconds and retrying...`,
|
||||||
|
);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, retryInterval * 1000));
|
||||||
|
} else {
|
||||||
|
throw new Error(`${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
`Received ${retryAttempts} 404 errors in a row while executing request at ${path} in ${retryInterval}-second intervals.`,
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
suites:
|
||||||
|
- name: default
|
||||||
|
yaml:
|
||||||
|
name: Bump Version
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
jobs:
|
||||||
|
bump-version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- id: version-bump
|
||||||
|
uses: ./action
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
tests:
|
||||||
|
- message: no keywords
|
||||||
|
expected:
|
||||||
|
version: 1.0.1
|
||||||
|
- message: feat
|
||||||
|
expected:
|
||||||
|
version: 1.1.0
|
||||||
|
- message: minor
|
||||||
|
expected:
|
||||||
|
version: 1.2.0
|
||||||
|
- message: BREAKING CHANGE
|
||||||
|
expected:
|
||||||
|
version: 2.0.0
|
||||||
|
- message: major
|
||||||
|
expected:
|
||||||
|
version: 3.0.0
|
||||||
|
- message: pre-alpha
|
||||||
|
expected:
|
||||||
|
version: 3.0.1-alpha.0
|
||||||
|
- message: pre-beta
|
||||||
|
expected:
|
||||||
|
version: 3.0.1-beta.0
|
||||||
|
- message: pre-rc
|
||||||
|
expected:
|
||||||
|
version: 3.0.1-rc.0
|
||||||
|
- name: no-push
|
||||||
|
yaml:
|
||||||
|
name: Do Nothing
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
jobs:
|
||||||
|
bump-version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- id: version-bump
|
||||||
|
uses: ./action
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
push: false
|
||||||
|
tests:
|
||||||
|
- message: no keywords
|
||||||
|
expected:
|
||||||
|
version: 3.0.1-rc.0
|
||||||
|
- name: custom-wording
|
||||||
|
yaml:
|
||||||
|
name: Bump Version (Custom Wording)
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
jobs:
|
||||||
|
bump-version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- id: version-bump
|
||||||
|
uses: ./action
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
minor-wording: custom-minor
|
||||||
|
major-wording: custom-major
|
||||||
|
rc-wording: custom-pre
|
||||||
|
tests:
|
||||||
|
- message: custom-minor
|
||||||
|
expected:
|
||||||
|
version: 3.1.0
|
||||||
|
- message: custom-major
|
||||||
|
expected:
|
||||||
|
version: 4.0.0
|
||||||
|
- message: custom-pre
|
||||||
|
expected:
|
||||||
|
version: 4.0.1-pre.0
|
||||||
|
- name: null-default
|
||||||
|
yaml:
|
||||||
|
name: Bump Version (Default="Minor")
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
jobs:
|
||||||
|
bump-version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- id: version-bump
|
||||||
|
uses: ./action
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
default: minor
|
||||||
|
patch-wording: patch
|
||||||
|
tests:
|
||||||
|
- message: no hint
|
||||||
|
expected:
|
||||||
|
version: 4.1.0
|
||||||
|
- message: patch
|
||||||
|
expected:
|
||||||
|
version: 4.1.1
|
||||||
|
- name: custom-tag-prefix
|
||||||
|
yaml:
|
||||||
|
name: Bump Version (Custom Tag Prefix)
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
jobs:
|
||||||
|
bump-version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- id: version-bump
|
||||||
|
uses: ./action
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag-prefix: 'v'
|
||||||
|
tests:
|
||||||
|
- message: no keywords
|
||||||
|
expected:
|
||||||
|
version: 4.1.2
|
||||||
|
tag: v4.1.2
|
||||||
|
- name: target-branch
|
||||||
|
yaml:
|
||||||
|
name: Bump Version (Target Branch)
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
jobs:
|
||||||
|
bump-version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- run: git branch other-branch
|
||||||
|
- id: version-bump
|
||||||
|
uses: ./action
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
target-branch: other-branch
|
||||||
|
tests:
|
||||||
|
- message: no keywords
|
||||||
|
expected:
|
||||||
|
version: 4.1.3
|
||||||
|
branch: other-branch
|
||||||
|
|
||||||
|
actionFiles:
|
||||||
|
- index.js
|
||||||
|
- Dockerfile
|
||||||
|
- package.json
|
||||||
|
- package-lock.json
|
||||||
|
- action.yml
|
||||||
|
image: catthehacker/ubuntu:act-latest
|
|
@ -0,0 +1,17 @@
|
||||||
|
const execa = require('execa');
|
||||||
|
|
||||||
|
module.exports = async function exec(command, options, ...params) {
|
||||||
|
let suppressOutput;
|
||||||
|
if (typeof options === 'object') {
|
||||||
|
suppressOutput = options.suppressOutput;
|
||||||
|
} else {
|
||||||
|
params.unshift(options);
|
||||||
|
suppressOutput = false;
|
||||||
|
}
|
||||||
|
const subprocess = execa(command, params);
|
||||||
|
if (!suppressOutput) {
|
||||||
|
subprocess.stdout.pipe(process.stdout);
|
||||||
|
}
|
||||||
|
subprocess.stderr.pipe(process.stderr);
|
||||||
|
return await subprocess;
|
||||||
|
};
|
|
@ -0,0 +1,7 @@
|
||||||
|
const exec = require('./exec');
|
||||||
|
|
||||||
|
function git(options, ...params) {
|
||||||
|
return exec('git', options, ...params);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = git;
|
|
@ -0,0 +1,140 @@
|
||||||
|
const dotenv = require('dotenv');
|
||||||
|
const setupTestRepo = require('./setupTestRepo');
|
||||||
|
const yaml = require('js-yaml');
|
||||||
|
const { readFileSync } = require('fs');
|
||||||
|
const { writeFile, readFile, mkdir } = require('fs/promises');
|
||||||
|
const { resolve, join } = require('path');
|
||||||
|
const { cwd } = require('process');
|
||||||
|
const git = require('./git');
|
||||||
|
const { getMostRecentWorkflowRun, getWorkflowRun } = require('./actionsApi');
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const config = getTestConfig();
|
||||||
|
|
||||||
|
beforeAll(() => setupTestRepo(config.actionFiles));
|
||||||
|
|
||||||
|
config.suites.forEach((suite) => {
|
||||||
|
const suiteYaml = yaml.dump(suite.yaml);
|
||||||
|
describe(suite.name, () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
const pushYamlPath = join('.github', 'workflows', 'push.yml');
|
||||||
|
await mkdir(join(cwd(), '.github', 'workflows'), { recursive: true });
|
||||||
|
await writeFile(join(cwd(), pushYamlPath), suiteYaml);
|
||||||
|
await git('add', pushYamlPath);
|
||||||
|
});
|
||||||
|
suite.tests.forEach((commit) => {
|
||||||
|
test(commit.message, async () => {
|
||||||
|
await generateReadMe(commit, suiteYaml);
|
||||||
|
await git('commit', '--message', commit.message);
|
||||||
|
|
||||||
|
const mostRecentDate = await getMostRecentWorkflowRunDate();
|
||||||
|
await git('push');
|
||||||
|
|
||||||
|
const completedRun = await getCompletedRunAfter(mostRecentDate);
|
||||||
|
expect(completedRun.conclusion).toBe('success');
|
||||||
|
|
||||||
|
await assertExpectation(commit.expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function getTestConfig() {
|
||||||
|
const path = resolve(__dirname, './config.yaml');
|
||||||
|
const buffer = readFileSync(path);
|
||||||
|
const contents = buffer.toString();
|
||||||
|
const config = yaml.load(contents);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateReadMe(commit, suiteYaml) {
|
||||||
|
const readmePath = 'README.md';
|
||||||
|
const readMeContents = [
|
||||||
|
'# Test Details',
|
||||||
|
'## .github/workflows/push.yml',
|
||||||
|
'```YAML',
|
||||||
|
yaml.dump(suiteYaml),
|
||||||
|
'```',
|
||||||
|
'## Message',
|
||||||
|
commit.message,
|
||||||
|
'## Expectation',
|
||||||
|
generateExpectationText(commit.expected),
|
||||||
|
].join('\n');
|
||||||
|
await writeFile(join(cwd(), readmePath), readMeContents);
|
||||||
|
await git('add', readmePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCompletedRunAfter(date) {
|
||||||
|
const run = await pollFor(getMostRecentWorkflowRun, (run) => run !== null && new Date(run.created_at) > date);
|
||||||
|
const completedRun = await pollFor(
|
||||||
|
() => getWorkflowRun(run.id),
|
||||||
|
(run) => run.status === 'completed',
|
||||||
|
);
|
||||||
|
return completedRun;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pollFor(getResult, validateResult) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
pollAndRetry();
|
||||||
|
|
||||||
|
async function pollAndRetry() {
|
||||||
|
try {
|
||||||
|
const result = await getResult();
|
||||||
|
if (validateResult(result)) {
|
||||||
|
resolve(result);
|
||||||
|
} else {
|
||||||
|
setTimeout(pollAndRetry, 1000);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getMostRecentWorkflowRunDate() {
|
||||||
|
const run = await getMostRecentWorkflowRun();
|
||||||
|
const date = run === null ? new Date(0) : new Date(run.created_at);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateExpectationText({ version: expectedVersion, tag: expectedTag, branch: expectedBranch }) {
|
||||||
|
const results = [`- **Version:** ${expectedVersion}`];
|
||||||
|
if (expectedTag) {
|
||||||
|
results.push(`- **Tag:** ${expectedTag}`);
|
||||||
|
}
|
||||||
|
if (expectedBranch) {
|
||||||
|
results.push(`- **Branch:** ${expectedBranch}`);
|
||||||
|
}
|
||||||
|
return results.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function assertExpectation({ version: expectedVersion, tag: expectedTag, branch: expectedBranch }) {
|
||||||
|
if (expectedTag === undefined) {
|
||||||
|
expectedTag = expectedVersion;
|
||||||
|
}
|
||||||
|
if (expectedBranch) {
|
||||||
|
await git('fetch', 'origin', expectedBranch);
|
||||||
|
await git('checkout', expectedBranch);
|
||||||
|
}
|
||||||
|
await git('pull');
|
||||||
|
const [packageVersion, latestTag] = await Promise.all([getPackageJsonVersion(), getLatestTag()]);
|
||||||
|
expect(packageVersion).toBe(expectedVersion);
|
||||||
|
expect(latestTag).toBe(expectedTag);
|
||||||
|
if (expectedBranch) {
|
||||||
|
await git('checkout', 'main');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getPackageJsonVersion() {
|
||||||
|
const path = join(cwd(), 'package.json');
|
||||||
|
const contents = await readFile(path);
|
||||||
|
const json = JSON.parse(contents);
|
||||||
|
return json.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getLatestTag() {
|
||||||
|
const result = await git({ suppressOutput: true }, 'describe', '--tags', '--abbrev=0');
|
||||||
|
return result.stdout;
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
const { existsSync } = require('fs');
|
||||||
|
const { rm, mkdir, copyFile, stat } = require('fs/promises');
|
||||||
|
const { chdir, cwd } = require('process');
|
||||||
|
const { resolve, join, dirname } = require('path');
|
||||||
|
const exec = require('./exec');
|
||||||
|
const git = require('./git');
|
||||||
|
const glob = require('tiny-glob');
|
||||||
|
const { clearWorkflowRuns } = require('./actionsApi');
|
||||||
|
|
||||||
|
module.exports = async function setupTestRepo(actionFileGlobPaths) {
|
||||||
|
const testRepoPath = resolve(__dirname, '..', '..', 'test-repo');
|
||||||
|
if (existsSync(testRepoPath)) {
|
||||||
|
await rm(testRepoPath, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
await mkdir(testRepoPath);
|
||||||
|
chdir(testRepoPath);
|
||||||
|
await Promise.all([clearWorkflowRuns(), createNpmPackage(), copyActionFiles(actionFileGlobPaths)]);
|
||||||
|
await git('init', '--initial-branch', 'main');
|
||||||
|
await addRemote();
|
||||||
|
await git('config', 'user.name', 'Automated Version Bump Test');
|
||||||
|
await git('config', 'user.email', 'gh-action-bump-version-test@users.noreply.github.com');
|
||||||
|
await git('add', '.');
|
||||||
|
await git('commit', '--message', 'initial commit (version 1.0.0)');
|
||||||
|
await git('push', '--force', '--set-upstream', 'origin', 'main');
|
||||||
|
await deleteTagsAndBranches();
|
||||||
|
};
|
||||||
|
|
||||||
|
function createNpmPackage() {
|
||||||
|
return exec('npm', 'init', '-y');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addRemote() {
|
||||||
|
const testRepoUrl = process.env.TEST_REPO;
|
||||||
|
const username = process.env.TEST_USER;
|
||||||
|
const token = process.env.TEST_TOKEN;
|
||||||
|
const authUrl = testRepoUrl.replace(/^https:\/\//, `https://${username}:${token}@`);
|
||||||
|
await git('remote', 'add', 'origin', authUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyActionFiles(globPaths) {
|
||||||
|
const actionFolder = join(cwd(), 'action');
|
||||||
|
await mkdir(actionFolder);
|
||||||
|
const projectRoot = join(__dirname, '..', '..');
|
||||||
|
const globResults = await Promise.all(globPaths.map((path) => glob(path, { cwd: projectRoot })));
|
||||||
|
const relativeFilePaths = await Promise.all([...new Set(globResults.flat())]);
|
||||||
|
const folders = [...new Set(relativeFilePaths.map(dirname))].filter((path) => path !== '.');
|
||||||
|
if (folders.length > 0) {
|
||||||
|
await Promise.all(folders.map((folder) => mkdir(join(actionFolder, folder), { recursive: true })));
|
||||||
|
}
|
||||||
|
await Promise.all(
|
||||||
|
relativeFilePaths.map(async (path) => {
|
||||||
|
const sourcePath = join(projectRoot, path);
|
||||||
|
const fileStat = await stat(sourcePath);
|
||||||
|
if (fileStat.isFile()) {
|
||||||
|
return copyFile(sourcePath, join(actionFolder, path));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteTagsAndBranches() {
|
||||||
|
const listResult = await git({ suppressOutput: true }, 'ls-remote', '--tags', '--heads', 'origin');
|
||||||
|
if (listResult.stdout) {
|
||||||
|
const lines = listResult.stdout.split('\n');
|
||||||
|
const refs = lines.map((line) => line.split('\t')[1]).filter((ref) => ref !== 'refs/heads/main');
|
||||||
|
if (refs.length > 0) {
|
||||||
|
await git('push', 'origin', '--delete', ...refs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue