Created
October 26, 2018 17:16
-
-
Save pdehaan/e61662b050564ee9ec0963f0d7547f04 to your computer and use it in GitHub Desktop.
TestPilot experiment YAML linter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const {existsSync} = require("fs"); | |
const Ajv = require("ajv"); | |
const checkLinks = require("check-links"); | |
const glob = require("glob"); | |
const YAML = require('yamljs'); | |
const ajv = new Ajv({allErrors: true}); | |
const experimentSchema = { | |
type: "object", | |
properties: { | |
addon_id: {type: "string"}, | |
android_url: {type: "string", format: "uri"}, | |
bug_report_url: {type: "string"}, | |
changelog_url: {type: "string"}, | |
completed: {type: "string", format: "date-time"}, | |
contribute_url: {type: "string"}, | |
contributors: { | |
type: "array", | |
items: [ | |
{ | |
type: "object", | |
properties: { | |
display_name: {type: "string"}, | |
avatar: {type: "string"}, | |
title: {type: "string"} | |
}, | |
required: ["display_name", "avatar"] | |
} | |
], | |
minItems: 1 | |
}, | |
contributors_extra: {type: "string"}, | |
contributors_extra_url: {type: "string", format: "uri"}, | |
created: {type: "string", format: "date-time"}, | |
description: {type: "string"}, | |
description_l10nsuffix: {type: "string"}, | |
details: { | |
type: "array", | |
items: [ | |
{ | |
type: "object", | |
properties: { | |
image: {type: "string"}, | |
copy: {type: "string"} | |
}, | |
required: ["image", "copy"] | |
} | |
] | |
}, | |
dev: {type: "boolean"}, | |
discourse_url: {type: "string"}, | |
eol_warning: {type: "string"}, | |
gradient_center: {type: "string"}, | |
gradient_start: {type: "string"}, | |
gradient_stop: {type: "string"}, | |
graduation_url: {type: "string", format: "uri"}, | |
id: {type: "integer"}, | |
image_facebook: {type: ["string", "null"]}, | |
image_twitter: {type: ["string", "null"]}, | |
introduction: {type: "string"}, | |
incompatible: {type: "object"}, | |
introduction_l10nsuffix: {type: "string"}, | |
ios_url: {type: "string", format: "uri"}, | |
is_featured: {type: "boolean"}, | |
launch_date: {type: "string", format: "date-time"}, | |
legal_notice: {type: "string"}, | |
legal_notice_l10nsuffix: {type: "string"}, | |
locales: {type: "array", items: {type: "string"}}, | |
locale_blocklist: {type: "array", items: {type: "string"}}, | |
locale_grantlist: {type: "array", items: {type: "string"}}, | |
measurements: { | |
type: "array", | |
items: [ | |
{type: "string"} | |
] | |
}, | |
max_release: {type: "integer"}, | |
measurements_l10nsuffix: {type: "string"}, | |
min_release: {type: ["integer", "null"]}, | |
modified: {format: "string", format: "date-time"}, | |
news_updates: { | |
type: "array", | |
items: { | |
type: "object", | |
properties: { | |
slug: {type: "string"}, | |
title: {type: "string"}, | |
link: {type: "string", format: "uri"}, | |
created: {instanceof: "Date"}, | |
published: {instanceof: "Date"}, | |
dev: {type: "boolean"}, | |
content: {type: "string"} | |
}, | |
required: ["slug", "title", "link", "created", "published", "content", "dev"] | |
} | |
}, | |
order: {type: "integer"}, | |
platforms: { | |
type: "array", | |
items: [ | |
{type: "string", enum: ["addon", "android", "ios", "web"]} | |
], | |
minItems: 1, | |
uniqueItems: true | |
}, | |
pre_feedback_copy: {type: ["string", "null"]}, | |
pre_feedback_image: {type: ["string", "null"]}, | |
privacy_notice_url: {type: "string", format: "uri"}, | |
privacy_preamble: {type: "string"}, | |
slug: {type: "string"}, | |
subtitle: {type: "string"}, | |
testpilot_options: { | |
type: "object", | |
properties: { | |
ratings: {type: "string", enum: ["disabled"]} | |
} | |
}, | |
thumbnail: {type: "string"}, | |
title: {type: "string"}, | |
tour_steps: { | |
type: "array", | |
items: [ | |
{ | |
type: "object", | |
properties: { | |
copy: {type: "string"}, | |
image: {type: "string"} | |
} | |
} | |
] | |
}, | |
uninstalled: { | |
type: "string", | |
format: "date-time" | |
}, | |
video_url: { | |
type: "string", | |
format: "uri" | |
}, | |
warning: {type: "string"}, | |
warning_l10nsuffix: {type: "string"}, | |
web_url: {type: "string", format: "uri"}, | |
xpi_url: { | |
type: "string", | |
format: "uri" | |
} | |
}, | |
additionalProperties: false, | |
required: ["id", "order", "image_facebook", "image_twitter", "slug", "title"] | |
}; | |
main("./content-src/experiments/*.yaml"); | |
function main(dir) { | |
glob(dir, async (err, files) => { | |
if (err) { | |
console.error(err); | |
process.exit(1); | |
} | |
for (const file of files) { | |
await lintExperiment(file); | |
} | |
process.exit(0); | |
}); | |
} | |
async function lintExperiment(p) { | |
const experiment = YAML.load(p); | |
console.log(`\n${experiment.title}`); | |
const valid = ajv.validate(experimentSchema, experiment); | |
if (!valid) { | |
console.log(JSON.stringify(ajv.errors, null, 2)); | |
console.log(experiment); | |
} | |
console.log(" checking urls:"); | |
await lintUrls(experiment); | |
console.log(" checking images:"); | |
lintImages(experiment); | |
} | |
function getImages(experiment) { | |
const arr = [ | |
experiment.image_facebook, | |
experiment.image_twitter, | |
experiment.thumbnail | |
]; | |
experiment.contributors.forEach(({avatar}) => arr.push(avatar)); | |
experiment.details.forEach(({image}) => arr.push(image)); | |
(experiment.tour_steps || []).forEach(({image}) => arr.push(image)); | |
return arr.filter(item => item); | |
} | |
function getUrls(experiment) { | |
const arr = [ | |
experiment.bug_report_url, | |
experiment.changelog_url, | |
experiment.contribute_url, | |
experiment.discourse_url, | |
experiment.graduation_url, | |
experiment.privacy_notice_url, | |
experiment.video_url, | |
experiment.xpi_url | |
]; | |
(experiment.news_updates || []).forEach(({link}) => arr.push(link)); | |
return arr.filter(item => item); | |
} | |
function staticImage(src) { | |
return src.replace("/static/", "./frontend/src/"); | |
} | |
function lintImages(experiment) { | |
const missingImages = getImages(experiment) | |
.filter(src => !existsSync(staticImage(src))); | |
if (missingImages.length) { | |
missingImages.forEach(src => console.log(` - Missing ${src}`)); | |
process.exitCode = 2; | |
} | |
} | |
async function lintUrls(experiment) { | |
const urls = getUrls(experiment); | |
const results = await checkLinks(urls); | |
urls.filter(link => results[link].status !== "alive") | |
.forEach(link => console.log(` - ${link}`, results[link])); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment