From f22e0822060f771152e0da1964ae38bc8fc2ec15 Mon Sep 17 00:00:00 2001 From: rhysd Date: Tue, 5 Nov 2019 14:37:54 +0900 Subject: [PATCH 001/430] start from hello world example of GitHub Action --- .github/workflows/test.yml | 15 +++++++++++++++ .gitignore | 1 + action.yml | 17 +++++++++++++++++ index.js | 15 +++++++++++++++ package.json | 29 +++++++++++++++++++++++++++++ 5 files changed, 77 insertions(+) create mode 100644 .github/workflows/test.yml create mode 100644 .gitignore create mode 100644 action.yml create mode 100644 index.js create mode 100644 package.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..c0a09445b --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,15 @@ +on: [push] + +jobs: + hello_world_job: + runs-on: ubuntu-latest + name: A job to say hello + steps: + - name: Hello world action step + id: hello + uses: actions/hello-world-javascript-action@master + with: + who-to-greet: 'Mona the Octocat' + # Use the output from the `hello` step + - name: Get the output time + run: echo "The time was ${{ steps.hello.outputs.time }}" diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..07e6e472c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/node_modules diff --git a/action.yml b/action.yml new file mode 100644 index 000000000..00c9c1be3 --- /dev/null +++ b/action.yml @@ -0,0 +1,17 @@ +name: 'Hello World' + +description: 'Greet someone and record the time' + +inputs: + who-to-greet: # id of input + description: 'Who to greet' + required: true + default: 'World' + +outputs: + time: # id of output + description: 'The time we greeted you' + +runs: + using: 'node12' + main: 'index.js' diff --git a/index.js b/index.js new file mode 100644 index 000000000..860e8585a --- /dev/null +++ b/index.js @@ -0,0 +1,15 @@ +const core = require('@actions/core'); +const github = require('@actions/github'); + +try { + // `who-to-greet` input defined in action metadata file + const nameToGreet = core.getInput('who-to-greet'); + console.log(`Hello ${nameToGreet}!`); + const time = new Date().toTimeString(); + core.setOutput('time', time); + // Get the JSON webhook payload for the event that triggered the workflow + const payload = JSON.stringify(github.context.payload, undefined, 2); + console.log(`The event payload: ${payload}`); +} catch (error) { + core.setFailed(error.message); +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..ee1da9038 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "github-action-benchmark", + "version": "0.0.0", + "private": true, + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/rhysd/github-action-benchmark.git" + }, + "keywords": [ + "github", + "action", + "benchmark" + ], + "author": "rhysd ", + "license": "MIT", + "bugs": { + "url": "https://github.com/rhysd/github-action-benchmark/issues" + }, + "homepage": "https://github.com/rhysd/github-action-benchmark#readme", + "dependencies": { + "@actions/core": "^1.2.0", + "@actions/github": "^1.1.0" + } +} From 5e3baca5b1d20255a91107974f5dd916ed88cc57 Mon Sep 17 00:00:00 2001 From: rhysd Date: Tue, 5 Nov 2019 15:03:54 +0900 Subject: [PATCH 002/430] use TypeScript --- .github/workflows/test.yml | 6 +- .gitignore | 2 + index.js => index.ts | 4 +- octokit_graphql.d.ts | 5 + package-lock.json | 355 +++++++++++++++++++++++++++++++++++++ package.json | 5 + tsconfig.json | 22 +++ 7 files changed, 396 insertions(+), 3 deletions(-) rename index.js => index.ts (85%) create mode 100644 octokit_graphql.d.ts create mode 100644 package-lock.json create mode 100644 tsconfig.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c0a09445b..b74217e0e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,9 +5,13 @@ jobs: runs-on: ubuntu-latest name: A job to say hello steps: + - uses: actions/checkout@v1 + - uses: actions/setup-node@v1 + - run: npm install + - run: npm run build - name: Hello world action step id: hello - uses: actions/hello-world-javascript-action@master + uses: ./ with: who-to-greet: 'Mona the Octocat' # Use the output from the `hello` step diff --git a/.gitignore b/.gitignore index 07e6e472c..0facbe1c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /node_modules +/index.js +/index.js.map diff --git a/index.js b/index.ts similarity index 85% rename from index.js rename to index.ts index 860e8585a..d5c0207a3 100644 --- a/index.js +++ b/index.ts @@ -1,5 +1,5 @@ -const core = require('@actions/core'); -const github = require('@actions/github'); +import * as core from '@actions/core'; +import * as github from '@actions/github'; try { // `who-to-greet` input defined in action metadata file diff --git a/octokit_graphql.d.ts b/octokit_graphql.d.ts new file mode 100644 index 000000000..8380b2c67 --- /dev/null +++ b/octokit_graphql.d.ts @@ -0,0 +1,5 @@ +// Workaround for @actions/core until @octokit/graphql v4 is used +declare module '@octokit/graphql' { + type GraphQlQueryResponse = unknown; + type Variables = unknown; +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..a6d260822 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,355 @@ +{ + "name": "github-action-benchmark", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@actions/core": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.0.tgz", + "integrity": "sha512-ZKdyhlSlyz38S6YFfPnyNgCDZuAF2T0Qv5eHflNWytPS8Qjvz39bZFMry9Bb/dpSnqWcNeav5yM2CTYpJeY+Dw==" + }, + "@actions/github": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-1.1.0.tgz", + "integrity": "sha512-cHf6PyoNMdei13jEdGPhKprIMFmjVVW/dnM5/9QmQDJ1ZTaGVyezUSCUIC/ySNLRvDUpeFwPYMdThSEJldSbUw==", + "requires": { + "@octokit/graphql": "^2.0.1", + "@octokit/rest": "^16.15.0" + } + }, + "@octokit/endpoint": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.5.1.tgz", + "integrity": "sha512-nBFhRUb5YzVTCX/iAK1MgQ4uWo89Gu0TH00qQHoYRCsE12dWcG1OiLd7v2EIo2+tpUKPMOQ62QFy9hy9Vg2ULg==", + "requires": { + "@octokit/types": "^2.0.0", + "is-plain-object": "^3.0.0", + "universal-user-agent": "^4.0.0" + }, + "dependencies": { + "universal-user-agent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz", + "integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==", + "requires": { + "os-name": "^3.1.0" + } + } + } + }, + "@octokit/graphql": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-2.1.3.tgz", + "integrity": "sha512-XoXJqL2ondwdnMIW3wtqJWEwcBfKk37jO/rYkoxNPEVeLBDGsGO1TCWggrAlq3keGt/O+C/7VepXnukUxwt5vA==", + "requires": { + "@octokit/request": "^5.0.0", + "universal-user-agent": "^2.0.3" + } + }, + "@octokit/request": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.3.1.tgz", + "integrity": "sha512-5/X0AL1ZgoU32fAepTfEoggFinO3rxsMLtzhlUX+RctLrusn/CApJuGFCd0v7GMFhF+8UiCsTTfsu7Fh1HnEJg==", + "requires": { + "@octokit/endpoint": "^5.5.0", + "@octokit/request-error": "^1.0.1", + "@octokit/types": "^2.0.0", + "deprecation": "^2.0.0", + "is-plain-object": "^3.0.0", + "node-fetch": "^2.3.0", + "once": "^1.4.0", + "universal-user-agent": "^4.0.0" + }, + "dependencies": { + "universal-user-agent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz", + "integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==", + "requires": { + "os-name": "^3.1.0" + } + } + } + }, + "@octokit/request-error": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.0.tgz", + "integrity": "sha512-DNBhROBYjjV/I9n7A8kVkmQNkqFAMem90dSxqvPq57e2hBr7mNTX98y3R2zDpqMQHVRpBDjsvsfIGgBzy+4PAg==", + "requires": { + "@octokit/types": "^2.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "16.34.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.34.1.tgz", + "integrity": "sha512-JUoS12cdktf1fv86rgrjC/RvYLuL+o7p57W7zX1x7ANFJ7OvdV8emvUNkFlcidEaOkYrxK3SoWgQFt3FhNmabA==", + "requires": { + "@octokit/request": "^5.2.0", + "@octokit/request-error": "^1.0.2", + "atob-lite": "^2.0.0", + "before-after-hook": "^2.0.0", + "btoa-lite": "^1.0.0", + "deprecation": "^2.0.0", + "lodash.get": "^4.4.2", + "lodash.set": "^4.3.2", + "lodash.uniq": "^4.5.0", + "octokit-pagination-methods": "^1.1.0", + "once": "^1.4.0", + "universal-user-agent": "^4.0.0" + }, + "dependencies": { + "universal-user-agent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz", + "integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==", + "requires": { + "os-name": "^3.1.0" + } + } + } + }, + "@octokit/types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.0.1.tgz", + "integrity": "sha512-YDYgV6nCzdGdOm7wy43Ce8SQ3M5DMKegB8E5sTB/1xrxOdo2yS/KgUgML2N2ZGD621mkbdrAglwTyA4NDOlFFA==", + "requires": { + "@types/node": ">= 8" + } + }, + "@types/node": { + "version": "12.12.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.5.tgz", + "integrity": "sha512-KEjODidV4XYUlJBF3XdjSH5FWoMCtO0utnhtdLf1AgeuZLOrRbvmU/gaRCVg7ZaQDjVf3l84egiY0mRNe5xE4A==" + }, + "atob-lite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", + "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=" + }, + "before-after-hook": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz", + "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==" + }, + "btoa-lite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", + "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=" + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "is-plain-object": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", + "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", + "requires": { + "isobject": "^4.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", + "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "lodash.set": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" + }, + "macos-release": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz", + "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "octokit-pagination-methods": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz", + "integrity": "sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "os-name": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", + "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", + "requires": { + "macos-release": "^2.2.0", + "windows-release": "^3.1.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "typescript": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz", + "integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==", + "dev": true + }, + "universal-user-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-2.1.0.tgz", + "integrity": "sha512-8itiX7G05Tu3mGDTdNY2fB4KJ8MgZLS54RdG6PkkfwMAavrXu1mV/lls/GABx9O3Rw4PnTtasxrvbMQoBYY92Q==", + "requires": { + "os-name": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "windows-release": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz", + "integrity": "sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA==", + "requires": { + "execa": "^1.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } +} diff --git a/package.json b/package.json index ee1da9038..cc8f5221a 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,8 @@ "description": "", "main": "index.js", "scripts": { + "build": "tsc -p .", + "watch": "tsc -p . --watch", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { @@ -25,5 +27,8 @@ "dependencies": { "@actions/core": "^1.2.0", "@actions/github": "^1.1.0" + }, + "devDependencies": { + "typescript": "^3.7.0" } } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..bfffab545 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "preserveConstEnums": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noEmitOnError": true, + "strictNullChecks": true, + "target": "es2019", + "sourceMap": true, + "esModuleInterop": true, + "resolveJsonModule": true + }, + "files": [ + "index.ts", + "octokit_graphql.d.ts" + ] +} From 7d0f7754065303f17e1a4c00f7cc8ec7eb1ee294 Mon Sep 17 00:00:00 2001 From: rhysd Date: Thu, 7 Nov 2019 18:58:00 +0900 Subject: [PATCH 003/430] first implementation --- action.yml | 28 ++++++++++----- config.ts | 92 +++++++++++++++++++++++++++++++++++++++++++++++ extract.ts | 66 ++++++++++++++++++++++++++++++++++ git.ts | 42 ++++++++++++++++++++++ index.ts | 28 ++++++++------- package-lock.json | 16 +++++++-- package.json | 4 ++- tsconfig.json | 4 +++ write.ts | 63 ++++++++++++++++++++++++++++++++ 9 files changed, 317 insertions(+), 26 deletions(-) create mode 100644 config.ts create mode 100644 extract.ts create mode 100644 git.ts create mode 100644 write.ts diff --git a/action.yml b/action.yml index 00c9c1be3..73a414ba8 100644 --- a/action.yml +++ b/action.yml @@ -1,16 +1,26 @@ -name: 'Hello World' +name: 'Continuous Benchmark' -description: 'Greet someone and record the time' +description: 'Continuous Benchmark using GitHub pages as dash board' inputs: - who-to-greet: # id of input - description: 'Who to greet' + name: + description: 'Name of the benchmark. This value must be identical among all benchmarks' required: true - default: 'World' - -outputs: - time: # id of output - description: 'The time we greeted you' + default: 'Benchmark' + tool: + description: 'Tool to use get benchmark output. One of "cargo", ... (TODO)' + required: true + output-file-path: + description: 'A path to file which contains the benchmark output' + required: true + gh-pages-branch: + description: 'Branch for gh-pages' + required: true + default: 'gh-pages' + benchmark-data-dir-path: + description: 'Path to directory which contains JSON files benchmarks' + required: true + default: 'dev/bench' runs: using: 'node12' diff --git a/config.ts b/config.ts new file mode 100644 index 000000000..11b2ee970 --- /dev/null +++ b/config.ts @@ -0,0 +1,92 @@ +import * as core from '@actions/core'; +import {promises as fs, Stats} from 'fs'; +import * as os from 'os'; +import * as path from 'path'; + +export type ToolType = 'cargo'; +export interface Config { + name: string, + tool: ToolType; + outputFilePath: string; + ghPagesBranch: string; + benchmarkDataDirPath: string; +} + +const VALID_TOOLS = ['cargo']; + +function validateToolType(tool: string): asserts tool is ToolType { + if (VALID_TOOLS.includes(tool)) { + return; + } + throw new Error(`Invalid value '${tool}' for 'tool' input. It must be one of ${VALID_TOOLS}`); +} + +function resolvePath(p: string): string { + if (p[0] === '~') { + const home = os.homedir(); + if (!home) { + throw new Error("Cannot resolve '~'"); + } + p = path.join(home, p.slice(1)); + } + return path.resolve(p); +} + +async function statPath(p: string): Promise<[Stats, string]> { + p = resolvePath(p); + try { + return [await fs.stat(p), p]; + } catch(e) { + throw new Error(`Cannot stat '${p}': ${e}`); + } +} + +async function validateOutputFilePath(filePath: string): Promise { + try { + const [stat, resolved] = await statPath(filePath); + if (!stat.isFile()) { + throw new Error(`Specified path '${filePath}' is not a file`); + } + return resolved; + } catch (err) { + throw new Error(`Invalid value for 'output-file-path' input: ${err}`); + } +} + +function validateGhPagesBranch(branch: string) { + if (branch) { + return; + } + throw new Error(`Branch value must not be empty for 'gh-pages-branch' input`); +} + +function validateBenchmarkDataDirPath(dirPath: string): string { + try { + return resolvePath(dirPath); + } catch(e) { + throw new Error(`Invalid value for 'benchmark-data-dir-path': ${e}`); + } +} + +function validateName(name: string) { + if (name) { + return; + } + throw new Error('Name must not be empty'); +} + +export async function configFromJobInput(): Promise { + const tool: string = core.getInput('tool'); + let outputFilePath: string = core.getInput('output-file-path'); + const ghPagesBranch: string = core.getInput('gh-pages-branch'); + let benchmarkDataDirPath: string = core.getInput('benchmark-data-dir-path'); + const name: string = core.getInput('name'); + + validateToolType(tool); + outputFilePath = await validateOutputFilePath(outputFilePath); + validateGhPagesBranch(ghPagesBranch); + benchmarkDataDirPath = validateBenchmarkDataDirPath(benchmarkDataDirPath); + validateName(name); + + return { name, tool, outputFilePath, ghPagesBranch, benchmarkDataDirPath }; +} diff --git a/extract.ts b/extract.ts new file mode 100644 index 000000000..a23b8454e --- /dev/null +++ b/extract.ts @@ -0,0 +1,66 @@ +import { promises as fs } from 'fs'; +import * as github from '@actions/github'; +import { Config } from './config'; + +export interface BenchmarkResult { + name: string; + value: number; + diff?: number; + unit: string; +} + +export interface Benchmark { + commit: string; + date: number; + benches: BenchmarkResult[]; +} + +function extractCargoResult(output: string): BenchmarkResult[] { + const lines = output.split('\n'); + const ret = []; + const reExtract = /^test (\w+)\s+\.\.\. bench:\s+([0-9,]+) ns\/iter \(\+\/- ([0-9,]+)\)$/; + const reComma = /,/g; + + for (const line of lines) { + const m = line.match(reExtract); + if (m === null) { + continue; + } + + const name = m[1]; + const value = parseInt(m[2].replace(reComma, ''), 10); + const diff = parseInt(m[3].replace(reComma, ''), 10); + + ret.push({ + name, + value, + diff, + unit: 'ns/iter', + }); + } + + return ret; +} + +export async function extractResult(config: Config): Promise { + const output = await fs.readFile(config.outputFilePath, 'utf8'); + const { tool } = config; + let benches; + + switch (tool) { + case 'cargo': + benches = extractCargoResult(output); + default: + throw new Error(`FATAL: Unexpected tool: ${tool}`); + } + + if (benches.length === 0) { + throw new Error(`No benchmark result was found in ${config.outputFilePath}. Benchmark output was '${output}'`); + } + + return { + commit: github.context.payload.id, + date: Date.now(), + benches, + }; +} diff --git a/git.ts b/git.ts new file mode 100644 index 000000000..c76f7c2a7 --- /dev/null +++ b/git.ts @@ -0,0 +1,42 @@ +import * as actionsExec from '@actions/exec'; +import * as core from '@actions/core'; + +interface ExecResult { + stdout: string; + stderr: string; + code: number | null; +} + +async function exec(cmd: string, args: string[]): Promise { + const res: ExecResult = { + stdout: '', + stderr: '', + code: null, + }; + + try { + const code = await actionsExec.exec(cmd, args, { + listeners: { + stdout(data) { + res.stdout += data.toString(); + }, + stderr(data) { + res.stderr += data.toString(); + }, + }, + }); + res.code = code; + return res; + } catch (err) { + core.debug(JSON.stringify(res)); + throw err; + } +} + +export default async function git(...args: string[]): Promise { + const res = await exec('git', args); + if (res.code !== 0) { + throw new Error(`Command 'git ${args.join(' ')}' failed: ${res}`); + } + return res.stdout; +} diff --git a/index.ts b/index.ts index d5c0207a3..af129ff52 100644 --- a/index.ts +++ b/index.ts @@ -1,15 +1,17 @@ import * as core from '@actions/core'; -import * as github from '@actions/github'; - -try { - // `who-to-greet` input defined in action metadata file - const nameToGreet = core.getInput('who-to-greet'); - console.log(`Hello ${nameToGreet}!`); - const time = new Date().toTimeString(); - core.setOutput('time', time); - // Get the JSON webhook payload for the event that triggered the workflow - const payload = JSON.stringify(github.context.payload, undefined, 2); - console.log(`The event payload: ${payload}`); -} catch (error) { - core.setFailed(error.message); +import { configFromJobInput } from './config'; +import { extractResult } from './extract'; +import { writeBenchmark } from './write'; + +async function main() { + const config = await configFromJobInput(); + core.debug(`Config extracted from job: ${config}`); + + const bench = await extractResult(config); + core.debug(`Benchmark result was extracted: ${bench}`); + + await writeBenchmark(bench, config); + core.debug('Benchmark was pushed successfully'); } + +main().catch(e => core.setFailed(e.message)); diff --git a/package-lock.json b/package-lock.json index a6d260822..477a14c8a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,11 @@ "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.0.tgz", "integrity": "sha512-ZKdyhlSlyz38S6YFfPnyNgCDZuAF2T0Qv5eHflNWytPS8Qjvz39bZFMry9Bb/dpSnqWcNeav5yM2CTYpJeY+Dw==" }, + "@actions/exec": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.1.tgz", + "integrity": "sha512-nvFkxwiicvpzNiCBF4wFBDfnBvi7xp/as7LE1hBxBxKG2L29+gkIPBiLKMVORL+Hg3JNf07AKRfl0V5djoypjQ==" + }, "@actions/github": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@actions/github/-/github-1.1.0.tgz", @@ -18,6 +23,11 @@ "@octokit/rest": "^16.15.0" } }, + "@actions/io": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.1.tgz", + "integrity": "sha512-rhq+tfZukbtaus7xyUtwKfuiCRXd1hWSfmJNEpFgBQJ4woqPEpsBw04awicjwz9tyG2/MVhAEMfVn664Cri5zA==" + }, "@octokit/endpoint": { "version": "5.5.1", "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.5.1.tgz", @@ -317,9 +327,9 @@ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, "typescript": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz", - "integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.2.tgz", + "integrity": "sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==", "dev": true }, "universal-user-agent": { diff --git a/package.json b/package.json index cc8f5221a..9e5156057 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,9 @@ "homepage": "https://github.com/rhysd/github-action-benchmark#readme", "dependencies": { "@actions/core": "^1.2.0", - "@actions/github": "^1.1.0" + "@actions/exec": "^1.0.1", + "@actions/github": "^1.1.0", + "@actions/io": "^1.0.1" }, "devDependencies": { "typescript": "^3.7.0" diff --git a/tsconfig.json b/tsconfig.json index bfffab545..2745d1fbc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,10 @@ }, "files": [ "index.ts", + "config.ts", + "extract.ts", + "git.ts", + "write.ts", "octokit_graphql.d.ts" ] } diff --git a/write.ts b/write.ts new file mode 100644 index 000000000..2e5f6a211 --- /dev/null +++ b/write.ts @@ -0,0 +1,63 @@ +import { promises as fs } from 'fs'; +import * as path from 'path'; +import * as io from '@actions/io'; +import * as core from '@actions/core'; +import git from './git'; +import { Benchmark } from './extract'; +import { Config } from './config'; + +type BenchmarkEntries = { [name: string]: Benchmark[] }; +interface DataJson { + lastUpdate: number; + benches: BenchmarkEntries; +} + +async function loadDataJson(jsonPath: string): Promise { + try { + const json = await fs.readFile(jsonPath, 'utf8'); + return JSON.parse(json); + } catch (err) { + core.debug(`Could not load data.json. Using empty default: ${err}`); + return { + lastUpdate: 0, + benches: {}, + }; + } +} + +function addBenchmark(benches: BenchmarkEntries, name: string, bench: Benchmark) { + if (benches[name] === undefined) { + benches[name] = []; + } + benches[name].push(bench); +} + +export async function writeBenchmark(bench: Benchmark, config: Config) { + const { name, tool, ghPagesBranch, benchmarkDataDirPath } = config; + const jsonPath = path.join(benchmarkDataDirPath, 'data.json'); + + await git('checkout', ghPagesBranch); + try { + // Remote may be updated after checkout. Ensure to be able to push + await git('pull', '--rebase', 'origin', ghPagesBranch); + + await io.mkdirP(benchmarkDataDirPath); + + const data = await loadDataJson(jsonPath); + data.lastUpdate = Date.now(); + + addBenchmark(data.benches, name, bench); + + await fs.writeFile(jsonPath, JSON.stringify(data, null, 2), 'utf8'); + + await git('add', jsonPath); + + // TODO: Write default index.html if not found + + await git('commit', '-m', `add ${tool} benchmark result for ${bench.commit}`); + + await git('push', 'origin', ghPagesBranch); + } finally { + await git('checkout', '-'); + } +} From 92ced84f26ae8460eef7873c8c6987a0fe1912dd Mon Sep 17 00:00:00 2001 From: rhysd Date: Thu, 7 Nov 2019 23:18:54 +0900 Subject: [PATCH 004/430] add Rust example for test --- .gitignore | 6 ++++-- examples/rust/Cargo.toml | 9 +++++++++ examples/rust/benches/bench.rs | 20 ++++++++++++++++++++ examples/rust/src/lib.rs | 7 +++++++ 4 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 examples/rust/Cargo.toml create mode 100644 examples/rust/benches/bench.rs create mode 100644 examples/rust/src/lib.rs diff --git a/.gitignore b/.gitignore index 0facbe1c1..5a296a7b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /node_modules -/index.js -/index.js.map +/*.js +/*.js.map +/examples/rust/Cargo.lock +/examples/rust/target diff --git a/examples/rust/Cargo.toml b/examples/rust/Cargo.toml new file mode 100644 index 000000000..c47cf0713 --- /dev/null +++ b/examples/rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rust_example" +version = "0.1.0" +authors = ["rhysd "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/examples/rust/benches/bench.rs b/examples/rust/benches/bench.rs new file mode 100644 index 000000000..b5671cfef --- /dev/null +++ b/examples/rust/benches/bench.rs @@ -0,0 +1,20 @@ +#![feature(test)] + +extern crate test; + +use rust_example::fib; +use test::Bencher; + +#[bench] +fn bench_fib_10(b: &mut Bencher) { + b.iter(|| { + let _ = fib(10); + }); +} + +#[bench] +fn bench_fib_20(b: &mut Bencher) { + b.iter(|| { + let _ = fib(20); + }); +} diff --git a/examples/rust/src/lib.rs b/examples/rust/src/lib.rs new file mode 100644 index 000000000..13893643c --- /dev/null +++ b/examples/rust/src/lib.rs @@ -0,0 +1,7 @@ +pub fn fib(u: u32) -> u32 { + if u <= 1 { + 1 + } else { + fib(u - 2) + fib(u - 1) + } +} From 7b34f1ae603b9cd0fe53695bb48bbd6f61939671 Mon Sep 17 00:00:00 2001 From: rhysd Date: Thu, 7 Nov 2019 23:24:16 +0900 Subject: [PATCH 005/430] run Rust example on CI --- .github/workflows/rust.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/rust.yml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 000000000..58db0dfe5 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,19 @@ +on: [push] + +jobs: + hello_world_job: + name: Run Rust benchmark example + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-node@v1 + - run: npm install + - run: npm run build + - run: rustup update nightly && rustup default nightly + - name: Run benchmark + run: cd examples/rust && cargo +nightly bench | tee output.txt + - name: Store benchmark result + uses: ./ + with: + tool: 'cargo' + output-file-path: examples/rust/output.txt From 2c3d61f5d77aecad8c39702aa85c09d7bf957ccd Mon Sep 17 00:00:00 2001 From: rhysd Date: Thu, 7 Nov 2019 23:29:40 +0900 Subject: [PATCH 006/430] remove old workflow --- .github/workflows/test.yml | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index b74217e0e..000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,19 +0,0 @@ -on: [push] - -jobs: - hello_world_job: - runs-on: ubuntu-latest - name: A job to say hello - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-node@v1 - - run: npm install - - run: npm run build - - name: Hello world action step - id: hello - uses: ./ - with: - who-to-greet: 'Mona the Octocat' - # Use the output from the `hello` step - - name: Get the output time - run: echo "The time was ${{ steps.hello.outputs.time }}" From e36bb4b61d77473e52ab18fdf2e620bbf115a397 Mon Sep 17 00:00:00 2001 From: rhysd Date: Thu, 7 Nov 2019 23:33:24 +0900 Subject: [PATCH 007/430] add cache for node_modules --- .github/workflows/rust.yml | 9 ++++++++- extract.ts | 3 ++- write.ts | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 58db0dfe5..005cdd90b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,3 +1,4 @@ +name: Rust Example on: [push] jobs: @@ -7,9 +8,15 @@ jobs: steps: - uses: actions/checkout@v1 - uses: actions/setup-node@v1 + - uses: actions/cache@v1 + with: + path: node_modules + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- - run: npm install - run: npm run build - - run: rustup update nightly && rustup default nightly + - run: rustup toolchain update nightly && rustup default nightly - name: Run benchmark run: cd examples/rust && cargo +nightly bench | tee output.txt - name: Store benchmark result diff --git a/extract.ts b/extract.ts index a23b8454e..b1b681671 100644 --- a/extract.ts +++ b/extract.ts @@ -50,8 +50,9 @@ export async function extractResult(config: Config): Promise { switch (tool) { case 'cargo': benches = extractCargoResult(output); + break; default: - throw new Error(`FATAL: Unexpected tool: ${tool}`); + throw new Error(`FATAL: Unexpected tool: '${tool}'`); } if (benches.length === 0) { diff --git a/write.ts b/write.ts index 2e5f6a211..dcc7a6bb0 100644 --- a/write.ts +++ b/write.ts @@ -36,7 +36,7 @@ export async function writeBenchmark(bench: Benchmark, config: Config) { const { name, tool, ghPagesBranch, benchmarkDataDirPath } = config; const jsonPath = path.join(benchmarkDataDirPath, 'data.json'); - await git('checkout', ghPagesBranch); + await git('switch', ghPagesBranch); try { // Remote may be updated after checkout. Ensure to be able to push await git('pull', '--rebase', 'origin', ghPagesBranch); @@ -58,6 +58,6 @@ export async function writeBenchmark(bench: Benchmark, config: Config) { await git('push', 'origin', ghPagesBranch); } finally { - await git('checkout', '-'); + await git('switch', '-'); } } From d8e4a7cdddc9785ed270a58e44494df9c1a21062 Mon Sep 17 00:00:00 2001 From: rhysd Date: Thu, 7 Nov 2019 23:58:09 +0900 Subject: [PATCH 008/430] fix mistakes by checking rust example --- .github/workflows/rust.yml | 2 ++ extract.ts | 5 ++++- index.ts | 2 +- write.ts | 18 +++++++++++------- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 005cdd90b..a1d504236 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -24,3 +24,5 @@ jobs: with: tool: 'cargo' output-file-path: examples/rust/output.txt + - name: Push benchmark result + run: git push 'https://rhysd:${{ secrets.GITHUB_TOKEN }}@github.com/rhysd/github-action-benchmark.git' gh-pages:gh-pages diff --git a/extract.ts b/extract.ts index b1b681671..ef5d3e62a 100644 --- a/extract.ts +++ b/extract.ts @@ -1,5 +1,6 @@ import { promises as fs } from 'fs'; import * as github from '@actions/github'; +import * as core from '@actions/core'; import { Config } from './config'; export interface BenchmarkResult { @@ -59,8 +60,10 @@ export async function extractResult(config: Config): Promise { throw new Error(`No benchmark result was found in ${config.outputFilePath}. Benchmark output was '${output}'`); } + core.debug(`GitHub payload: ${github.context.payload}`); + return { - commit: github.context.payload.id, + commit: github.context.payload.after, date: Date.now(), benches, }; diff --git a/index.ts b/index.ts index af129ff52..f2144eda5 100644 --- a/index.ts +++ b/index.ts @@ -11,7 +11,7 @@ async function main() { core.debug(`Benchmark result was extracted: ${bench}`); await writeBenchmark(bench, config); - core.debug('Benchmark was pushed successfully'); + console.log('Benchmark was pushed successfully!', bench, config); } main().catch(e => core.setFailed(e.message)); diff --git a/write.ts b/write.ts index dcc7a6bb0..1bc0499a7 100644 --- a/write.ts +++ b/write.ts @@ -38,9 +38,6 @@ export async function writeBenchmark(bench: Benchmark, config: Config) { await git('switch', ghPagesBranch); try { - // Remote may be updated after checkout. Ensure to be able to push - await git('pull', '--rebase', 'origin', ghPagesBranch); - await io.mkdirP(benchmarkDataDirPath); const data = await loadDataJson(jsonPath); @@ -54,10 +51,17 @@ export async function writeBenchmark(bench: Benchmark, config: Config) { // TODO: Write default index.html if not found - await git('commit', '-m', `add ${tool} benchmark result for ${bench.commit}`); - - await git('push', 'origin', ghPagesBranch); + await git( + '-c', + 'user.name=github-action-benchmark', + '-c', + 'user.email=github@users.noreply.github.com', + 'commit', + '-m', + `add ${tool} benchmark result for ${bench.commit}`, + ); } finally { - await git('switch', '-'); + // `git switch` does not work for backing to detached head + await git('checkout', '-'); } } From b569ed487de805da56441da6e15ee38d9a81a2d6 Mon Sep 17 00:00:00 2001 From: rhysd Date: Fri, 8 Nov 2019 12:45:59 +0900 Subject: [PATCH 009/430] use data.js instead of data.json --- .github/workflows/rust.yml | 1 + extract.ts | 7 ++++--- index.ts | 2 +- write.ts | 34 +++++++++++++++++++++------------- 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a1d504236..d40b55866 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -22,6 +22,7 @@ jobs: - name: Store benchmark result uses: ./ with: + name: Rust Benchmark tool: 'cargo' output-file-path: examples/rust/output.txt - name: Push benchmark result diff --git a/extract.ts b/extract.ts index ef5d3e62a..1427af4b2 100644 --- a/extract.ts +++ b/extract.ts @@ -6,7 +6,7 @@ import { Config } from './config'; export interface BenchmarkResult { name: string; value: number; - diff?: number; + range?: number; unit: string; } @@ -30,12 +30,12 @@ function extractCargoResult(output: string): BenchmarkResult[] { const name = m[1]; const value = parseInt(m[2].replace(reComma, ''), 10); - const diff = parseInt(m[3].replace(reComma, ''), 10); + const range = parseInt(m[3].replace(reComma, ''), 10); ret.push({ name, value, - diff, + range, unit: 'ns/iter', }); } @@ -61,6 +61,7 @@ export async function extractResult(config: Config): Promise { } core.debug(`GitHub payload: ${github.context.payload}`); + console.log(github.context.payload); return { commit: github.context.payload.after, diff --git a/index.ts b/index.ts index f2144eda5..288e00576 100644 --- a/index.ts +++ b/index.ts @@ -11,7 +11,7 @@ async function main() { core.debug(`Benchmark result was extracted: ${bench}`); await writeBenchmark(bench, config); - console.log('Benchmark was pushed successfully!', bench, config); + console.log('Benchmark was pushed successfully!', '\nData:', bench, '\nConfig:', config); } main().catch(e => core.setFailed(e.message)); diff --git a/write.ts b/write.ts index 1bc0499a7..43309a901 100644 --- a/write.ts +++ b/write.ts @@ -9,45 +9,53 @@ import { Config } from './config'; type BenchmarkEntries = { [name: string]: Benchmark[] }; interface DataJson { lastUpdate: number; - benches: BenchmarkEntries; + entries: BenchmarkEntries; } -async function loadDataJson(jsonPath: string): Promise { +const SCRIPT_PREFIX = 'window.BENCHMARK_DATA = '; + +async function loadDataJson(dataPath: string): Promise { try { - const json = await fs.readFile(jsonPath, 'utf8'); + const script = await fs.readFile(dataPath, 'utf8'); + const json = script.slice(SCRIPT_PREFIX.length); return JSON.parse(json); } catch (err) { core.debug(`Could not load data.json. Using empty default: ${err}`); return { lastUpdate: 0, - benches: {}, + entries: {}, }; } } -function addBenchmark(benches: BenchmarkEntries, name: string, bench: Benchmark) { - if (benches[name] === undefined) { - benches[name] = []; +async function storeDataJson(dataPath: string, data: DataJson) { + const script = SCRIPT_PREFIX + JSON.stringify(data, null, 2); + await fs.writeFile(dataPath, script, 'utf8'); +} + +function addBenchmark(entries: BenchmarkEntries, name: string, bench: Benchmark) { + if (entries[name] === undefined) { + entries[name] = []; } - benches[name].push(bench); + entries[name].push(bench); } export async function writeBenchmark(bench: Benchmark, config: Config) { const { name, tool, ghPagesBranch, benchmarkDataDirPath } = config; - const jsonPath = path.join(benchmarkDataDirPath, 'data.json'); + const dataPath = path.join(benchmarkDataDirPath, 'data.js'); await git('switch', ghPagesBranch); try { await io.mkdirP(benchmarkDataDirPath); - const data = await loadDataJson(jsonPath); + const data = await loadDataJson(dataPath); data.lastUpdate = Date.now(); - addBenchmark(data.benches, name, bench); + addBenchmark(data.entries, name, bench); - await fs.writeFile(jsonPath, JSON.stringify(data, null, 2), 'utf8'); + await storeDataJson(dataPath, data); - await git('add', jsonPath); + await git('add', dataPath); // TODO: Write default index.html if not found From 40453e27bc5b1fb391321af497bebdb78cb67ce7 Mon Sep 17 00:00:00 2001 From: rhysd Date: Fri, 8 Nov 2019 16:27:35 +0900 Subject: [PATCH 010/430] include commit payload --- extract.ts | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/extract.ts b/extract.ts index 1427af4b2..3daf734d3 100644 --- a/extract.ts +++ b/extract.ts @@ -11,7 +11,24 @@ export interface BenchmarkResult { } export interface Benchmark { - commit: string; + commit: { + author: { + email: string; + name: string; + username: string; + }; + committer: { + email: string; + name: string; + username: string; + }; + distinct: boolean; + id: string; + message: string; + timestamp: string; + tree_id: string; + url: string; + }; date: number; benches: BenchmarkResult[]; } @@ -64,7 +81,7 @@ export async function extractResult(config: Config): Promise { console.log(github.context.payload); return { - commit: github.context.payload.after, + commit: github.context.payload.head_commit, date: Date.now(), benches, }; From 4b1a3753b6d66db27e6e7f52e7f452670522d3b3 Mon Sep 17 00:00:00 2001 From: rhysd Date: Fri, 8 Nov 2019 16:58:16 +0900 Subject: [PATCH 011/430] include tool to benchmark data --- extract.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extract.ts b/extract.ts index 3daf734d3..de759e671 100644 --- a/extract.ts +++ b/extract.ts @@ -30,6 +30,7 @@ export interface Benchmark { url: string; }; date: number; + tool: string; benches: BenchmarkResult[]; } @@ -83,6 +84,7 @@ export async function extractResult(config: Config): Promise { return { commit: github.context.payload.head_commit, date: Date.now(), + tool, benches, }; } From 55db2f2b15a10d7e20d811dccc5ae796bce18d00 Mon Sep 17 00:00:00 2001 From: rhysd Date: Fri, 8 Nov 2019 17:59:52 +0900 Subject: [PATCH 012/430] add default index.html if necessary --- default_index_html.ts | 179 ++++++++++++++++++++++++++++++++++++++++++ extract.ts | 1 - write.ts | 24 +++++- 3 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 default_index_html.ts diff --git a/default_index_html.ts b/default_index_html.ts new file mode 100644 index 000000000..f91999d0c --- /dev/null +++ b/default_index_html.ts @@ -0,0 +1,179 @@ +export const DEFAULT_INDEX_HTML = ` + + + + + + Benchmarks + + + + +
+
+ + + + + + +`; diff --git a/extract.ts b/extract.ts index de759e671..a8a0fac82 100644 --- a/extract.ts +++ b/extract.ts @@ -79,7 +79,6 @@ export async function extractResult(config: Config): Promise { } core.debug(`GitHub payload: ${github.context.payload}`); - console.log(github.context.payload); return { commit: github.context.payload.head_commit, diff --git a/write.ts b/write.ts index 43309a901..cc0563e6e 100644 --- a/write.ts +++ b/write.ts @@ -5,6 +5,7 @@ import * as core from '@actions/core'; import git from './git'; import { Benchmark } from './extract'; import { Config } from './config'; +import { DEFAULT_INDEX_HTML } from './default_index_html'; type BenchmarkEntries = { [name: string]: Benchmark[] }; interface DataJson { @@ -40,6 +41,25 @@ function addBenchmark(entries: BenchmarkEntries, name: string, bench: Benchmark) entries[name].push(bench); } +async function addIndexHtmlIfNeeded(dir: string) { + console.log('hello', dir); + const indexHtml = path.join(dir, 'index.html'); + try { + await fs.stat(indexHtml); + core.debug(`Skipped to create default index.html since it is already existing: ${indexHtml}`); + console.log('skipped!', dir); + return; + } catch (_) { + // Continue + } + + console.log('will create!', dir); + await fs.writeFile(indexHtml, DEFAULT_INDEX_HTML, 'utf8'); + await git('add', indexHtml); + core.debug(`Created default index.html at ${indexHtml}`); + console.log('did create!', dir); +} + export async function writeBenchmark(bench: Benchmark, config: Config) { const { name, tool, ghPagesBranch, benchmarkDataDirPath } = config; const dataPath = path.join(benchmarkDataDirPath, 'data.js'); @@ -57,7 +77,7 @@ export async function writeBenchmark(bench: Benchmark, config: Config) { await git('add', dataPath); - // TODO: Write default index.html if not found + await addIndexHtmlIfNeeded(benchmarkDataDirPath); await git( '-c', @@ -66,7 +86,7 @@ export async function writeBenchmark(bench: Benchmark, config: Config) { 'user.email=github@users.noreply.github.com', 'commit', '-m', - `add ${tool} benchmark result for ${bench.commit}`, + `add ${name} (${tool}) benchmark result for ${bench.commit}`, ); } finally { // `git switch` does not work for backing to detached head From b92a80431e523ea0e6354a18b6366fddc42d1722 Mon Sep 17 00:00:00 2001 From: rhysd Date: Sat, 9 Nov 2019 22:01:17 +0900 Subject: [PATCH 013/430] update default index.html for better styling --- default_index_html.ts | 255 +++++++++++++++++++++++------------------- 1 file changed, 137 insertions(+), 118 deletions(-) diff --git a/default_index_html.ts b/default_index_html.ts index f91999d0c..3b10c844e 100644 --- a/default_index_html.ts +++ b/default_index_html.ts @@ -1,25 +1,39 @@ -export const DEFAULT_INDEX_HTML = ` +export const DEFAULT_INDEX_HTML = String.raw`