[internal] Set up Danger build on PR (#3243)

* Return all error messages from checks (to be used by Danger).
* Add Danger dev dep.
* Fix danger.yml + dangerfile.ts.
* Fix root files.
* Finetune Danger message.

Co-authored-by: Catenocrypt <catenocrypt@users.noreply.github.com>
This commit is contained in:
Adam R 2020-08-12 10:03:28 +02:00 committed by GitHub
parent 0c013b91ac
commit 197613e61c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1067 additions and 156 deletions

View File

@ -1,17 +1,23 @@
name: Danger name: Danger
on: on:
push: pull_request:
branches: [ master ] branches: [ 'master' ]
workflow_dispatch:
jobs: jobs:
danger: danger:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install gems - name: Use Node.js 10.x
run: sudo gem install bundler --no-document uses: actions/setup-node@v1
- name: Bundle update with:
run: bundle update --bundler node-version: 10.x
- name: Run Danger - name: install yarn
run: danger --verbose run: npm install -g yarn
- name: yarn install
run: |
yarn install --frozen-lockfile
- name: Danger
run: yarn danger ci
env: env:
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,115 +0,0 @@
require 'find'
require 'image_size'
require 'json-schema'
blockchains_folder = './blockchains'
allowed_extensions = ['png', 'json']
allowed_file_names = ['info', 'list', 'logo', 'whitelist', 'blacklist']
maxAssetLogoSizeInKilobyte = 100
minLogoWidth = 64
minLogoHeight = 64
maxLogoWidth = 512
maxLogoHeight = 512
# Failures
# Do not allow files in this directory
Dir.foreach(blockchains_folder) \
.map { |x| File.expand_path("#{blockchains_folder}/#{x}") } \
.select { |x| File.file?(x) }
.map { |x|
fail("Not allowed to have files inside blockchains folder itself. You have to add them inside specific blockchain folder as blockchain/ethereum or blockchain/binance for file: " + x)
}
Find.find(blockchains_folder) do |file|
file_extension = File.extname(file).delete('.')
file_name = File.basename(file, File.extname(file))
# Skip if directory
if File.directory?(file)
next
end
if !allowed_extensions.include? file_extension
fail("Extension not allowed for file: " + file)
end
if !allowed_file_names.include? file_name
fail("Filename not allowed for file: " + file)
end
# Validate JSON content
if file_extension == 'json'
value = nil
begin
value = JSON.parse(File.open(file).read)
rescue JSON::ParserError, TypeError => e
fail("Wrong JSON content in file: " + file)
end
# Validate info.json
if file_name == 'info'
schema = {
"type": "object",
"required": ["name", "website", "short_description", "explorer"],
"properties": {
"name": {"type": "string"},
"website": {"type": "string"},
"short_description": {"type": "string"},
"explorer": {"type": "string"}
}
}
errors = JSON::Validator.fully_validate(schema, value)
errors.each { |error| message("#{error} in file #{file}") }
end
# Validate list.json for validators
if file_name == 'list'
schema = {
"type": "array",
"items": {
"properties": {
"id": { "type": "string" },
"name": { "type": "string" },
"description": { "type": "string" },
"website": { "type": "string" }
},
"required": ["id", "name", "description", "website"]
}
}
errors = JSON::Validator.fully_validate(schema, value)
errors.each { |error| message("#{error} in file #{file}") }
end
# Validate whitelist, blacklist files
if file_name == 'whitelist' || file_name == 'blacklist'
schema = {
"type": "array",
"items": {
"type": "string"
}
}
errors = JSON::Validator.fully_validate(schema, value)
errors.each { |error| message("#{error} in file #{file}") }
end
end
# Validate images
if file_extension == 'png'
image_size = ImageSize.path(file)
if image_size.width > maxLogoWidth || image_size.height > maxLogoHeight
fail("Image width or height is higher than 512px for file: " + file)
end
if image_size.format == 'png'
fail("Image should be PNG for file: " + file)
end
# Make sure file size only 100kb
if File.size(file).to_f / 1024 > maxAssetLogoSizeInKilobyte
fail("Image should less than 100kb for file: " + file)
end
end
end

9
dangerfile.ts Normal file
View File

@ -0,0 +1,9 @@
import { fail, markdown } from "danger";
import { sanityCheckAll } from "./script/action/update-all";
sanityCheckAll().then(errors => {
if (errors.length > 0) {
errors.forEach(err => fail(err));
markdown("Please fix the errors above. Files can be replaced/renamed in this pull request (using command-line, or GitHub Desktop). Alternatively, you may close this pull request and open a new one.");
}
});

1010
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -39,6 +39,7 @@
"bip44-constants": "^8.0.30", "bip44-constants": "^8.0.30",
"bluebird": "^3.7.2", "bluebird": "^3.7.2",
"chalk": "^4.1.0", "chalk": "^4.1.0",
"danger": "^10.3.0",
"ethereum-checksum-address": "0.0.5", "ethereum-checksum-address": "0.0.5",
"eztz-lib": "^0.1.2", "eztz-lib": "^0.1.2",
"image-size": "^0.8.3", "image-size": "^0.8.3",

View File

@ -34,38 +34,38 @@ const actionList: ActionInterface[] = [
new Coinmarketcap() new Coinmarketcap()
]; ];
async function checkStepList(steps: CheckStepInterface[]): Promise<number> { async function checkStepList(steps: CheckStepInterface[]): Promise<string[]> {
var returnCode = 0; var errors: string[] = [];
await bluebird.each(steps, async (step) => { await bluebird.each(steps, async (step) => {
try { try {
//console.log(` Running check step '${step.getName()}'...`); //console.log(` Running check step '${step.getName()}'...`);
const error = await step.check(); const error = await step.check();
if (error && error.length > 0) { if (error && error.length > 0) {
console.log(`- ${chalk.red('X')} '${step.getName()}': '${error}'`); console.log(`- ${chalk.red('X')} '${step.getName()}': '${error}'`);
returnCode = 1; errors.push(`${step.getName()}: ${error}`);
} else { } else {
console.log(`- ${chalk.green('✓')} '${step.getName()}' OK`); console.log(`- ${chalk.green('✓')} '${step.getName()}' OK`);
} }
} catch (error) { } catch (error) {
console.log(`- ${chalk.red('X')} '${step.getName()}': Caught error: ${error.message}`); console.log(`- ${chalk.red('X')} '${step.getName()}': Caught error: ${error.message}`);
returnCode = 2; errors.push(`${step.getName()}: Exception: ${error.message}`);
} }
}); });
return returnCode; return errors;
} }
async function sanityCheckByActionList(actions: ActionInterface[]): Promise<number> { async function sanityCheckByActionList(actions: ActionInterface[]): Promise<string[]> {
console.log("Running sanity checks..."); console.log("Running sanity checks...");
var returnCode = 0; var errors: string[] = [];
await bluebird.each(actions, async (action) => { await bluebird.each(actions, async (action) => {
try { try {
if (action.getSanityChecks) { if (action.getSanityChecks) {
const steps = action.getSanityChecks(); const steps = action.getSanityChecks();
if (steps && steps.length > 0) { if (steps && steps.length > 0) {
console.log(` Action '${action.getName()}' has ${steps.length} check steps`); console.log(` Action '${action.getName()}' has ${steps.length} check steps`);
const ret1 = await checkStepList(steps); const errors1 = await checkStepList(steps);
if (ret1 != 0) { if (errors1.length > 0) {
returnCode = ret1; errors1.forEach(e => errors.push(e));
} else { } else {
console.log(`- ${chalk.green('✓')} Action '${action.getName()}' OK, all ${steps.length} steps`); console.log(`- ${chalk.green('✓')} Action '${action.getName()}' OK, all ${steps.length} steps`);
} }
@ -73,25 +73,25 @@ async function sanityCheckByActionList(actions: ActionInterface[]): Promise<numb
} }
} catch (error) { } catch (error) {
console.log(`- ${chalk.red('X')} '${action.getName()}' Caught error: ${error.message}`); console.log(`- ${chalk.red('X')} '${action.getName()}' Caught error: ${error.message}`);
returnCode = 3; errors.push(`${action.getName()}: Exception: ${error.message}`);
} }
}); });
console.log(`All sanity checks done, returnCode ${returnCode}`); console.log(`All sanity checks done, found ${errors.length} errors`);
return returnCode; return errors;
} }
async function consistencyCheckByActionList(actions: ActionInterface[]): Promise<number> { async function consistencyCheckByActionList(actions: ActionInterface[]): Promise<string[]> {
console.log("Running consistency checks..."); console.log("Running consistency checks...");
var returnCode = 0; var errors: string[] = [];
await bluebird.each(actions, async (action) => { await bluebird.each(actions, async (action) => {
try { try {
if (action.getConsistencyChecks) { if (action.getConsistencyChecks) {
const steps = action.getConsistencyChecks(); const steps = action.getConsistencyChecks();
if (steps && steps.length > 0) { if (steps && steps.length > 0) {
console.log(` Action '${action.getName()}' has ${steps.length} check steps`); console.log(` Action '${action.getName()}' has ${steps.length} check steps`);
const ret1 = await checkStepList(steps); const errors1 = await checkStepList(steps);
if (ret1 != 0) { if (errors1.length > 0) {
returnCode = ret1; errors1.forEach(e => errors.push(e));
} else { } else {
console.log(`- ${chalk.green('✓')} Action '${action.getName()}' OK, all ${steps.length} steps`); console.log(`- ${chalk.green('✓')} Action '${action.getName()}' OK, all ${steps.length} steps`);
} }
@ -99,11 +99,11 @@ async function consistencyCheckByActionList(actions: ActionInterface[]): Promise
} }
} catch (error) { } catch (error) {
console.log(`- ${chalk.red('X')} '${action.getName()}' Caught error: ${error.message}`); console.log(`- ${chalk.red('X')} '${action.getName()}' Caught error: ${error.message}`);
returnCode = 3; errors.push(`${action.getName()}: Exception: ${error.message}`);
} }
}); });
console.log(`All consistency checks done, returnCode ${returnCode}`); console.log(`All consistency checks done, found ${errors.length} errors`);
return returnCode; return errors;
} }
async function sanityFixByList(actions: ActionInterface[]) { async function sanityFixByList(actions: ActionInterface[]) {
@ -151,11 +151,11 @@ async function updateByList(actions: ActionInterface[]) {
console.log("All updates done."); console.log("All updates done.");
} }
export async function sanityCheckAll(): Promise<number> { export async function sanityCheckAll(): Promise<string[]> {
return await sanityCheckByActionList(actionList); return await sanityCheckByActionList(actionList);
} }
export async function consistencyCheckAll(): Promise<number> { export async function consistencyCheckAll(): Promise<string[]> {
return await consistencyCheckByActionList(actionList); return await consistencyCheckByActionList(actionList);
} }

View File

@ -4,7 +4,7 @@
"image_min_logo_width": 64, "image_min_logo_width": 64,
"image_min_logo_height": 64, "image_min_logo_height": 64,
"image_logo_size_kb": 100, "image_logo_size_kb": 100,
"folders_rootdir_allowed_files": [".github", "blockchains", "dapps", "media", "node_modules", "script-old", "script", "test", ".gitignore", "azure-pipelines.yml", "jest.config.js", "LICENSE", "package-lock.json", "package.json", "README.md", ".git", "pricing", "Dangerfile", "Gemfile", "Gemfile.lock"], "folders_rootdir_allowed_files": [".github", "blockchains", "dapps", "media", "node_modules", "script-old", "script", "test", ".gitignore", "azure-pipelines.yml", "jest.config.js", "LICENSE", "package-lock.json", "package.json", "README.md", ".git", "pricing", "dangerfile.ts", "Gemfile", "Gemfile.lock"],
"binance_url_tokens2": "https://dex-atlantic.binance.org/api/v1/tokens?limit=1000", "binance_url_tokens2": "https://dex-atlantic.binance.org/api/v1/tokens?limit=1000",
"binance_url_tokens8": "https://dex-atlantic.binance.org/api/v1/mini/tokens?limit=1000", "binance_url_tokens8": "https://dex-atlantic.binance.org/api/v1/mini/tokens?limit=1000",
"binance_url_token_assets": "https://explorer.binance.org/api/v1/assets?page=1&rows=1000" "binance_url_token_assets": "https://explorer.binance.org/api/v1/assets?page=1&rows=1000"

View File

@ -2,8 +2,8 @@ import { sanityCheckAll } from "../action/update-all";
export async function main() { export async function main() {
try { try {
const returnCode = await sanityCheckAll(); const errors = await sanityCheckAll();
process.exit(returnCode); process.exit(errors.length);
} catch(err) { } catch(err) {
console.error(err); console.error(err);
process.exit(1); process.exit(1);

View File

@ -4,9 +4,9 @@ export async function main() {
var returnCode: number = 0; var returnCode: number = 0;
try { try {
const ret1 = await sanityCheckAll(); const errors1 = await sanityCheckAll();
if (ret1 != 0) { if (errors1.length > 0) {
returnCode = ret1; returnCode = errors1.length;
} }
} catch(err) { } catch(err) {
console.error(err); console.error(err);
@ -14,9 +14,9 @@ export async function main() {
} }
try { try {
const ret1 = await consistencyCheckAll(); const errors1 = await consistencyCheckAll();
if (ret1 != 0) { if (errors1.length > 0) {
returnCode = ret1; returnCode = errors1.length;
} }
} catch(err) { } catch(err) {
console.error(err); console.error(err);