feat: comment on PR with validation outcome (#503)

* comment on PR after validation run.

* bork my url for sample output

* populate GITHUB_TOKEN in action

* more verbose logging

* more logging

* fix wrong import

* logging

* remove logging & implementation attempt

* log out comment creation query output

* styling

* prettify comments + increase logging

* more prettifying of the text

* Revert "bork my url for sample output"

This reverts commit 0c6d17450e753cd407db69a8129bf0cbc5831b88.

* add data change to trigger GH action

* improve messages...

* change my URL so it appears in the message

* more formatting of comments

* move + rename commentPullRequest -> utils.communicateValationOutcome
This commit is contained in:
Hugo 2020-01-21 22:15:35 +00:00 committed by Andrew Luca
parent ce7b253da5
commit e20e0f5991
6 changed files with 261 additions and 14 deletions

View file

@ -27,3 +27,5 @@ jobs:
- name: Validate data.js - name: Validate data.js
run: node ./scripts/data-validate.js run: node ./scripts/data-validate.js
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

197
package-lock.json generated
View file

@ -17,6 +17,15 @@
"@actions/io": "^1.0.1" "@actions/io": "^1.0.1"
} }
}, },
"@actions/github": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-2.0.1.tgz",
"integrity": "sha512-C7dAsCkpPi1HxTzLldz+oY+9c5G+nnaK7xgk8KA83VVGlrGK7d603E3snUAFocWrqEu/uvdYD82ytggjcpYSQA==",
"requires": {
"@octokit/graphql": "^4.3.1",
"@octokit/rest": "^16.15.0"
}
},
"@actions/io": { "@actions/io": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz", "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz",
@ -1334,6 +1343,113 @@
"fastq": "^1.6.0" "fastq": "^1.6.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": {
"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"
}
},
"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=="
}
}
},
"@octokit/graphql": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.3.1.tgz",
"integrity": "sha512-hCdTjfvrK+ilU2keAdqNBWOk+gm1kai1ZcdjRfB30oA3/T6n53UVJb7w0L5cR3/rhU91xT3HSqCd+qbvH06yxA==",
"requires": {
"@octokit/request": "^5.3.0",
"@octokit/types": "^2.0.0",
"universal-user-agent": "^4.0.0"
}
},
"@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": {
"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"
}
},
"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=="
},
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
}
}
},
"@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.37.0",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.37.0.tgz",
"integrity": "sha512-qLPK9FOCK4iVpn6ghknNuv/gDDxXQG6+JBQvoCwWjQESyis9uemakjzN36nvvp8SCny7JuzHI2RV8ChbV5mYdQ==",
"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"
}
},
"@octokit/types": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.1.0.tgz",
"integrity": "sha512-n1GUYFgKm5glcy0E+U5jnqAFY2p04rnK4A0YhuM70C7Vm9Vyx+xYwd/WOTEr8nUJcbPSR/XL+/26+rirY6jJQA==",
"requires": {
"@types/node": ">= 8"
}
},
"@pieh/friendly-errors-webpack-plugin": { "@pieh/friendly-errors-webpack-plugin": {
"version": "1.7.0-chalk-2", "version": "1.7.0-chalk-2",
"resolved": "https://registry.npmjs.org/@pieh/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.0-chalk-2.tgz", "resolved": "https://registry.npmjs.org/@pieh/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.0-chalk-2.tgz",
@ -2094,6 +2210,11 @@
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
}, },
"atob-lite": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz",
"integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY="
},
"auto-bind": { "auto-bind": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-3.0.0.tgz", "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-3.0.0.tgz",
@ -2465,6 +2586,11 @@
"tweetnacl": "^0.14.3" "tweetnacl": "^0.14.3"
} }
}, },
"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=="
},
"better-assert": { "better-assert": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
@ -3041,6 +3167,11 @@
"electron-to-chromium": "^1.3.47" "electron-to-chromium": "^1.3.47"
} }
}, },
"btoa-lite": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz",
"integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc="
},
"buffer": { "buffer": {
"version": "4.9.2", "version": "4.9.2",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
@ -4828,6 +4959,11 @@
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
}, },
"deprecation": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
},
"des.js": { "des.js": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
@ -11092,6 +11228,11 @@
"resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
"integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM="
}, },
"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.map": { "lodash.map": {
"version": "4.6.0", "version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz",
@ -11107,6 +11248,11 @@
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4="
}, },
"lodash.set": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
"integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM="
},
"lodash.template": { "lodash.template": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
@ -11294,6 +11440,11 @@
"yallist": "^2.0.0" "yallist": "^2.0.0"
} }
}, },
"macos-release": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz",
"integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA=="
},
"make-dir": { "make-dir": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
@ -12259,6 +12410,11 @@
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
}, },
"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=="
},
"omggif": { "omggif": {
"version": "1.0.10", "version": "1.0.10",
"resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz",
@ -12403,6 +12559,15 @@
} }
} }
}, },
"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"
}
},
"os-tmpdir": { "os-tmpdir": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
@ -16569,6 +16734,14 @@
"crypto-random-string": "^1.0.0" "crypto-random-string": "^1.0.0"
} }
}, },
"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"
}
},
"universalify": { "universalify": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
@ -17520,6 +17693,30 @@
} }
} }
}, },
"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"
},
"dependencies": {
"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"
}
}
}
},
"with-open-file": { "with-open-file": {
"version": "0.1.7", "version": "0.1.7",
"resolved": "https://registry.npmjs.org/with-open-file/-/with-open-file-0.1.7.tgz", "resolved": "https://registry.npmjs.org/with-open-file/-/with-open-file-0.1.7.tgz",

View file

@ -14,6 +14,7 @@
"dependencies": { "dependencies": {
"@actions/core": "^1.2.1", "@actions/core": "^1.2.1",
"@actions/exec": "^1.0.3", "@actions/exec": "^1.0.3",
"@actions/github": "^2.0.1",
"@hapi/joi": "^17.0.2", "@hapi/joi": "^17.0.2",
"country-emoji": "^1.5.0", "country-emoji": "^1.5.0",
"esm": "^3.2.25", "esm": "^3.2.25",

View file

@ -1,8 +1,13 @@
const core = require('@actions/core'); const core = require('@actions/core');
const { getMasterData, Schema, getStatusCode } = require('./utils.js'); const {
getMasterData,
Schema,
getStatusCode,
communicateValidationOutcome,
} = require('./utils.js');
const srcData = require('../src/data.js'); const srcData = require('../src/data.js');
(async () => { async function main() {
// on master branch will be empty array // on master branch will be empty array
const masterDataUrls = (await getMasterData()).map(d => d.url); const masterDataUrls = (await getMasterData()).map(d => d.url);
// so here data will be an array with all users // so here data will be an array with all users
@ -18,25 +23,21 @@ const srcData = require('../src/data.js');
e.details.forEach(d => core.error(d.message)); e.details.forEach(d => core.error(d.message));
}); });
let failedUrlsCount = 0; const failedUrls = [];
for (const { url } of data) { for (const { url } of data) {
try { try {
const statusCode = await getStatusCode(url); const statusCode = await getStatusCode(url);
if (statusCode < 200 || statusCode >= 400) { if (statusCode < 200 || statusCode >= 400) {
core.error(`Ping to "${url}" failed with status: ${statusCode}`); core.error(`Ping to "${url}" failed with status: ${statusCode}`);
failedUrlsCount += 1; failedUrls.push(url);
} }
} catch (e) { } catch (e) {
core.error(`Ping to "${url}" failed with error: ${e}`); core.error(`Ping to "${url}" failed with error: ${e}`);
failedUrlsCount += 1; failedUrls.push(url);
} }
} }
if (failedUrlsCount) { await communicateValidationOutcome(errors, failedUrls, data);
core.error(`Action failed with ${failedUrlsCount} URL fetch failures`); }
}
if (errors.length || failedUrlsCount) { main();
core.setFailed('Action failed with errors, see logs');
}
})();

