Skip to content

Commit 2bfa1b8

Browse files
committed
Implement next workflow run strategy
1 parent bf13d79 commit 2bfa1b8

File tree

4 files changed

+404
-179
lines changed

4 files changed

+404
-179
lines changed

action.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
description: Create Workflow Dispatch
22
name: Workflow Dispatch Plus
33
inputs:
4+
inputs:
5+
description: JSON object of inputs
6+
default: "{}"
7+
marker-input:
8+
description: Name of input for this workflow's URL
49
repo:
510
description: Repository name
611
owner:
712
description: Owner name
13+
ref:
14+
description: Ref
815
token:
916
description:
1017
Personal access token, see
1118
https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token
1219
required: true
13-
workflow:
14-
description: Workflow file path
15-
ref:
16-
description: Ref
17-
inputs:
18-
description: JSON object of inputs
19-
default: "{}"
20-
upstream-input-name:
21-
description: Name of upstream input
2220
wait:
2321
default: "false"
2422
description: Whether to wait for
23+
workflow:
24+
description: Workflow file path
2525
outputs:
2626
conclusion:
2727
description: Conclusion of workflow run

workflow-dispatch/dist/index.js

Lines changed: 179 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -29709,6 +29709,16 @@ const Octokit = Octokit$1.plugin(requestLog, legacyRestEndpointMethods, paginate
2970929709
const extraGithubContext = {
2971029710
runAttempt: +process.env.GITHUB_RUN_ATTEMPT,
2971129711
};
29712+
function getBooleanInput(name) {
29713+
const text = coreExports.getInput(name);
29714+
switch (text) {
29715+
case "false":
29716+
return false;
29717+
case "true":
29718+
return true;
29719+
}
29720+
coreExports.setFailed(`Invalid boolean for ${name}`);
29721+
}
2971229722
function getJsonInput(name) {
2971329723
const text = coreExports.getInput(name);
2971429724
try {
@@ -29733,96 +29743,183 @@ function currentUrl() {
2973329743
return workflowRunAttemptUrl(context.serverUrl, context.repo.owner, context.repo.repo, context.runId, extraGithubContext.runAttempt);
2973429744
}
2973529745

29746+
const CLOCK_DRIFT = Duration.ofMinutes(1);
29747+
class NextRunFinder {
29748+
constructor(octokit, params, created, prevRunNumber) {
29749+
this.octokit = octokit;
29750+
this.params = params;
29751+
this.created = created;
29752+
this.prevRunNumber = prevRunNumber;
29753+
}
29754+
async find() {
29755+
const minCreated = this.created
29756+
.minus(CLOCK_DRIFT)
29757+
.truncatedTo(ChronoUnit.SECONDS);
29758+
const maxCreated = this.created
29759+
.plus(CLOCK_DRIFT)
29760+
.truncatedTo(ChronoUnit.SECONDS);
29761+
for (let i = 0; i < 30; i++) {
29762+
const response = await this.octokit.actions.listWorkflowRuns({
29763+
created: `${minCreated}...${maxCreated}`,
29764+
event: "workflow_dispatch",
29765+
owner: this.params.owner,
29766+
ref: this.params.ref,
29767+
repo: this.params.repo,
29768+
per_page: this.prevRunNumber !== undefined ? 100 : 1,
29769+
workflow_id: this.params.workflowId,
29770+
});
29771+
for (const workflowRun of response.data.workflow_runs) {
29772+
if (this.prevRunNumber === undefined ||
29773+
this.prevRunNumber < workflowRun.run_number) {
29774+
return workflowRun.id;
29775+
}
29776+
}
29777+
await new Promise((resolve) => setTimeout(resolve, Duration.ofSeconds(1).toMillis()));
29778+
}
29779+
}
29780+
}
29781+
class NextRunDispatcher {
29782+
constructor(octokit) {
29783+
this.octokit = octokit;
29784+
}
29785+
async start(params) {
29786+
const created = Instant.now();
29787+
const response = await this.octokit.actions.listWorkflowRuns({
29788+
owner: params.owner,
29789+
ref: params.ref,
29790+
repo: params.repo,
29791+
per_page: 1,
29792+
workflow_id: params.workflowId,
29793+
});
29794+
const workflowRun = response.data.workflow_runs[0];
29795+
const prevRunNumber = workflowRun?.run_number;
29796+
await this.octokit.actions.createWorkflowDispatch({
29797+
owner: params.owner,
29798+
repo: params.repo,
29799+
workflow_id: params.workflowId,
29800+
ref: params.ref,
29801+
inputs: params.inputs,
29802+
});
29803+
return new NextRunFinder(this.octokit, params, created, prevRunNumber);
29804+
}
29805+
}
29806+
class MarkerRunFinder {
29807+
constructor(octokit, stepName, params, created) {
29808+
this.octokit = octokit;
29809+
this.stepName = stepName;
29810+
this.params = params;
29811+
this.created = created;
29812+
}
29813+
async find() {
29814+
const minCreated = this.created
29815+
.minus(CLOCK_DRIFT)
29816+
.truncatedTo(ChronoUnit.SECONDS);
29817+
const maxCreated = this.created
29818+
.plus(CLOCK_DRIFT)
29819+
.truncatedTo(ChronoUnit.SECONDS);
29820+
for (let i = 0; i < 30; i++) {
29821+
const response = await this.octokit.actions.listWorkflowRuns({
29822+
created: `${minCreated}...${maxCreated}`,
29823+
event: "workflow_dispatch",
29824+
owner: this.params.owner,
29825+
ref: this.params.ref,
29826+
repo: this.params.repo,
29827+
workflow_id: this.params.workflowId,
29828+
});
29829+
for (const workflowRun of response.data.workflow_runs) {
29830+
const response = await this.octokit.actions.listJobsForWorkflowRun({
29831+
owner: this.params.owner,
29832+
repo: this.params.repo,
29833+
run_id: workflowRun.id,
29834+
});
29835+
for (const job of response.data.jobs) {
29836+
const response = await this.octokit.actions.getJobForWorkflowRun({
29837+
owner: this.params.owner,
29838+
repo: this.params.repo,
29839+
run_id: workflowRun.id,
29840+
job_id: job.id,
29841+
});
29842+
if (!response.data.steps) {
29843+
continue;
29844+
}
29845+
for (const step of response.data.steps) {
29846+
if (step.name === this.stepName) {
29847+
return workflowRun.id;
29848+
}
29849+
}
29850+
}
29851+
}
29852+
await new Promise((resolve) => setTimeout(resolve, Duration.ofSeconds(1).toMillis()));
29853+
}
29854+
}
29855+
}
29856+
class MarkerRunDispatcher {
29857+
constructor(octokit, inputName, stepName) {
29858+
this.octokit = octokit;
29859+
this.inputName = inputName;
29860+
this.stepName = stepName;
29861+
}
29862+
async start(params) {
29863+
const created = Instant.now();
29864+
await this.octokit.actions.createWorkflowDispatch({
29865+
owner: params.owner,
29866+
repo: params.repo,
29867+
workflow_id: params.workflowId,
29868+
ref: params.ref,
29869+
inputs: { ...params.inputs, [this.inputName]: this.stepName },
29870+
});
29871+
return new MarkerRunFinder(this.octokit, this.stepName, params, created);
29872+
}
29873+
}
29874+
2973629875
class ActionError extends Error {
2973729876
}
29738-
const clockDrift = Duration.ofMinutes(1);
2973929877
async function main() {
29878+
const inputs = getJsonInput("inputs");
29879+
const markerInput = coreExports.getInput("marker-input");
2974029880
const owner = coreExports.getInput("owner") || context.repo.owner;
29881+
const ref = coreExports.getInput("ref") || context.ref;
2974129882
const repo = coreExports.getInput("repo") || context.repo.repo;
2974229883
const token = coreExports.getInput("token", { required: true });
29884+
const wait = getBooleanInput("wait");
2974329885
const workflow = coreExports.getInput("workflow", { required: true });
29744-
const ref = coreExports.getInput("ref") || context.ref;
29745-
const inputs = getJsonInput("inputs");
29746-
const upstreamInputName = coreExports.getInput("upstream-input-name");
29747-
const wait = coreExports.getInput("wait");
29748-
const upstreamUrl = currentUrl();
29749-
if (upstreamInputName) {
29750-
inputs[upstreamInputName] = upstreamUrl;
29751-
}
2975229886
const octokit = new Octokit({ auth: token });
29753-
const created = Instant.now();
29754-
await octokit.actions.createWorkflowDispatch({
29887+
let dispatcher;
29888+
if (markerInput) {
29889+
dispatcher = new MarkerRunDispatcher(octokit, markerInput, currentUrl());
29890+
}
29891+
else {
29892+
dispatcher = new NextRunDispatcher(octokit);
29893+
}
29894+
const runFinder = await dispatcher.start({
2975529895
owner,
2975629896
repo,
29757-
workflow_id: workflow,
29897+
workflowId: workflow,
2975829898
ref,
2975929899
inputs,
2976029900
});
2976129901
coreExports.info("Created workflow dispatch");
29762-
if (upstreamInputName) {
29763-
const runId = await findWorkflowRunFromMarkerStep(octokit, owner, repo, workflow, ref, upstreamUrl, created);
29764-
if (runId === undefined) {
29765-
coreExports.info("Could not find created workflow run");
29766-
coreExports.error("Could not find workflow run", { title: "Workflow Dispatch" });
29767-
return;
29768-
}
29769-
const downstreamUrl = workflowRunUrl(context.serverUrl, owner, repo, runId);
29770-
coreExports.info(`Created workflow run ${downstreamUrl}`);
29771-
coreExports.summary
29772-
.addRaw(`Created workflow run [${downstreamUrl}](${downstreamUrl})`)
29773-
.addEOL();
29774-
coreExports.setOutput("run_id", runId);
29902+
const runId = await runFinder.find();
29903+
if (runId === undefined) {
29904+
coreExports.info("Could not find created workflow run");
2977529905
if (wait) {
29776-
const conclusion = await waitWorkflowRun(octokit, owner, repo, runId);
29777-
coreExports.setOutput("conclusion", conclusion);
29778-
coreExports.info(`Conclusion: ${conclusion}`);
29906+
coreExports.setFailed("Could not find workflow run");
2977929907
}
29780-
}
29781-
}
29782-
main().catch((e) => {
29783-
if (e instanceof ActionError) {
29784-
coreExports.setFailed(e.message);
29785-
}
29786-
else {
29787-
console.error(e.stack);
29788-
process.exitCode = 1;
29789-
}
29790-
});
29791-
async function findWorkflowRunFromMarkerStep(octokit, owner, repo, workflow, ref, upstreamUrl, created) {
29792-
const minCreated = created.minus(clockDrift).truncatedTo(ChronoUnit.SECONDS);
29793-
const maxCreated = created.plus(clockDrift).truncatedTo(ChronoUnit.SECONDS);
29794-
for (let i = 0; i < 20; i++) {
29795-
const response = await octokit.actions.listWorkflowRuns({
29796-
owner,
29797-
repo,
29798-
workflow_id: workflow,
29799-
ref,
29800-
created: `${minCreated}...${maxCreated}`,
29801-
});
29802-
for (const workflowRun of response.data.workflow_runs) {
29803-
const response = await octokit.actions.listJobsForWorkflowRun({
29804-
owner,
29805-
repo,
29806-
run_id: workflowRun.id,
29807-
});
29808-
for (const job of response.data.jobs) {
29809-
const response = await octokit.actions.getJobForWorkflowRun({
29810-
owner,
29811-
repo,
29812-
run_id: workflowRun.id,
29813-
job_id: job.id,
29814-
});
29815-
if (!response.data.steps) {
29816-
continue;
29817-
}
29818-
for (const step of response.data.steps) {
29819-
if (step.name === upstreamUrl) {
29820-
return workflowRun.id;
29821-
}
29822-
}
29823-
}
29908+
else {
29909+
coreExports.warning("Could not find workflow run", { title: "Workflow Dispatch" });
2982429910
}
29825-
await new Promise((resolve) => setTimeout(resolve, Duration.ofSeconds(1).toMillis()));
29911+
return;
29912+
}
29913+
const downstreamUrl = workflowRunUrl(context.serverUrl, owner, repo, runId);
29914+
coreExports.info(`Created workflow run ${downstreamUrl}`);
29915+
coreExports.summary
29916+
.addRaw(`Created workflow run [${downstreamUrl}](${downstreamUrl})`)
29917+
.addEOL();
29918+
coreExports.setOutput("run_id", runId);
29919+
if (wait) {
29920+
const conclusion = await waitWorkflowRun(octokit, owner, repo, runId);
29921+
coreExports.setOutput("conclusion", conclusion);
29922+
coreExports.info(`Conclusion: ${conclusion}`);
2982629923
}
2982729924
}
2982829925
async function waitWorkflowRun(octokit, owner, repo, runId) {
@@ -29838,3 +29935,12 @@ async function waitWorkflowRun(octokit, owner, repo, runId) {
2983829935
await new Promise((resolve) => setTimeout(resolve, Duration.ofSeconds(10).toMillis()));
2983929936
}
2984029937
}
29938+
main().catch((e) => {
29939+
if (e instanceof ActionError) {
29940+
coreExports.setFailed(e.message);
29941+
}
29942+
else {
29943+
console.error(e.stack);
29944+
}
29945+
process.exitCode = 1;
29946+
});

0 commit comments

Comments
 (0)