Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 62d30ec

Browse files
authored
Create a very simple CLI wrapper (#2)
* Move heavy lifting parts into a dedicated module That way, you should be able to reuse most of the action-code, but replace the GitHub Actions specific part with other wrappers. For example, a simple CLI command could be used to trigger deployments as well. * Add a very simple CLI wrapper to trigger deployments * Add a package version * Catch missing appspec.yml files early on * Pass the "core" module * Avoid querying for parameters that should have sane defaults * Improve exception handling * Remove commented-out code, bump dependencies
1 parent 38eda93 commit 62d30ec

File tree

7 files changed

+39462
-39497
lines changed

7 files changed

+39462
-39497
lines changed

‎README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ The IAM User that is used to run the action requires the following IAM permissio
165165
As a note to my future self, in order to work on this repo:
166166

167167
* Clone it
168-
* Run `npm install` to fetch dependencies
168+
* Run `yarn install` to fetch dependencies
169169
* _hack hack hack_
170170
* Run `npm run build` to update `dist/*`, which holds the files actually run
171171
* Read https://help.github.com/en/articles/creating-a-javascript-action if unsure.

‎cli.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
'use strict';
2+
3+
(async function () {
4+
var core = require('@actions/core');
5+
core.setOutput = function () {};
6+
core.setFailed = function (message) {
7+
console.log(message instanceof Error ? message.toString() : message);
8+
process.exitCode = 1;
9+
}
10+
11+
const fs = require('fs');
12+
if (!fs.existsSync('./appspec.yml')) {
13+
core.setFailed("❓ ./appspec.yml does not exist. Make sure you are in the project's top level directory.");
14+
process.exit();
15+
}
16+
17+
const simpleGit = require('simple-git');
18+
const git = simpleGit();
19+
var branchName, commitId;
20+
21+
try {
22+
await git.init();
23+
const remotes = await git.getRemotes(true);
24+
var applicationName, fullRepositoryName;
25+
26+
for (const remote of remotes) {
27+
if (remote.name !== 'origin') {
28+
continue;
29+
}
30+
31+
const url = remote.refs.push;
32+
33+
var matches
34+
35+
if (matches = url.match(/git@github.com:([a-z0-9_-]+)\/([a-z0-9_-]+).git/)) {
36+
applicationName = matches[2];
37+
fullRepositoryName = `${matches[1]}/${matches[2]}`;
38+
}
39+
}
40+
41+
branchName = await git.revparse(['--abbrev-ref', 'HEAD']);
42+
commitId = await git.revparse(['HEAD']);
43+
} catch (e) {
44+
core.setFailed('🌩 Failed to parse git information. Are you sure this is a git repo?')
45+
process.exit();
46+
}
47+
48+
if (!applicationName || !fullRepositoryName) {
49+
core.setFailed("❓ Unable to parse GitHub repository name from the 'origin' remote.");
50+
process.exit();
51+
}
52+
53+
console.log("🚂 OK, let's ship this...");
54+
console.log(`GitHub 💎 repository '${fullRepositoryName}'`);
55+
console.log(`Branch 🎋 ${branchName}`);
56+
console.log(`Commit ⚙️ ${commitId}`);
57+
58+
const prompt = require('prompt');
59+
60+
prompt.message = '';
61+
prompt.start();
62+
63+
try {
64+
await prompt.get({
65+
properties: {
66+
confirm: {
67+
name: 'yes',
68+
message: 'Type "yes" to create deployment',
69+
validator: /yes/,
70+
warning: 'Must respond yes to continue',
71+
default: ''
72+
}
73+
}
74+
});
75+
} catch (e) {
76+
core.setFailed('🙈 Aborted.');
77+
process.exit();
78+
}
79+
80+
const action = require('./create-deployment');
81+
try {
82+
await action.createDeployment(applicationName, fullRepositoryName, branchName, commitId, core);
83+
} catch (e) {
84+
console.log(`👉🏻 ${e.message}`);
85+
process.exit(1);
86+
}
87+
})();

‎create-deployment.js

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
'use strict';
2+
3+
function fetchBranchConfig(branchName) {
4+
const fs = require('fs');
5+
const yaml = require('js-yaml');
6+
7+
let fileContents = fs.readFileSync('./appspec.yml', 'utf8');
8+
let data = yaml.safeLoad(fileContents);
9+
10+
for (var prop in data.branch_config) {
11+
var regex = new RegExp('^' + prop + '$', 'i');
12+
if (branchName.match(regex)) {
13+
if (data.branch_config[prop] == null) {
14+
console.log(`🤷🏻‍♂️ Found an empty appspec.yml -> branch_config for '${branchName}' – skipping deployment`);
15+
process.exit();
16+
}
17+
console.log(`💡 Using appspec.yml -> branch_config '${prop}' for branch '${branchName}'`);
18+
return data.branch_config[prop];
19+
}
20+
}
21+
22+
console.log(`❓ Found no matching appspec.yml -> branch_config for '${branchName}' – skipping deployment`);
23+
process.exit();
24+
}
25+
26+
exports.createDeployment = async function(applicationName, fullRepositoryName, branchName, commitId, core) {
27+
const branchConfig = fetchBranchConfig(branchName);
28+
const safeBranchName = branchName.replace(/[^a-z0-9-/]+/gi, '-').replace(/\/+/, '--');
29+
const deploymentGroupName = branchConfig.deploymentGroupName ? branchConfig.deploymentGroupName.replace('$BRANCH', safeBranchName) : safeBranchName;
30+
const deploymentGroupConfig = branchConfig.deploymentGroupConfig;
31+
const deploymentConfig = branchConfig.deploymentConfig;
32+
33+
console.log(`🎳 Using deployment group '${deploymentGroupName}'`);
34+
35+
const client = require('aws-sdk/clients/codedeploy');
36+
const codeDeploy = new client();
37+
38+
try {
39+
await codeDeploy.updateDeploymentGroup({
40+
...deploymentGroupConfig,
41+
...{
42+
applicationName: applicationName,
43+
currentDeploymentGroupName: deploymentGroupName
44+
}
45+
}).promise();
46+
console.log(`⚙️ Updated deployment group '${deploymentGroupName}'`);
47+
core.setOutput('deploymentGroupCreated', false);
48+
} catch (e) {
49+
if (e.code == 'DeploymentGroupDoesNotExistException') {
50+
await codeDeploy.createDeploymentGroup({
51+
...deploymentGroupConfig,
52+
...{
53+
applicationName: applicationName,
54+
deploymentGroupName: deploymentGroupName,
55+
}
56+
}).promise();
57+
console.log(`🎯 Created deployment group '${deploymentGroupName}'`);
58+
core.setOutput('deploymentGroupCreated', true);
59+
} else {
60+
core.setFailed(`🌩 Unhandled exception`);
61+
throw e;
62+
}
63+
}
64+
65+
let tries = 0;
66+
while (true) {
67+
68+
if (++tries > 5) {
69+
core.setFailed('🤥 Unable to create a new deployment (too much concurrency?)');
70+
return;
71+
}
72+
73+
try {
74+
var {deploymentId: deploymentId} = await codeDeploy.createDeployment({
75+
...deploymentConfig,
76+
...{
77+
applicationName: applicationName,
78+
deploymentGroupName: deploymentGroupName,
79+
revision: {
80+
revisionType: 'GitHub',
81+
gitHubLocation: {
82+
commitId: commitId,
83+
repository: fullRepositoryName
84+
}
85+
}
86+
}
87+
}).promise();
88+
console.log(`🚚️ Created deployment ${deploymentId} – https://console.aws.amazon.com/codesuite/codedeploy/deployments/${deploymentId}?region=${codeDeploy.config.region}`);
89+
core.setOutput('deploymentId', deploymentId);
90+
core.setOutput('deploymentGroupName', deploymentGroupName);
91+
break;
92+
} catch (e) {
93+
if (e.code == 'DeploymentLimitExceededException') {
94+
var [, otherDeployment] = e.message.toString().match(/isalreadydeployingdeployment\'(d-\w+)\'/);
95+
console.log(`😶 Waiting for another pending deployment ${otherDeployment}`);
96+
try {
97+
await codeDeploy.waitFor('deploymentSuccessful', {deploymentId: otherDeployment}).promise();
98+
console.log(`🙂 The pending deployment ${otherDeployment} sucessfully finished.`);
99+
} catch (e) {
100+
console.log(`🤔 The other pending deployment ${otherDeployment} seems to have failed.`);
101+
}
102+
continue;
103+
} else {
104+
core.setFailed(`🌩 Unhandled exception`);
105+
throw e;
106+
}
107+
}
108+
}
109+
110+
console.log(`⏲ Waiting for deployment ${deploymentId} to finish`);
111+
112+
try {
113+
await codeDeploy.waitFor('deploymentSuccessful', {deploymentId: deploymentId}).promise();
114+
console.log('🥳 Deployment successful');
115+
} catch (e) {
116+
core.setFailed(`😱 The deployment ${deploymentId} seems to have failed.`);
117+
throw e;
118+
}
119+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /