diff --git a/.github/workflows/data-validate.yml b/.github/workflows/data-validate.yml index bbfbd1e8..9b312fcd 100644 --- a/.github/workflows/data-validate.yml +++ b/.github/workflows/data-validate.yml @@ -27,3 +27,5 @@ jobs: - name: Validate data.js run: node ./scripts/data-validate.js + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/package-lock.json b/package-lock.json index 3ae17837..00544d33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,15 @@ "@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": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz", @@ -1334,6 +1343,113 @@ "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": { "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", @@ -2094,6 +2210,11 @@ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "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": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-3.0.0.tgz", @@ -2465,6 +2586,11 @@ "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": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", @@ -3041,6 +3167,11 @@ "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": { "version": "4.9.2", "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", "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, "des.js": { "version": "1.0.1", "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", "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": { "version": "4.6.0", "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", "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": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", @@ -11294,6 +11440,11 @@ "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": { "version": "2.1.0", "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", "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": { "version": "1.0.10", "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": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -16569,6 +16734,14 @@ "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": { "version": "0.1.2", "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": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/with-open-file/-/with-open-file-0.1.7.tgz", diff --git a/package.json b/package.json index 0077a3b8..ae594981 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "dependencies": { "@actions/core": "^1.2.1", "@actions/exec": "^1.0.3", + "@actions/github": "^2.0.1", "@hapi/joi": "^17.0.2", "country-emoji": "^1.5.0", "esm": "^3.2.25", diff --git a/scripts/data-validate.js b/scripts/data-validate.js index 960c6020..01f4ff15 100644 --- a/scripts/data-validate.js +++ b/scripts/data-validate.js @@ -1,8 +1,13 @@ 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'); -(async () => { +async function main() { // on master branch will be empty array const masterDataUrls = (await getMasterData()).map(d => d.url); // 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)); }); - let failedUrlsCount = 0; + const failedUrls = []; for (const { url } of data) { try { const statusCode = await getStatusCode(url); if (statusCode < 200 || statusCode >= 400) { core.error(`Ping to "${url}" failed with status: ${statusCode}`); - failedUrlsCount += 1; + failedUrls.push(url); } } catch (e) { core.error(`Ping to "${url}" failed with error: ${e}`); - failedUrlsCount += 1; + failedUrls.push(url); } } - if (failedUrlsCount) { - core.error(`Action failed with ${failedUrlsCount} URL fetch failures`); - } + await communicateValidationOutcome(errors, failedUrls, data); +} - if (errors.length || failedUrlsCount) { - core.setFailed('Action failed with errors, see logs'); - } -})(); +main(); diff --git a/scripts/utils.js b/scripts/utils.js index fd5e7ea2..0bbda125 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -1,5 +1,6 @@ const exec = require('@actions/exec'); const core = require('@actions/core'); +const github = require('@actions/github'); const Joi = require('@hapi/joi'); const http = require('http'); const https = require('https'); @@ -84,3 +85,48 @@ module.exports.getStatusCode = function(url) { .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, + }); +}; diff --git a/src/data.js b/src/data.js index 0c94a9eb..5b33409f 100644 --- a/src/data.js +++ b/src/data.js @@ -1854,8 +1854,8 @@ module.exports = [ { name: 'Hugo Di Francesco', description: - "JavaScript developer, blogger at codewithhugo.com, author of 'Professional JavaScript' with Packt.", - url: 'https://codewithhugo.com/uses/', + "JavaScript developer, blogger at codewithhugo.com, co-author of 'Professional JavaScript' with Packt.", + url: 'https://codewithhugo.com/uses', twitter: '@hugo__df', emoji: '👓', country: '🇬🇧',