Skip to content

Commit

Permalink
feat: change validate action to take into consideration only add… (we…
Browse files Browse the repository at this point in the history
…sbos#422)

`node scripts/data-valdiate.js` cand also be run locally

This also will fix the issue when some valid URL returned timeout
Because the action wanted to make all requests at the same time

`git restore` is available only from Git 2.23

https://github.blog/2019-08-16-highlights-from-git-2-23/#experimental-alternatives-for-git-checkout

Closes wesbos#382
  • Loading branch information
iamandrewluca authored Jan 18, 2020
1 parent 26a5bd5 commit 86b6b5d
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 89 deletions.
18 changes: 11 additions & 7 deletions .github/workflows/data-validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@ jobs:
- uses: actions/setup-node@v1
with:
node-version: 13.x
- name: Install validation libs
run: |
npm install -g @hapi/[email protected]
npm install -g @actions/[email protected]
npm link @hapi/joi
npm link @actions/core

- name: Cache/Restore node modules
uses: actions/cache@v1
with:
path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS
key: ${{ runner.os }}-npm-${{ hashFiles('./package-lock.json') }}

- name: Install Dependencies
run: npm install

- name: Validate data.js
run: node ./scripts/data-validate.js
run: node ./scripts/data-validate.js
19 changes: 16 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
]
},
"dependencies": {
"@actions/core": "^1.2.0",
"@actions/core": "^1.2.1",
"@actions/exec": "^1.0.3",
"@hapi/joi": "^17.0.2",
"country-emoji": "^1.5.0",
"esm": "^3.2.25",
Expand Down
110 changes: 32 additions & 78 deletions scripts/data-validate.js
Original file line number Diff line number Diff line change
@@ -1,88 +1,42 @@
import Joi from '@hapi/joi';
import core from '@actions/core';
import * as http from 'http';
import * as https from 'https';
import data from '../src/data.js';
import flags from './flags.js';
import { getMasterData, Schema, getStatusCode } from './utils.js';
import srcData from '../src/data.js';

if (process.env.CI !== 'true') {
core.error = console.error;
core.setFailed = console.error;
}

const schema = Joi.object({
name: Joi.string().required(),
description: Joi.string().required(),
url: Joi.string()
.uri()
.required()
.pattern(/(use|uses|using|setup|environment|^https:\/\/gist.github.com\/)/),
country: Joi.string()
.valid(...flags)
.required(),
twitter: Joi.string().pattern(new RegExp(/^@?(\w){1,15}$/)),
emoji: Joi.string().allow(''),
computer: Joi.string().valid('apple', 'windows', 'linux'),
phone: Joi.string().valid('iphone', 'android'),
tags: Joi.array().items(Joi.string()),
});

const errors = data
.map(person => schema.validate(person))
.filter(v => v.error)
.map(v => v.error);

errors.forEach(e => {
core.error(e._original.name);
e.details.forEach(d => core.error(d.message));
});

if (errors.length) {
core.setFailed('Action failed with validation errors, see logs');
}
const REQUEST_TIMEOUT = 10000;

function getStatusCode(url) {
const client = url.startsWith('https') ? https : http;
return new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('Request timed out')), REQUEST_TIMEOUT);
client
.get(url, res => {
resolve(res.statusCode);
})
.on('error', err => {
reject(err);
});
(async () => {
// 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
const data = srcData.filter(d => !masterDataUrls.includes(d.url));

const errors = data
.map(person => Schema.validate(person))
.filter(v => v.error)
.map(v => v.error);

errors.forEach(e => {
core.error(e._original.name || e._original.url);
e.details.forEach(d => core.error(d.message));
});
}

async function isWorkingUrl(url) {
try {
const statusCode = await getStatusCode(url);
if (statusCode < 200 || statusCode >= 400) {
core.error(`Ping to "${url}" failed with status: ${statusCode}`);
return false;
let failedUrlsCount = 0;
for await (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;
}
} catch (e) {
core.error(`Ping to "${url}" failed with error: ${e}`);
failedUrlsCount += 1;
}
return true;
} catch (e) {
core.error(`Ping to "${url}" failed with error: ${e}`);
return false;
}
}

(async () => {
// TODO: we might need to batch these in sets instead of requesting 100+ URLs
// at the same time
const areWorkingUrls = await Promise.all(
data.map(p => p.url).map(url => isWorkingUrl(url))
);
const failingUrls = areWorkingUrls.filter(a => !a);
if (failingUrls.length > 0) {
core.setFailed(
`Action failed with ${failingUrls.length} URL fetch failures, see logs`
);
if (failedUrlsCount) {
core.error(`Action failed with ${failedUrlsCount} URL fetch failures`);
}
if (process.env.CI !== 'true') {
process.exit(failingUrls.length > 0 ? 1 : 0)

if (errors.length || failedUrlsCount) {
core.setFailed('Action failed with errors, see logs');
}
})();
5 changes: 5 additions & 0 deletions scripts/masterData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* this is a stub file, do not edit it
* see `scripts/utils.js` -> `getMasterData`
*/
export default [];
85 changes: 85 additions & 0 deletions scripts/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import exec from '@actions/exec';
import core from '@actions/core';
import Joi from '@hapi/joi';
import * as http from 'http';
import * as https from 'https';
import flags from './flags.js';

async function getCurrentBranchName() {
let myOutput = '';
let myError = '';

const options = {
silent: true,
listeners: {
stdout: data => (myOutput += data.toString()),
stderr: data => (myError += data.toString()),
},
};

await exec.exec('git rev-parse --abbrev-ref HEAD', [], options);
return myOutput.trim();
}

/** on master branch will return an empty array */
export async function getMasterData() {
const options = { silent: true };
const curentBranchName = getCurrentBranchName();
// when on a branch/PR different from master
// will populate scripts/masterData.js with src/data.js from master
if (curentBranchName !== 'master') {
core.info('Executing action on branch different from master');
await exec.exec('mv src/data.js src/tmpData.js', [], options);
await exec.exec('git fetch origin master', [], options);
await exec.exec('git restore --source=FETCH_HEAD src/data.js', [], options);
await exec.exec('mv src/data.js scripts/masterData.js', [], options);
await exec.exec('mv src/tmpData.js src/data.js', [], options);
} else {
core.info('Executing action on master branch');
}

const masterData = await import('./masterData.js').then(m => m.default);

// restore `scripts/masterData.js` after was loaded
if (curentBranchName !== 'master') {
await exec.exec('git restore scripts/masterData.js', [], options);
}

return masterData;
}

export const Schema = Joi.object({
name: Joi.string().required(),
description: Joi.string().required(),
url: Joi.string()
.uri()
.required()
.pattern(/(use|uses|using|setup|environment|^https:\/\/gist.github.com\/)/),
country: Joi.string()
.valid(...flags)
.required(),
twitter: Joi.string().pattern(new RegExp(/^@?(\w){1,15}$/)),
emoji: Joi.string().allow(''),
computer: Joi.string().valid('apple', 'windows', 'linux'),
phone: Joi.string().valid('iphone', 'android'),
tags: Joi.array().items(Joi.string()),
});

export function getStatusCode(url) {
const client = url.startsWith('https') ? https : http;
return new Promise((resolve, reject) => {
const REQUEST_TIMEOUT = 10000;
const timeoutId = setTimeout(
reject,
REQUEST_TIMEOUT,
new Error('Request timed out')
);

client
.get(url, res => {
clearTimeout(timeoutId);
resolve(res.statusCode);
})
.on('error', err => reject(err));
});
}

0 comments on commit 86b6b5d

Please sign in to comment.