From 2484c63d77e05fff384cc08b6ea46a29a22a62ec Mon Sep 17 00:00:00 2001 From: Markus Hatvan Date: Tue, 5 Oct 2021 16:46:55 +0200 Subject: chore: repo maintenance (#732) --- scripts/package.js | 221 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 152 insertions(+), 69 deletions(-) (limited to 'scripts/package.js') diff --git a/scripts/package.js b/scripts/package.js index 0f2ae6f..4fc02f1 100644 --- a/scripts/package.js +++ b/scripts/package.js @@ -13,24 +13,28 @@ const pkgVersionChangedMatcher = new RegExp(/\n\+.*version.*/); const repo = 'https://cdn.jsdelivr.net/gh/getferdi/recipes/recipes/'; // Helper: Compress src folder into dest file -const compress = (src, dest) => new Promise((resolve, reject) => { - targz.compress({ - src, - dest, - tar: { - // Don't package .DS_Store files and .md files - ignore: function(name) { - return path.basename(name) === '.DS_Store' || name.endsWith('.md') - } - }, - }, (err) => { - if (err) { - reject(err); - } else { - resolve(dest); - } +const compress = (src, dest) => + new Promise((resolve, reject) => { + targz.compress( + { + src, + dest, + tar: { + // Don't package .DS_Store files and .md files + ignore: function (name) { + return path.basename(name) === '.DS_Store' || name.endsWith('.md'); + }, + }, + }, + err => { + if (err) { + reject(err); + } else { + resolve(dest); + } + }, + ); }); -}); // Let us work in an async environment (async () => { @@ -51,20 +55,23 @@ const compress = (src, dest) => new Promise((resolve, reject) => { const git = await simpleGit(repoRoot); const isGitRepo = await git.checkIsRepo(); if (!isGitRepo) { - console.debug("NOT A git repo: will bypass dirty state checks"); + console.debug('NOT A git repo: will bypass dirty state checks'); } - const availableRecipes = fs.readdirSync(recipesFolder, { withFileTypes: true }) + const availableRecipes = fs + .readdirSync(recipesFolder, { withFileTypes: true }) .filter(dir => dir.isDirectory()) .map(dir => dir.name); - for(let recipe of availableRecipes) { + for (let recipe of availableRecipes) { const recipeSrc = path.join(recipesFolder, recipe); const packageJson = path.join(recipeSrc, 'package.json'); // Check that package.json exists - if (!await fs.pathExists(packageJson)) { - console.log(`⚠️ Couldn't package "${recipe}": Folder doesn't contain a "package.json".`); + if (!(await fs.pathExists(packageJson))) { + console.log( + `⚠️ Couldn't package "${recipe}": Folder doesn't contain a "package.json".`, + ); unsuccessful++; continue; } @@ -73,7 +80,9 @@ const compress = (src, dest) => new Promise((resolve, reject) => { const svgIcon = path.join(recipeSrc, 'icon.svg'); const hasSvg = await fs.pathExists(svgIcon); if (!hasSvg) { - console.log(`⚠️ Couldn't package "${recipe}": Recipe doesn't contain an icon SVG`); + console.log( + `⚠️ Couldn't package "${recipe}": Recipe doesn't contain an icon SVG`, + ); unsuccessful++; continue; } @@ -82,7 +91,9 @@ const compress = (src, dest) => new Promise((resolve, reject) => { const svgSize = sizeOf(svgIcon); const svgHasRightSize = svgSize.width === svgSize.height; if (!svgHasRightSize) { - console.log(`⚠️ Couldn't package "${recipe}": Recipe SVG icon isn't a square`); + console.log( + `⚠️ Couldn't package "${recipe}": Recipe SVG icon isn't a square`, + ); unsuccessful++; continue; } @@ -90,61 +101,114 @@ const compress = (src, dest) => new Promise((resolve, reject) => { // Check that user.js does not exist const userJs = path.join(recipeSrc, 'user.js'); if (await fs.pathExists(userJs)) { - console.log(`⚠️ Couldn't package "${recipe}": Folder contains a "user.js".`); + console.log( + `⚠️ Couldn't package "${recipe}": Folder contains a "user.js".`, + ); unsuccessful++; continue; } // Read package.json - const config = await fs.readJson(packageJson) + const config = await fs.readJson(packageJson); // Make sure it contains all required fields if (!config) { - console.log(`⚠️ Couldn't package "${recipe}": Could not read or parse "package.json"`); + console.log( + `⚠️ Couldn't package "${recipe}": Could not read or parse "package.json"`, + ); unsuccessful++; continue; } let configErrors = []; if (!config.id) { - configErrors.push("The recipe's package.json contains no 'id' field. This field should contain a unique ID made of lowercase letters (a-z), numbers (0-9), hyphens (-), periods (.), and underscores (_)"); + configErrors.push( + "The recipe's package.json contains no 'id' field. This field should contain a unique ID made of lowercase letters (a-z), numbers (0-9), hyphens (-), periods (.), and underscores (_)", + ); + // eslint-disable-next-line no-useless-escape } else if (!/^[a-zA-Z0-9._\-]+$/.test(config.id)) { - configErrors.push("The recipe's package.json defines an invalid recipe ID. Please make sure the 'id' field only contains lowercase letters (a-z), numbers (0-9), hyphens (-), periods (.), and underscores (_)"); + configErrors.push( + "The recipe's package.json defines an invalid recipe ID. Please make sure the 'id' field only contains lowercase letters (a-z), numbers (0-9), hyphens (-), periods (.), and underscores (_)", + ); } if (config.id !== recipe) { - configErrors.push(`The recipe's id (${config.id}) does not match the folder name (${recipe})`); + configErrors.push( + `The recipe's id (${config.id}) does not match the folder name (${recipe})`, + ); } if (!config.name) { - configErrors.push("The recipe's package.json contains no 'name' field. This field should contain the name of the service (e.g. 'Google Keep')"); + configErrors.push( + "The recipe's package.json contains no 'name' field. This field should contain the name of the service (e.g. 'Google Keep')", + ); } if (!config.version) { - configErrors.push("The recipe's package.json contains no 'version' field. This field should contain the a semver-compatible version number for your recipe (e.g. '1.0.0')"); + configErrors.push( + "The recipe's package.json contains no 'version' field. This field should contain the a semver-compatible version number for your recipe (e.g. '1.0.0')", + ); } - if (!config.config || typeof config.config !== "object") { - configErrors.push("The recipe's package.json contains no 'config' object. This field should contain a configuration for your service."); + if (!config.config || typeof config.config !== 'object') { + configErrors.push( + "The recipe's package.json contains no 'config' object. This field should contain a configuration for your service.", + ); } const topLevelKeys = Object.keys(config); topLevelKeys.forEach(key => { - if (typeof(config[key]) === 'string') { + if (typeof config[key] === 'string') { if (config[key] === '') { - configErrors.push(`The recipe's package.json contains empty value for key: ${key}`); + configErrors.push( + `The recipe's package.json contains empty value for key: ${key}`, + ); } - } else if ((key === 'config' || key === 'aliases') && typeof(config[key]) !== 'object') { - configErrors.push(`The recipe's package.json contains unexpected value for key: ${key}`); + } else if ( + (key === 'config' || key === 'aliases') && + typeof config[key] !== 'object' + ) { + configErrors.push( + `The recipe's package.json contains unexpected value for key: ${key}`, + ); } }); - const knownTopLevelKeys = ['id', 'name', 'version', 'license', 'repository', 'aliases', 'config']; - const unrecognizedKeys = topLevelKeys.filter(x => !knownTopLevelKeys.includes(x)); + const knownTopLevelKeys = [ + 'id', + 'name', + 'version', + 'license', + 'repository', + 'aliases', + 'config', + ]; + const unrecognizedKeys = topLevelKeys.filter( + x => !knownTopLevelKeys.includes(x), + ); if (unrecognizedKeys.length > 0) { - configErrors.push(`The recipe's package.json contains the following keys that are not recognized: ${unrecognizedKeys}`); + configErrors.push( + `The recipe's package.json contains the following keys that are not recognized: ${unrecognizedKeys}`, + ); } - if (config.config && typeof config.config === "object") { + if (config.config && typeof config.config === 'object') { const configKeys = Object.keys(config.config); - const knownConfigKeys = ['serviceURL', 'hasTeamId', 'urlInputPrefix', 'urlInputSuffix', 'hasHostedOption', 'hasCustomUrl', 'hasNotificationSound', 'hasDirectMessages', 'hasIndirectMessages', 'allowFavoritesDelineationInUnreadCount', 'message', 'disablewebsecurity']; - const unrecognizedConfigKeys = configKeys.filter(x => !knownConfigKeys.includes(x)); + const knownConfigKeys = [ + 'serviceURL', + 'hasTeamId', + 'urlInputPrefix', + 'urlInputSuffix', + 'hasHostedOption', + 'hasCustomUrl', + 'hasNotificationSound', + 'hasDirectMessages', + 'hasIndirectMessages', + 'allowFavoritesDelineationInUnreadCount', + 'message', + 'disablewebsecurity', + ]; + const unrecognizedConfigKeys = configKeys.filter( + x => !knownConfigKeys.includes(x), + ); if (unrecognizedConfigKeys.length > 0) { - configErrors.push(`The recipe's package.json contains the following keys that are not recognized: ${unrecognizedConfigKeys}`); + configErrors.push( + `The recipe's package.json contains the following keys that are not recognized: ${unrecognizedConfigKeys}`, + ); } // if (config.config.hasCustomUrl !== undefined && config.config.hasHostedOption !== undefined) { @@ -152,8 +216,13 @@ const compress = (src, dest) => new Promise((resolve, reject) => { // } configKeys.forEach(key => { - if (typeof(config.config[key]) === 'string' && config.config[key] === '') { - configErrors.push(`The recipe's package.json contains empty value for key: ${key}`); + if ( + typeof config.config[key] === 'string' && + config.config[key] === '' + ) { + configErrors.push( + `The recipe's package.json contains empty value for key: ${key}`, + ); } }); } @@ -164,38 +233,50 @@ const compress = (src, dest) => new Promise((resolve, reject) => { // Check for changes in recipe's directory, and if changes are present, then the changes should contain a version bump await git.diffSummary(relativeRepoSrc, (err, result) => { if (err) { - configErrors.push(`Got the following error while checking for git changes: ${err}`); - } else if (result && (result.changed !== 0 || result.insertions !== 0 || result.deletions !== 0)) { + configErrors.push( + `Got the following error while checking for git changes: ${err}`, + ); + } else if ( + result && + (result.changed !== 0 || + result.insertions !== 0 || + result.deletions !== 0) + ) { const pkgJsonRelative = path.relative(repoRoot, packageJson); - if (!result.files.find(({file}) => file === pkgJsonRelative)) { - configErrors.push(`Found changes in '${relativeRepoSrc}' without the corresponding version bump in '${pkgJsonRelative}'`); + if (!result.files.find(({ file }) => file === pkgJsonRelative)) { + configErrors.push( + `Found changes in '${relativeRepoSrc}' without the corresponding version bump in '${pkgJsonRelative}'`, + ); } else { git.diff(pkgJsonRelative, (_diffErr, diffResult) => { if (diffResult && !pkgVersionChangedMatcher.test(diffResult)) { - configErrors.push(`Found changes in '${relativeRepoSrc}' without the corresponding version bump in '${pkgJsonRelative}' (found other changes though)`); + configErrors.push( + `Found changes in '${relativeRepoSrc}' without the corresponding version bump in '${pkgJsonRelative}' (found other changes though)`, + ); } }); } } }); - }; + } if (configErrors.length > 0) { console.log(`⚠️ Couldn't package "${recipe}": There were errors in the recipe's package.json: ${configErrors.reduce((str, err) => `${str}\n${err}`)}`); unsuccessful++; - continue; } - if (!await fs.exists(path.join(recipeSrc, 'webview.js'))) { - console.log(`⚠️ Couldn't package "${recipe}": The recipe doesn't contain a "webview.js"`); + if (!fs.existsSync(path.join(recipeSrc, 'webview.js'))) { + console.log( + `⚠️ Couldn't package "${recipe}": The recipe doesn't contain a "webview.js"`, + ); unsuccessful++; - continue; } - if (!await fs.exists(path.join(recipeSrc, 'index.js'))) { - console.log(`⚠️ Couldn't package "${recipe}": The recipe doesn't contain a "index.js"`); + if (!fs.existsSync(path.join(recipeSrc, 'index.js'))) { + console.log( + `⚠️ Couldn't package "${recipe}": The recipe doesn't contain a "index.js"`, + ); unsuccessful++; - continue; } // Package to .tar.gz @@ -204,13 +285,13 @@ const compress = (src, dest) => new Promise((resolve, reject) => { // Add recipe to all.json const isFeatured = featuredRecipes.includes(config.id); const packageInfo = { - "featured": isFeatured, - "id": config.id, - "name": config.name, - "version": config.version, - "aliases": config.aliases, - "icons": { - "svg": `${repo}${config.id}/icon.svg`, + featured: isFeatured, + id: config.id, + name: config.name, + version: config.version, + aliases: config.aliases, + icons: { + svg: `${repo}${config.id}/icon.svg`, }, }; recipeList.push(packageInfo); @@ -220,14 +301,16 @@ const compress = (src, dest) => new Promise((resolve, reject) => { recipeList = recipeList.sort((a, b) => { var textA = a.id.toLowerCase(); var textB = b.id.toLowerCase(); - return (textA < textB) ? -1 : (textA > textB) ? 1 : 0; + return textA < textB ? -1 : textA > textB ? 1 : 0; }); await fs.writeJson(allJson, recipeList, { spaces: 2, EOL: '\n', }); - console.log(`✅ Successfully packaged and added ${recipeList.length} recipes (${unsuccessful} unsuccessful recipes)`); + console.log( + `✅ Successfully packaged and added ${recipeList.length} recipes (${unsuccessful} unsuccessful recipes)`, + ); if (unsuccessful > 0) { process.exit(1); -- cgit v1.2.3-70-g09d2