View file

@ -1,5 +1,6 @@
const exec = require('@actions/exec'); const exec = require('@actions/exec');
const core = require('@actions/core'); const core = require('@actions/core');
const github = require('@actions/github');
const Joi = require('@hapi/joi'); const Joi = require('@hapi/joi');
const http = require('http'); const http = require('http');
const https = require('https'); const https = require('https');
@ -84,3 +85,48 @@ module.exports.getStatusCode = function(url) {
.on('error', err => reject(err)); .on('error', err => reject(err));
}); });
}; };
// If there are errors, will fail the action & add a comment detailing the issues
// If there are no errors, will leave an "all-clear" comment with relevant URLs (to ease a potential manual check)
module.exports.communicateValidationOutcome = async function(
errors,
failedUrls,
changedData
) {
let comment = '';
if (errors.length || failedUrls.length) {
core.setFailed('Action failed with errors, see logs & comment');
comment += [
'🚨 We have detected the following issues, let us (contributors) know if you need support or clarifications:',
...errors.map(e => `- ${e.message}`),
...failedUrls.map(url => `- URL is invalid: ${url}`),
].join('\n');
} else {
comment += [
'✅ Automatic validation checks succeeded for:',
// Comment with the URLs of users that have changed
// for easy access, way easier than taking a screenshot
...changedData.map(({ name, url }) => `- ${name}, ${url}`),
].join('\n');
}
const { GITHUB_TOKEN } = process.env;
const { context } = github;
if (!GITHUB_TOKEN || !context.payload.pull_request) {
core.error(
'Cannot add a comment if GITHUB_TOKEN or context.payload.pull_request is not set'
);
core.info(`Comment contents:\n${comment}`);
return;
}
const pullRequestNumber = context.payload.pull_request.number;
const octokit = new github.GitHub(GITHUB_TOKEN);
await octokit.issues.createComment({
...context.repo,
issue_number: pullRequestNumber,
body: comment,
});
};

View file

@ -1854,8 +1854,8 @@ module.exports = [
{ {
name: 'Hugo Di Francesco', name: 'Hugo Di Francesco',
description: description:
"JavaScript developer, blogger at codewithhugo.com, author of 'Professional JavaScript' with Packt.", "JavaScript developer, blogger at codewithhugo.com, co-author of 'Professional JavaScript' with Packt.",
url: 'https://codewithhugo.com/uses/', url: 'https://codewithhugo.com/uses',
twitter: '@hugo__df', twitter: '@hugo__df',
emoji: '👓', emoji: '👓',
country: '🇬🇧', country: '🇬🇧',