This commit is contained in:
Jack 2022-02-18 18:25:33 +00:00 committed by GitHub
parent a478128bc5
commit 432fedb3c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 283 additions and 216 deletions

View file

@ -11,11 +11,24 @@ jobs:
- uses: actions/checkout@v2
- uses: dorny/paths-filter@v2
id: changes
id: filter
with:
filters: |
json:
any-json:
- 'frontend/**/*.json'
language-json:
- 'frontend/static/languages/*.json'
quotes-json:
- 'frontend/static/quotes/*.json'
other-json:
- 'frontend/funbox/*.json'
- 'frontend/fonts/*.json'
- 'frontend/themes/*.json'
- 'frontend/challenges/*.json'
any-tsscss:
- 'frontend/**/*.scss'
- 'frontend/**/*.js'
- 'frontend/**/*.ts'
scss:
- 'frontend/**/*.scss'
ts:
@ -45,14 +58,30 @@ jobs:
# - name: Run webpack
# run: npm run build:live
- name: Lint and validate JSON
if: steps.filter.outputs.json == 'true'
run: npm run pr-check-json
- name: Lint JSON
if: steps.filter.outputs.any-json == 'true'
run: npm run pr-check-lint-json
- name: Lint and compile SCSS
- name: Validate languages JSON
if: steps.filter.outputs.language-json == 'true'
run: npm run pr-check-language-json
- name: Validate quotes JSON
if: steps.filter.outputs.quotes-json == 'true'
run: npm run pr-check-quote-json
- name: Validate other JSON
if: steps.filter.outputs.other-json == 'true'
run: npm run pr-check-other-json
- name: Lint
if: steps.filter.outputs.any-tsscss == 'true'
run: npm run pr-check-lint-json
- name: Compile SCSS
if: steps.filter.outputs.scss == 'true'
run: npm run pr-check-scss
- name: Lint source code and run webpack
if: steps.filter.outputs.scss == 'true'
- name: Run webpack
if: steps.filter.outputs.ts == 'true'
run: npm run pr-check-ts

View file

