forked from nrwl/nx-set-shas
-
Notifications
You must be signed in to change notification settings - Fork 0
/
find-successful-workflow.js
136 lines (123 loc) · 4.59 KB
/
find-successful-workflow.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
const { Octokit } = require("@octokit/action");
const core = require("@actions/core");
const github = require('@actions/github');
const { execSync } = require('child_process');
const { existsSync } = require('fs');
const { runId, repo: { repo, owner }, eventName } = github.context;
process.env.GITHUB_TOKEN = process.argv[2];
const mainBranchName = process.argv[3];
const errorOnNoSuccessfulWorkflow = process.argv[4];
const lastSuccessfulEvent = process.argv[5];
const workingDirectory = process.argv[6];
const workflowId = process.argv[7];
const defaultWorkingDirectory = '.';
let BASE_SHA;
(async () => {
if (workingDirectory !== defaultWorkingDirectory) {
if (existsSync(workingDirectory)) {
process.chdir(workingDirectory);
} else {
process.stdout.write('\n');
process.stdout.write(`WARNING: Working directory '${workingDirectory}' doesn't exist.\n`);
}
}
const HEAD_SHA = execSync(`git rev-parse HEAD`, { encoding: 'utf-8' });
if (eventName === 'pull_request') {
BASE_SHA = execSync(`git merge-base origin/${mainBranchName} HEAD`, { encoding: 'utf-8' });
} else {
try {
BASE_SHA = await findSuccessfulCommit(workflowId, runId, owner, repo, mainBranchName, lastSuccessfulEvent);
} catch (e) {
core.setFailed(e.message);
return;
}
if (!BASE_SHA) {
if (errorOnNoSuccessfulWorkflow === 'true') {
reportFailure(mainBranchName);
return;
} else {
process.stdout.write('\n');
process.stdout.write(`WARNING: Unable to find a successful workflow run on 'origin/${mainBranchName}'\n`);
process.stdout.write(`We are therefore defaulting to use HEAD~1 on 'origin/${mainBranchName}'\n`);
process.stdout.write('\n');
process.stdout.write(`NOTE: You can instead make this a hard error by setting 'error-on-no-successful-workflow' on the action in your workflow.\n`);
BASE_SHA = execSync(`git rev-parse origin/${mainBranchName}~1`, { encoding: 'utf-8' });
core.setOutput('noPreviousBuild', 'true');
}
} else {
process.stdout.write('\n');
process.stdout.write(`Found the last successful workflow run on 'origin/${mainBranchName}'\n`);
process.stdout.write(`Commit: ${BASE_SHA}\n`);
}
}
const stripNewLineEndings = sha => sha.replace('\n', '');
core.setOutput('base', stripNewLineEndings(BASE_SHA));
core.setOutput('head', stripNewLineEndings(HEAD_SHA));
})();
function reportFailure(branchName) {
core.setFailed(`
Unable to find a successful workflow run on 'origin/${branchName}'
NOTE: You have set 'error-on-no-successful-workflow' on the action so this is a hard error.
Is it possible that you have no runs currently on 'origin/${branchName}'?
- If yes, then you should run the workflow without this flag first.
- If no, then you might have changed your git history and those commits no longer exist.`);
}
/**
* Find last successful workflow run on the repo
* @param {string?} workflow_id
* @param {number} run_id
* @param {string} owner
* @param {string} repo
* @param {string} branch
* @returns
*/
async function findSuccessfulCommit(workflow_id, run_id, owner, repo, branch, lastSuccessfulEvent) {
const octokit = new Octokit();
if (!workflow_id) {
workflow_id = await octokit.request(`GET /repos/${owner}/${repo}/actions/runs/${run_id}`, {
owner,
repo,
branch,
run_id
}).then(({ data: { workflow_id } }) => workflow_id);
process.stdout.write('\n');
process.stdout.write(`Workflow Id not provided. Using workflow '${workflow_id}'\n`);
}
// fetch all workflow runs on a given repo/branch/workflow with push and success
const shas = await octokit.request(`GET /repos/${owner}/${repo}/actions/workflows/${workflow_id}/runs`, {
owner,
repo,
// on non-push workflow runs we do not have branch property
branch: lastSuccessfulEvent !== 'push' ? undefined : branch,
workflow_id,
event: lastSuccessfulEvent,
status: 'success'
}).then(({ data: { workflow_runs } }) => workflow_runs.map(run => run.head_sha));
return await findExistingCommit(shas);
}
/**
* Get first existing commit
* @param {string[]} commit_shas
* @returns {string?}
*/
async function findExistingCommit(shas) {
for (const commitSha of shas) {
if (await commitExists(commitSha)) {
return commitSha;
}
}
return undefined;
}
/**
* Check if given commit is valid
* @param {string} commitSha
* @returns {boolean}
*/
async function commitExists(commitSha) {
try {
execSync(`git cat-file -e ${commitSha}`, { stdio: ['pipe', 'pipe', null] });
return true;
} catch {
return false;
}
}