You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by po...@apache.org on 2020/12/27 14:02:25 UTC
[airflow-checks-action] 23/27: Rework create/update workflow,
unify Checks API arguments
This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-checks-action.git
commit 88ce8c595fcf9e7e3829dca9b6b59a972a98133e
Author: Louis Brunner <lo...@gmail.com>
AuthorDate: Mon Sep 7 18:48:24 2020 +0100
Rework create/update workflow, unify Checks API arguments
---
.github/workflows/examples.yml | 10 ++++++---
README.md | 24 ++++++++++++++-------
__tests__/main.test.ts | 45 ++++++++++++++++++++++++++++++----------
action.yml | 10 +++++++--
dist/index.js | 2 +-
src/checks.ts | 47 ++++++++++++++++++------------------------
src/inputs.ts | 18 +++++++++++++++-
src/main.ts | 34 ++++++++++++------------------
src/namespaces/Inputs.ts | 17 +++++++++++----
9 files changed, 129 insertions(+), 78 deletions(-)
diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml
index bd34184..e139d30 100644
--- a/.github/workflows/examples.yml
+++ b/.github/workflows/examples.yml
@@ -207,6 +207,7 @@ jobs:
steps:
- uses: actions/checkout@v1
- uses: ./
+ id: init
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: Test With Init
@@ -215,8 +216,10 @@ jobs:
- uses: ./
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Will not be used
+ check_id: ${{ steps.init.outputs.check_id }}
status: completed
+ output: |
+ {"summary":"Some warnings in README.md"}
conclusion: failure
test_with_init_implicit:
@@ -224,6 +227,7 @@ jobs:
steps:
- uses: actions/checkout@v1
- uses: ./
+ id: init
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: Test With Init (Implicit)
@@ -232,8 +236,8 @@ jobs:
- uses: ./
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Will not be used
- conclusion: failure
+ check_id: ${{ steps.init.outputs.check_id }}
+ conclusion: success
## Based on job
test_based_job_success:
diff --git a/README.md b/README.md
index 5b3991a..e996573 100644
--- a/README.md
+++ b/README.md
@@ -22,10 +22,9 @@ jobs:
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: Test XYZ
- conclusion: ${{ job }}
- output:
- summary: ${{ steps.test.outputs.summary }}
- text_description: ${{ steps.test.outputs.description }}
+ conclusion: ${{ job.status }}
+ output: |
+ {"summary":${{ steps.test.outputs.summary }}}
```
See the [examples workflow](.github/workflows/examples.yml) for more details and examples (and see the [associated runs](https://github.com/LouisBrunner/checks-action/actions?query=workflow%3Aexamples) to see how it will look like).
@@ -38,7 +37,11 @@ See the [examples workflow](.github/workflows/examples.yml) for more details and
### `name`
-**Required** The name of your check
+**Required** for creation, the name of the check to create (mutually exclusive with `check_id`)
+
+### `check_id`
+
+**Required** for update, ID of the check to update (mutually exclusive with `name`)
### `conclusion`
@@ -93,9 +96,14 @@ Supports the same properties with the same types and names as the [Check Runs AP
Note that this will override `details_url` as it relies on `action_url` (the two inputs set the same check attribute, `details_url`)
+## Outputs
+
+### `check_id`
+
+The ID of the created check, useful to update it in another action (e.g. non-`completed` `status`)
+
## Issues
- - Action Required conclusion: button doesn't work
- - Action elements: button doesn't work
+ - Action Required conclusion: button doesn't work?
+ - Action elements: button doesn't work?
- Non-completed status: too many arguments required
- - Name is required when completing a non-`completed` `status` check even though we don't use it (see examples `test_with_init*`)
diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts
index 9eae102..5514487 100644
--- a/__tests__/main.test.ts
+++ b/__tests__/main.test.ts
@@ -1,25 +1,48 @@
-import * as process from 'process';
import * as cp from 'child_process';
import * as path from 'path';
// shows how the runner will run a javascript action with env / stdout protocol
-test('test runs', () => {
- process.env['GITHUB_REPOSITORY'] = 'LB/ABC';
- process.env['INPUT_TOKEN'] = 'ABC';
- process.env['INPUT_NAME'] = 'ABC';
- process.env['INPUT_STATUS'] = 'completed';
- process.env['INPUT_CONCLUSION'] = 'success';
- const ip = path.join(__dirname, '..', 'lib', 'main.js');
+test('test runs (creation)', () => {
+ const entry = path.join(__dirname, '..', 'lib', 'main.js');
const options: cp.ExecSyncOptions = {
- env: process.env,
+ env: {
+ GITHUB_REPOSITORY: 'LB/ABC',
+ INPUT_TOKEN: 'ABC',
+ INPUT_NAME: 'ABC',
+ INPUT_STATUS: 'completed',
+ INPUT_CONCLUSION: 'success',
+ },
};
try {
- console.log(cp.execSync(`node ${ip}`, options).toString());
+ console.log(cp.execSync(`node ${entry}`, options).toString());
} catch (e) {
const error = e as Error & {stdout: Buffer};
const output = error.stdout.toString();
console.log(output);
- expect(output).toMatch(/::debug::Error: HttpError: Bad credentials/);
+ expect(output).toMatch(/::debug::Creating a new Run/);
+ expect(output).toMatch(/::debug::HttpError: Bad credentials/);
+ }
+});
+
+test('test runs (update)', () => {
+ const entry = path.join(__dirname, '..', 'lib', 'main.js');
+ const options: cp.ExecSyncOptions = {
+ env: {
+ GITHUB_REPOSITORY: 'LB/ABC',
+ INPUT_TOKEN: 'ABC',
+ INPUT_CHECK_ID: '123',
+ INPUT_STATUS: 'completed',
+ INPUT_CONCLUSION: 'success',
+ },
+ };
+ try {
+ console.log(cp.execSync(`node ${entry}`, options).toString());
+ } catch (e) {
+ const error = e as Error & {stdout: Buffer};
+ const output = error.stdout.toString();
+ console.log(output);
+ expect(output).toMatch(/::debug::Updating a Run/);
+ expect(output).toMatch(/::debug::HttpError: Bad credentials/);
}
});
diff --git a/action.yml b/action.yml
index 1457cc3..444da5e 100644
--- a/action.yml
+++ b/action.yml
@@ -9,8 +9,11 @@ inputs:
description: 'your GITHUB_TOKEN'
required: true
name:
- description: 'the name of your check'
- required: true
+ description: 'the name of the check to create (incompatible with `check_id`)'
+ required: false
+ check_id:
+ description: 'ID of the check to update (incompatible with `name`)'
+ required: false
conclusion:
description: 'the conclusion of your check'
required: false
@@ -36,6 +39,9 @@ inputs:
actions:
description: 'the actions of your check'
required: false
+outputs:
+ check_id:
+ description: 'the ID of the created check, useful to update it in another action'
runs:
using: 'node12'
main: 'dist/index.js'
diff --git a/dist/index.js b/dist/index.js
index 0c5f986..1a3450f 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1 +1 @@
-module.exports=(()=>{var __webpack_modules__={321:function(e,t,r){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,r,s){if(s===undefined)s=r;Object.defineProperty(e,s,{enumerable:true,get:function(){return t[r]}})}:function(e,t,r,s){if(s===undefined)s=r;e[s]=t[r]});var o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var n=this&&this.__importStar||function(e [...]
\ No newline at end of file
+module.exports=(()=>{var __webpack_modules__={321:function(e,t,r){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,r,s){if(s===undefined)s=r;Object.defineProperty(e,s,{enumerable:true,get:function(){return t[r]}})}:function(e,t,r,s){if(s===undefined)s=r;e[s]=t[r]});var o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:true,value:t})}:function(e,t){e["default"]=t});var n=this&&this.__importStar||function(e [...]
\ No newline at end of file
diff --git a/src/checks.ts b/src/checks.ts
index d90ee7c..e26f79e 100644
--- a/src/checks.ts
+++ b/src/checks.ts
@@ -7,25 +7,20 @@ type Ownership = {
repo: string;
};
-type CreateOptions = {
- completed: boolean;
-};
-
-const unpackInputs = (inputs: Inputs.Args, options: {update: boolean} = {update: false}): Record<string, unknown> => {
+const unpackInputs = (title: string, inputs: Inputs.Args): Record<string, unknown> => {
let output;
if (inputs.output) {
output = {
- title: options.update ? undefined : inputs.name,
+ title,
summary: inputs.output.summary,
text: inputs.output.text_description,
actions: inputs.actions,
images: inputs.images,
};
}
- const more: {
- details_url?: string;
- conclusion?: string;
- } = {};
+
+ let details_url;
+
if (inputs.conclusion === Inputs.Conclusion.ActionRequired || inputs.actions) {
if (inputs.detailsURL) {
const reasonList = [];
@@ -36,22 +31,22 @@ const unpackInputs = (inputs: Inputs.Args, options: {update: boolean} = {update:
reasonList.push(`'actions' was provided`);
}
const reasons = reasonList.join(' and ');
- core.warning(
+ core.info(
`'details_url' was ignored in favor of 'action_url' because ${reasons} (see documentation for details)`,
);
}
- more.details_url = inputs.actionURL;
+ details_url = inputs.actionURL;
} else if (inputs.detailsURL) {
- more.details_url = inputs.detailsURL;
- }
- if (inputs.conclusion) {
- more.conclusion = inputs.conclusion.toString();
+ details_url = inputs.detailsURL;
}
+
return {
status: inputs.status.toString(),
output,
actions: inputs.actions,
- ...more,
+ conclusion: inputs.conclusion ? inputs.conclusion.toString() : undefined,
+ completed_at: inputs.status === Inputs.Status.Completed ? formatDate() : undefined,
+ details_url,
};
};
@@ -61,22 +56,17 @@ const formatDate = (): string => {
export const createRun = async (
octokit: InstanceType<typeof GitHub>,
+ name: string,
sha: string,
ownership: Ownership,
inputs: Inputs.Args,
- options?: CreateOptions,
): Promise<number> => {
- const dates: {completed_at?: string} = {};
- if (!options || options.completed) {
- dates.completed_at = formatDate();
- }
const {data} = await octokit.checks.create({
...ownership,
head_sha: sha,
- name: inputs.name,
+ name: name,
started_at: formatDate(),
- ...dates,
- ...unpackInputs(inputs),
+ ...unpackInputs(name, inputs),
});
return data.id;
};
@@ -87,10 +77,13 @@ export const updateRun = async (
ownership: Ownership,
inputs: Inputs.Args,
): Promise<void> => {
+ const previous = await octokit.checks.get({
+ ...ownership,
+ check_run_id: id,
+ });
await octokit.checks.update({
...ownership,
check_run_id: id,
- completed_at: formatDate(),
- ...unpackInputs(inputs, {update: true}),
+ ...unpackInputs(previous.data.name, inputs),
});
};
diff --git a/src/inputs.ts b/src/inputs.ts
index d5a1437..32931a1 100644
--- a/src/inputs.ts
+++ b/src/inputs.ts
@@ -18,12 +18,26 @@ const parseJSON = <T>(getInput: GetInput, property: string): T | undefined => {
export const parseInputs = (getInput: GetInput): Inputs.Args => {
const token = getInput('token', {required: true});
- const name = getInput('name', {required: true});
+
+ const name = getInput('name');
+ const checkIDStr = getInput('check_id');
+
const status = getInput('status', {required: true}) as Inputs.Status;
let conclusion = getInput('conclusion') as Inputs.Conclusion;
+
const actionURL = getInput('action_url');
const detailsURL = getInput('details_url');
+ if (name && checkIDStr) {
+ throw new Error(`can only provide 'name' or 'check_id'`);
+ }
+
+ if (!name && !checkIDStr) {
+ throw new Error(`must provide 'name' or 'check_id'`);
+ }
+
+ const checkID = checkIDStr ? parseInt(checkIDStr) : undefined;
+
if (!Object.values(Inputs.Status).includes(status)) {
throw new Error(`invalid value for 'status': '${status}'`);
}
@@ -64,6 +78,8 @@ export const parseInputs = (getInput: GetInput): Inputs.Args => {
status,
conclusion,
+ checkID,
+
actionURL,
detailsURL,
diff --git a/src/main.ts b/src/main.ts
index fa3db4e..d03548a 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,9 +1,12 @@
import * as core from '@actions/core';
import * as github from '@actions/github';
+import * as Inputs from './namespaces/Inputs';
import {parseInputs} from './inputs';
import {createRun, updateRun} from './checks';
-const stateID = 'checkID';
+const isCreation = (inputs: Inputs.Args): inputs is Inputs.ArgsCreate => {
+ return !!(inputs as Inputs.ArgsCreate).name;
+};
async function run(): Promise<void> {
try {
@@ -19,30 +22,19 @@ async function run(): Promise<void> {
};
const sha = github.context.sha;
- switch (inputs.status) {
- case 'in_progress':
- case 'queued': {
- core.debug(`Creating a new Run`);
- const id = await createRun(octokit, sha, ownership, inputs, {completed: false});
- core.saveState(stateID, id.toString());
- break;
- }
- case 'completed': {
- const id = core.getState(stateID);
- if (id) {
- core.debug(`Updating a Run (${id})`);
- await updateRun(octokit, parseInt(id), ownership, inputs);
- } else {
- core.debug(`Creating a new Run`);
- await createRun(octokit, sha, ownership, inputs);
- }
- break;
- }
+ if (isCreation(inputs)) {
+ core.debug(`Creating a new Run`);
+ const id = await createRun(octokit, inputs.name, sha, ownership, inputs);
+ core.setOutput('check_id', id);
+ } else {
+ const id = inputs.checkID;
+ core.debug(`Updating a Run (${id})`);
+ await updateRun(octokit, id, ownership, inputs);
}
core.debug(`Done`);
} catch (e) {
const error = e as Error;
- core.debug(`Error: ${error.toString()}`);
+ core.debug(error.toString());
core.setFailed(error.message);
}
}
diff --git a/src/namespaces/Inputs.ts b/src/namespaces/Inputs.ts
index fb9a89d..8363cd9 100644
--- a/src/namespaces/Inputs.ts
+++ b/src/namespaces/Inputs.ts
@@ -1,19 +1,28 @@
import {RestEndpointMethodTypes} from '@octokit/rest';
-export type Args = {
- name: string;
+interface ArgsBase {
token: string;
conclusion?: Conclusion;
status: Status;
- actionURL: string;
+ actionURL?: string;
detailsURL?: string;
output?: Output;
annotations?: Annotations;
images?: Images;
actions?: Actions;
-};
+}
+
+export interface ArgsCreate extends ArgsBase {
+ name: string;
+}
+
+export interface ArgsUpdate extends ArgsBase {
+ checkID: number;
+}
+
+export type Args = ArgsCreate | ArgsUpdate;
// ChecksCreateParamsOutputAnnotations[]
export type Annotations = NonNullable<