@ -36,7 +36,7 @@ task("lint-json", function () {
});
task("validate-json-schema", function () {
return JSONValidation.validate();
return JSONValidation.validateAll();
});
task("copy-src-contents", function () {
@ -150,8 +150,26 @@ task("build", series("clean", "compile"));
task("build-production", series("clean", "compile-production"));
task("pr-check-json", series("lint-json", "validate-json-schema"));
//PR CHECK
task("pr-check-scss", series("lint", "sass"));
task("validate-quote-json-schema", function () {
return JSONValidation.validateQuotes();
});
task("pr-check-ts", series("lint", "webpack-production"));
task("validate-language-json-schema", function () {
return JSONValidation.validateLanguages();
});
task("validate-other-json-schema", function () {
return JSONValidation.validateOthers();
});
task("pr-check-lint-json", series("lint-json"));
task("pr-check-quote-json", series("validate-quote-json-schema"));
task("pr-check-language-json", series("validate-language-json-schema"));
task("pr-check-other-json", series("validate-other-json-schema"));
task("pr-check-lint", series("lint"));
task("pr-check-scss", series("sass"));
task("pr-check-ts", series("webpack-production"));

View file

@ -6,7 +6,7 @@ function escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function validate() {
function validateOthers() {
return new Promise((resolve, reject) => {
//fonts
const fontsData = JSON.parse(
@ -90,207 +90,6 @@ function validate() {
return reject(new Error(themesValidator.errors));
}
//languages
const languagesData = JSON.parse(
fs.readFileSync("./static/languages/_list.json", {
encoding: "utf8",
flag: "r",
})
);
const languagesSchema = {
type: "array",
items: {
type: "string",
},
};
const languagesValidator = JSONValidator.validate(
languagesData,
languagesSchema
);
if (languagesValidator.valid) {
console.log("Languages list JSON schema is \u001b[32mvalid\u001b[0m");
} else {
console.log("Languages list JSON schema is \u001b[31minvalid\u001b[0m");
return reject(new Error(languagesValidator.errors));
}
//languages group
const languagesGroupData = JSON.parse(
fs.readFileSync("./static/languages/_groups.json", {
encoding: "utf8",
flag: "r",
})
);
const languagesGroupSchema = {
type: "array",
items: {
type: "object",
properties: {
name: { type: "string" },
languages: {
type: "array",
items: {
type: "string",
},
},
},
required: ["name", "languages"],
},
};
const languagesGroupValidator = JSONValidator.validate(
languagesGroupData,
languagesGroupSchema
);
if (languagesGroupValidator.valid) {
console.log("Languages groups JSON schema is \u001b[32mvalid\u001b[0m");
} else {
console.log("Languages groups JSON schema is \u001b[31minvalid\u001b[0m");
return reject(new Error(languagesGroupValidator.errors));
}
//language files
const languageFileSchema = {
type: "object",
properties: {
name: { type: "string" },
leftToRight: { type: "boolean" },
noLazyMode: { type: "boolean" },
bcp47: { type: "string" },
words: {
type: "array",
items: { type: "string", minLength: 1 },
},
accents: {
type: "array",
items: {
type: "array",
items: { type: "string", minLength: 1 },
minItems: 2,
maxItems: 2,
},
},
},
required: ["name", "leftToRight", "words"],
};
let languageFilesAllGood = true;
let languageFilesErrors;
languagesData.forEach((language) => {
const languageFileData = JSON.parse(
fs.readFileSync(`./static/languages/${language}.json`, {
encoding: "utf8",
flag: "r",
})
);
languageFileSchema.properties.name.pattern =
"^" + escapeRegExp(language) + "$";
const languageFileValidator = JSONValidator.validate(
languageFileData,
languageFileSchema
);
if (!languageFileValidator.valid) {
languageFilesAllGood = false;
languageFilesErrors = languageFileValidator.errors;
}
});
if (languageFilesAllGood) {
console.log(
`Language word list JSON schemas are \u001b[32mvalid\u001b[0m`
);
} else {
console.log(
`Language word list JSON schemas are \u001b[31minvalid\u001b[0m`
);
return reject(new Error(languageFilesErrors));
}
//quotes
const quoteSchema = {
type: "object",
properties: {
language: { type: "string" },
groups: {
type: "array",
items: {
type: "array",
items: {
type: "number",
},
minItems: 2,
maxItems: 2,
},
},
quotes: {
type: "array",
items: {
type: "object",
properties: {
text: { type: "string" },
source: { type: "string" },
length: { type: "number" },
id: { type: "number" },
},
required: ["text", "source", "length", "id"],
},
},
},
required: ["language", "groups", "quotes"],
};
const quoteIdsSchema = {
type: "array",
items: {
type: "number",
},
uniqueItems: true,
};
let quoteFilesAllGood = true;
let quoteFilesErrors;
let quoteIdsAllGood = true;
let quoteIdsErrors;
const quotesFiles = fs.readdirSync("./static/quotes/");
quotesFiles.forEach((quotefilename) => {
quotefilename = quotefilename.split(".")[0];
const quoteData = JSON.parse(
fs.readFileSync(`./static/quotes/${quotefilename}.json`, {
encoding: "utf8",
flag: "r",
})
);
quoteSchema.properties.language.pattern =
"^" + escapeRegExp(quotefilename) + "$";
const quoteValidator = JSONValidator.validate(quoteData, quoteSchema);
if (!quoteValidator.valid) {
console.log(
`Quote ${quotefilename} JSON schema is \u001b[31minvalid\u001b[0m`
);
quoteFilesAllGood = false;
quoteFilesErrors = quoteValidator.errors;
}
const quoteIds = quoteData.quotes.map((quote) => quote.id);
const quoteIdsValidator = JSONValidator.validate(
quoteIds,
quoteIdsSchema
);
if (!quoteIdsValidator.valid) {
console.log(
`Quote ${quotefilename} IDs are \u001b[31mnot unique\u001b[0m`
);
quoteIdsAllGood = false;
quoteIdsErrors = quoteIdsValidator.errors;
}
});
if (quoteFilesAllGood) {
console.log(`Quote file JSON schemas are \u001b[32mvalid\u001b[0m`);
} else {
console.log(`Quote file JSON schemas are \u001b[31minvalid\u001b[0m`);
return reject(new Error(quoteFilesErrors));
}
if (quoteIdsAllGood) {
console.log(`Quote IDs are \u001b[32munique\u001b[0m`);
} else {
console.log(`Quote IDs are \u001b[31mnot unique\u001b[0m`);
return reject(new Error(quoteIdsErrors));
}
//challenges
const challengesSchema = {
type: "array",
@ -500,6 +299,224 @@ function validate() {
});
}
function validateQuotes() {
return new Promise((resolve, reject) => {
//quotes
const quoteSchema = {
type: "object",
properties: {
language: { type: "string" },
groups: {
type: "array",
items: {
type: "array",
items: {
type: "number",
},
minItems: 2,
maxItems: 2,
},
},
quotes: {
type: "array",
items: {
type: "object",
properties: {
text: { type: "string" },
source: { type: "string" },
length: { type: "number" },
id: { type: "number" },
},
required: ["text", "source", "length", "id"],
},
},
},
required: ["language", "groups", "quotes"],
};
const quoteIdsSchema = {
type: "array",
items: {
type: "number",
},
uniqueItems: true,
};
let quoteFilesAllGood = true;
let quoteFilesErrors;
let quoteIdsAllGood = true;
let quoteIdsErrors;
const quotesFiles = fs.readdirSync("./static/quotes/");
quotesFiles.forEach((quotefilename) => {
quotefilename = quotefilename.split(".")[0];
const quoteData = JSON.parse(
fs.readFileSync(`./static/quotes/${quotefilename}.json`, {
encoding: "utf8",
flag: "r",
})
);
quoteSchema.properties.language.pattern =
"^" + escapeRegExp(quotefilename) + "$";
const quoteValidator = JSONValidator.validate(quoteData, quoteSchema);
if (!quoteValidator.valid) {
console.log(
`Quote ${quotefilename} JSON schema is \u001b[31minvalid\u001b[0m`
);
quoteFilesAllGood = false;
quoteFilesErrors = quoteValidator.errors;
}
const quoteIds = quoteData.quotes.map((quote) => quote.id);
const quoteIdsValidator = JSONValidator.validate(
quoteIds,
quoteIdsSchema
);
if (!quoteIdsValidator.valid) {
console.log(
`Quote ${quotefilename} IDs are \u001b[31mnot unique\u001b[0m`
);
quoteIdsAllGood = false;
quoteIdsErrors = quoteIdsValidator.errors;
}
});
if (quoteFilesAllGood) {
console.log(`Quote file JSON schemas are \u001b[32mvalid\u001b[0m`);
} else {
console.log(`Quote file JSON schemas are \u001b[31minvalid\u001b[0m`);
return reject(new Error(quoteFilesErrors));
}
if (quoteIdsAllGood) {
console.log(`Quote IDs are \u001b[32munique\u001b[0m`);
} else {
console.log(`Quote IDs are \u001b[31mnot unique\u001b[0m`);
return reject(new Error(quoteIdsErrors));
}
resolve();
});
}
function validateLanguages() {
return new Promise((resolve, reject) => {
//languages
const languagesData = JSON.parse(
fs.readFileSync("./static/languages/_list.json", {
encoding: "utf8",
flag: "r",
})
);
const languagesSchema = {
type: "array",
items: {
type: "string",
},
};
const languagesValidator = JSONValidator.validate(
languagesData,
languagesSchema
);
if (languagesValidator.valid) {
console.log("Languages list JSON schema is \u001b[32mvalid\u001b[0m");
} else {
console.log("Languages list JSON schema is \u001b[31minvalid\u001b[0m");
return reject(new Error(languagesValidator.errors));
}
//languages group
const languagesGroupData = JSON.parse(
fs.readFileSync("./static/languages/_groups.json", {
encoding: "utf8",
flag: "r",
})
);
const languagesGroupSchema = {
type: "array",
items: {
type: "object",
properties: {
name: { type: "string" },
languages: {
type: "array",
items: {
type: "string",
},
},
},
required: ["name", "languages"],
},
};
const languagesGroupValidator = JSONValidator.validate(
languagesGroupData,
languagesGroupSchema
);
if (languagesGroupValidator.valid) {
console.log("Languages groups JSON schema is \u001b[32mvalid\u001b[0m");
} else {
console.log("Languages groups JSON schema is \u001b[31minvalid\u001b[0m");
return reject(new Error(languagesGroupValidator.errors));
}
//language files
const languageFileSchema = {
type: "object",
properties: {
name: { type: "string" },
leftToRight: { type: "boolean" },
noLazyMode: { type: "boolean" },
bcp47: { type: "string" },
words: {
type: "array",
items: { type: "string", minLength: 1 },
},
accents: {
type: "array",
items: {
type: "array",
items: { type: "string", minLength: 1 },
minItems: 2,
maxItems: 2,
},
},
},
required: ["name", "leftToRight", "words"],
};
let languageFilesAllGood = true;
let languageFilesErrors;
languagesData.forEach((language) => {
const languageFileData = JSON.parse(
fs.readFileSync(`./static/languages/${language}.json`, {
encoding: "utf8",
flag: "r",
})
);
languageFileSchema.properties.name.pattern =
"^" + escapeRegExp(language) + "$";
const languageFileValidator = JSONValidator.validate(
languageFileData,
languageFileSchema
);
if (!languageFileValidator.valid) {
languageFilesAllGood = false;
languageFilesErrors = languageFileValidator.errors;
}
});
if (languageFilesAllGood) {
console.log(
`Language word list JSON schemas are \u001b[32mvalid\u001b[0m`
);
} else {
console.log(
`Language word list JSON schemas are \u001b[31minvalid\u001b[0m`
);
return reject(new Error(languageFilesErrors));
}
resolve();
});
}
function validateAll() {
return Promise.all([validateOthers(), validateLanguages(), validateQuotes()]);
}
module.exports = {
validate,
validateAll,
validateOthers,
validateLanguages,
validateQuotes,
};

View file

@ -12,7 +12,10 @@
"lint": "./node_modules/.bin/eslint './backend/**/*.js' './frontend/src/scripts/**/*.js'",
"build:live": "cd ./frontend && npm run build:live",
"pretty": "prettier --check './backend/**/*.js' './frontend/src/**/*.{js,scss}' './frontend/static/**/*.{json,html}'",
"pr-check-json": "cd frontend && npx gulp pr-check-json",
"pr-check-lint-json": "cd frontend && npx gulp pr-check-lint-json",
"pr-check-quote-json": "cd frontend && npx gulp pr-check-quote-json",
"pr-check-language-json": "cd frontend && npx gulp pr-check-language-json",
"pr-check-other-json": "cd frontend && npx gulp pr-check-other-json",
"pr-check-scss": "cd frontend && npx gulp pr-check-scss",
"pr-check-ts": "cd frontend && npx gulp pr-check-ts"
},