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:07 UTC
[airflow-checks-action] 05/27: First draft
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 d3d705842bf1b5a37ab2362b9cedf5959cdd5db9
Author: Louis Brunner <lo...@gmail.com>
AuthorDate: Sat Feb 29 21:09:12 2020 +0000
First draft
---
.eslintrc.json | 1 +
.github/workflows/{test.yml => build.yml} | 12 +-
.github/workflows/examples.yml | 252 ++++++++++++++++++++++++++++++
.prettierrc.json | 4 +-
__tests__/main.test.ts | 16 +-
action.yml | 23 +++
dist/index.js | 2 +-
jest.config.js | 2 +-
package.json | 4 +-
src/checks.ts | 69 ++++++++
src/inputs.ts | 51 ++++++
src/main.ts | 60 +++----
src/namespaces/Inputs.ts | 38 +++++
13 files changed, 479 insertions(+), 55 deletions(-)
diff --git a/.eslintrc.json b/.eslintrc.json
index ef9fcf4..528d8da 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -14,6 +14,7 @@
"project": "./tsconfig.eslint.json"
},
"rules": {
+ "@typescript-eslint/camelcase": ["off"]
},
"env": {
"node": true,
diff --git a/.github/workflows/test.yml b/.github/workflows/build.yml
similarity index 53%
rename from .github/workflows/test.yml
rename to .github/workflows/build.yml
index c058dd1..ce4ac4d 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/build.yml
@@ -4,6 +4,7 @@ on: # rebuild any PRs and main branch changes
push:
branches:
- master
+ - 'feature/*'
- 'releases/*'
jobs:
@@ -15,14 +16,3 @@ jobs:
- run: |
npm install
npm run all
-
- # make sure the action works on a clean machines without building
- test1:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v1
- - uses: ./
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
-
- # TODO: add more (race conditions, failures, etc)
diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml
new file mode 100644
index 0000000..1f82161
--- /dev/null
+++ b/.github/workflows/examples.yml
@@ -0,0 +1,252 @@
+name: "examples"
+on: [push]
+
+jobs:
+ # make sure the action works on a clean machines without building
+
+ ## Basic
+ test_basic_success:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test Basic Success
+ conclusion: success
+
+ test_basic_success_with_output:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test Basic Success (Implicit)
+ conclusion: success
+ output:
+ summary: Test was a success
+ text_description: |
+ This is a text description of the annotations and images
+ With more stuff
+ And more
+
+ test_basic_failure:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test Basic Failure
+ conclusion: failure
+
+ # Other codes
+ test_basic_neutral:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test 1 = Neutral
+ conclusion: neutral
+
+ test_basic_cancelled:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test Basic Cancelled
+ conclusion: cancelled
+
+ test_basic_timed_out:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test Basic Timed Out
+ conclusion: timed_out
+
+ test_basic_action_required:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test Basic Action Required
+ conclusion: action_required
+
+ ## Based on command
+ test_with_annotation:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test Advanced With Output
+ conclusion: success
+
+ ## With annotations
+ test_with_annotations:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test With Annotations
+ conclusion: success
+ output: # output.summary is required with annotations!
+ summary: Some warnings in README.md
+ annotations:
+ - path: README.md
+ annotation_level: warning
+ title: Spell Checker
+ message: Check your spelling for 'banaas'.
+ raw_details: Do you mean 'bananas' or 'banana'?
+ start_line: 1
+ end_line: 2
+
+ test_with_annotations_from_run:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - id: annotations
+ run: |
+ echo ::set-output name=value::{"path":"README.md","start_line":1,"end_line":2,"message":"Check your spelling for 'banaas'.","annotation_level":"warning"}
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test With Annotations From Run
+ conclusion: success
+ output: # output.summary is required with annotations!
+ summary: Some warnings in README.md
+ annotations: ${{ steps.annotations.outputs.value }}
+
+ ## With images
+ test_with_images:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test With Images
+ conclusion: success
+ output: # output.summary is required with images!
+ summary: Some cool pics
+ images:
+ - alt: Cool pic
+ image_url: https://via.placeholder.com/150
+ caption: Cool description
+
+ test_with_images_from_run:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - id: images
+ run: |
+ echo ::set-output name=value::{"alt":"Cool pic","image_url":"https://via.placeholder.com/150","caption":"Cool description"}
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test With Images From Run
+ conclusion: success
+ output: # output.summary is required with images!
+ summary: Some warnings in README.md
+ images: ${{ steps.images.outputs.value }}
+
+ ## With actions
+ # TODO: HOW DO WE SET THE WEBHOOK!
+ test_with_actions:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test With Actions
+ conclusion: success
+ actions:
+ - label: Click Me
+ description: Click me to get free RAM
+ identifier: sent_to_webhook
+
+ test_with_actions_from_run:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - id: actions
+ run: |
+ echo ::set-output name=value::{"label":"Click Me","description":"Click me to get free RAM","identifier":"sent_to_webhook"}
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test With Actions From Run
+ conclusion: success
+ # output.summary is required with actions!
+ output:
+ summary: Some warnings in README.md
+ actions: ${{ steps.actions.outputs.value }}
+
+ ## With init
+ test_with_init:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test With Init
+ status: in_progress
+ - run: sleep 30
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ status: completed
+ conclusion: failure
+
+ test_with_init_implicit:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test With Init (Implicit)
+ status: in_progress
+ - run: sleep 30
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ conclusion: failure
+
+ ## Based on job
+ test_based_job_success:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - uses: ./
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test Based On Job (Success)
+ conclusion: ${{ job }}
+
+ test_based_job_failure:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - run: false
+ - uses: ./
+ if: always()
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ name: Test Based On Job (Failure)
+ conclusion: ${{ job }}
diff --git a/.prettierrc.json b/.prettierrc.json
index 9d64c21..bf0887a 100644
--- a/.prettierrc.json
+++ b/.prettierrc.json
@@ -1,8 +1,8 @@
{
- "printWidth": 80,
+ "printWidth": 120,
"tabWidth": 2,
"useTabs": false,
- "semi": false,
+ "semi": true,
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": false,
diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts
index 32cf034..bced80d 100644
--- a/__tests__/main.test.ts
+++ b/__tests__/main.test.ts
@@ -1,15 +1,15 @@
-import * as process from 'process'
-import * as cp from 'child_process'
-import * as path from 'path'
+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['TOKEN'] = 'ABC'
- const ip = path.join(__dirname, '..', 'lib', 'main.js')
+ process.env['TOKEN'] = 'ABC';
+ const ip = path.join(__dirname, '..', 'lib', 'main.js');
const options: cp.ExecSyncOptions = {
env: process.env,
- }
- console.log(cp.execSync(`node ${ip}`, options).toString())
-})
+ };
+ console.log(cp.execSync(`node ${ip}`, options).toString());
+});
// TODO: add more
diff --git a/action.yml b/action.yml
index e7a778d..125221d 100644
--- a/action.yml
+++ b/action.yml
@@ -4,6 +4,29 @@ author: 'Louis Brunner'
inputs:
token:
description: 'your GITHUB_TOKEN'
+ required: true
+ name:
+ description: 'the name of your check'
+ required: true
+ conclusion:
+ description: 'the conclusion of your check'
+ required: true
+ status:
+ description: 'the status of your check'
+ required: false
+ default: completed
+ output:
+ description: 'the output of your check'
+ required: false
+ annotations:
+ description: 'the annotations of your check'
+ required: false
+ images:
+ description: 'the images of your check'
+ required: false
+ actions:
+ description: 'the actions of your check'
+ required: false
runs:
using: 'node12'
main: 'dist/index.js'
diff --git a/dist/index.js b/dist/index.js
index 24aa44e..37237e3 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1 +1 @@
-module.exports=function(e,t){"use strict";var r={};function __webpack_require__(t){if(r[t]){return r[t].exports}var n=r[t]={i:t,l:false,exports:{}};e[t].call(n.exports,n,n.exports,__webpack_require__);n.l=true;return n.exports}__webpack_require__.ab=__dirname+"/";function startup(){return __webpack_require__(429)}return startup()}({87:function(e){e.exports=require("os")},429:function(e,t,r){e.exports=function(e,t){"use strict";var r={};function __webpack_require__(t){if(r[t]){return r[t] [...]
\ No newline at end of file
+module.exports=function(e,t){"use strict";var r={};function __webpack_require__(t){if(r[t]){return r[t].exports}var n=r[t]={i:t,l:false,exports:{}};e[t].call(n.exports,n,n.exports,__webpack_require__);n.l=true;return n.exports}__webpack_require__.ab=__dirname+"/";function startup(){return __webpack_require__(429)}return startup()}({87:function(e){e.exports=require("os")},429:function(e,t,r){e.exports=function(e,t){"use strict";var r={};function __webpack_require__(t){if(r[t]){return r[t] [...]
\ No newline at end of file
diff --git a/jest.config.js b/jest.config.js
index 9bac3cc..f3a27fa 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -8,4 +8,4 @@ module.exports = {
'^.+\\.ts$': 'ts-jest',
},
verbose: true,
-}
+};
diff --git a/package.json b/package.json
index dfd516b..381c53c 100644
--- a/package.json
+++ b/package.json
@@ -6,10 +6,10 @@
"main": "dist/index.js",
"scripts": {
"build": "tsc",
- "lint": "eslint *.js **/*.ts",
+ "lint": "eslint '**/*.js' '**/*.ts'",
"pack": "ncc build -m",
"test": "jest",
- "format": "prettier --write *.js **/*.ts",
+ "format": "prettier --write '**/*.js' '**/*.ts'",
"all": "npm run build && npm run lint && npm run pack && npm test"
},
"repository": {
diff --git a/src/checks.ts b/src/checks.ts
new file mode 100644
index 0000000..8271217
--- /dev/null
+++ b/src/checks.ts
@@ -0,0 +1,69 @@
+import {GitHub} from '@actions/github';
+import * as Inputs from './namespaces/Inputs';
+
+type Ownership = {
+ owner: string;
+ repo: string;
+};
+
+type CreateOptions = {
+ completed: boolean;
+};
+
+const unpackInputs = (inputs: Inputs.Args): object => {
+ let output;
+ if (inputs.output) {
+ output = {
+ summary: inputs.output.summary,
+ text: inputs.output.text_description,
+ actions: inputs.actions,
+ images: inputs.images,
+ };
+ }
+ return {
+ status: inputs.status.toString(),
+ conclusion: inputs.conclusion.toString(),
+ output,
+ actions: inputs.actions,
+ };
+};
+
+const formatDate = (): string => {
+ return new Date().toISOString();
+};
+
+export const createRun = async (
+ octokit: GitHub,
+ 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,
+ started_at: formatDate(),
+ ...dates,
+ ...unpackInputs(inputs),
+ });
+ return data.id;
+};
+
+export const updateRun = async (
+ octokit: GitHub,
+ id: number,
+ ownership: Ownership,
+ inputs: Inputs.Args,
+): Promise<void> => {
+ await octokit.checks.update({
+ ...ownership,
+ check_run_id: id,
+ completed_at: formatDate(),
+ ...unpackInputs(inputs),
+ });
+};
diff --git a/src/inputs.ts b/src/inputs.ts
new file mode 100644
index 0000000..6b5ed0d
--- /dev/null
+++ b/src/inputs.ts
@@ -0,0 +1,51 @@
+import {InputOptions} from '@actions/core';
+import * as Inputs from './namespaces/Inputs';
+
+type GetInput = (name: string, options?: InputOptions | undefined) => string;
+
+const parseJSON = <T>(getInput: GetInput, property: string): T | undefined => {
+ const value = getInput(property);
+ if (!value) {
+ return;
+ }
+ try {
+ const obj = JSON.parse(value);
+ return obj as T;
+ } catch (e) {
+ throw new Error(`invalid format for '${property}: ${e.toString()}`);
+ }
+};
+
+export const parseInputs = (getInput: GetInput): Inputs.Args => {
+ const token = getInput('token', {required: true});
+ const name = getInput('name', {required: true});
+ const statusStr = getInput('status', {required: true});
+ const conclusionStr = getInput('conclusion', {required: true});
+
+ if (!(statusStr in Inputs.Status)) {
+ throw new Error(`invalid value for 'status': '${statusStr}'`);
+ }
+ const status = statusStr as Inputs.Status;
+
+ if (!(conclusionStr in Inputs.Conclusion)) {
+ throw new Error(`invalid value for 'conclusion': '${conclusionStr}'`);
+ }
+ const conclusion = conclusionStr as Inputs.Conclusion;
+
+ const output = parseJSON<Inputs.Output>(getInput, 'output');
+ const annotations = parseJSON<Inputs.Annotations>(getInput, 'annotations');
+ const images = parseJSON<Inputs.Images>(getInput, 'images');
+ const actions = parseJSON<Inputs.Actions>(getInput, 'actions');
+
+ return {
+ name,
+ token,
+ status,
+ conclusion,
+
+ output,
+ annotations,
+ images,
+ actions,
+ };
+};
diff --git a/src/main.ts b/src/main.ts
index 0208cac..2a5ff19 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,44 +1,44 @@
-import * as core from '@actions/core'
-import * as github from '@actions/github'
+import * as core from '@actions/core';
+import * as github from '@actions/github';
+import {parseInputs} from './inputs';
+import {createRun, updateRun} from './checks';
+
+const stateID = 'checkID';
-// eslint-disable-next-line @typescript-eslint/require-await
async function run(): Promise<void> {
try {
- const token: string = core.getInput('token')
+ core.debug(`Parsing inputs`);
+ const inputs = parseInputs(core.getInput);
- core.debug(`Setting up OctoKit`)
- const octokit = new github.GitHub(token)
+ core.debug(`Setting up OctoKit`);
+ const octokit = new github.GitHub(inputs.token);
const ownership = {
owner: github.context.repo.owner,
repo: github.context.repo.repo,
- }
-
- const info = { // TODO: from argument
- }
-
- const { data } = await octokit.checks.listForRef({
- ...ownership,
- ref: github.context.sha,
- })
+ };
+ const sha = github.context.sha;
- if (data.check_runs.length > 0) {
- octokit.checks.update({
- ...ownership,
- check_run_id: data.check_runs[0].id,
- ...info,
- })
- } else {
- octokit.checks.create({
- ...ownership,
- head_sha: github.context.sha,
- name: 'Check Run Test', // TODO: from argument
- ...info,
- })
+ switch (inputs.status) {
+ case 'in_progress':
+ case 'queued': {
+ 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) {
+ updateRun(octokit, parseInt(id), ownership, inputs);
+ } else {
+ createRun(octokit, sha, ownership, inputs);
+ }
+ break;
+ }
}
} catch (error) {
- core.setFailed(error.message)
+ core.setFailed(error.message);
}
}
-run()
+run();
diff --git a/src/namespaces/Inputs.ts b/src/namespaces/Inputs.ts
new file mode 100644
index 0000000..9bfcba3
--- /dev/null
+++ b/src/namespaces/Inputs.ts
@@ -0,0 +1,38 @@
+import {Octokit} from '@octokit/rest';
+
+export type Args = {
+ name: string;
+ token: string;
+ conclusion: Conclusion;
+ status: Status;
+ output?: Output;
+ annotations?: Annotations;
+ images?: Images;
+ actions?: Actions;
+};
+
+export type Annotations = Octokit.ChecksCreateParamsOutputAnnotations[];
+
+export type Images = Octokit.ChecksCreateParamsOutputImages[];
+
+export type Actions = Octokit.ChecksCreateParamsActions[];
+
+export type Output = {
+ summary: string;
+ text_description?: string;
+};
+
+export enum Conclusion {
+ Success = 'success',
+ Failure = 'failure',
+ Neutral = 'neutral',
+ Cancelled = 'cancelled',
+ TimedOut = 'timed_out',
+ ActionRequired = 'action_required',
+}
+
+export enum Status {
+ Queued = 'queued',
+ InProgress = 'in_progress',
+ Completed = 'completed',
+}