aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/api/package.js
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/api/package.js')
-rw-r--r--scripts/api/package.js204
1 files changed, 204 insertions, 0 deletions
diff --git a/scripts/api/package.js b/scripts/api/package.js
new file mode 100644
index 0000000..2d265f6
--- /dev/null
+++ b/scripts/api/package.js
@@ -0,0 +1,204 @@
1/**
2 * Package recipe into tar.gz file
3 */
4const targz = require('targz');
5const fs = require('fs-extra');
6const path = require('path');
7const sizeOf = require('image-size');
8const semver = require('semver');
9
10// Publicly availible link to this repository's uncompressed folder
11// Used for generating public icon URLs
12const repo = 'https://cdn.jsdelivr.net/gh/getferdi/recipes/uncompressed/';
13
14// Helper: Compress src folder into dest file
15const compress = (src, dest) => new Promise((resolve, reject) => {
16 targz.compress({
17 src,
18 dest,
19 tar: {
20 // Don't package .DS_Store files
21 ignore: function(name) {
22 return path.basename(name) === '.DS_Store'
23 }
24 },
25 }, (err) => {
26 if (err) {
27 reject(err);
28 } else {
29 resolve(dest);
30 }
31 });
32});
33// Let us work in an async environment
34module.exports = async () => {
35 // Create paths to important files
36 const recipeSrc = path.join(__dirname, '../recipe_src');
37 const packageJson = path.join(recipeSrc, 'package.json');
38 const svgIcon = path.join(recipeSrc, 'icon.svg');
39 const pngIcon = path.join(recipeSrc, 'icon.png');
40 const allJson = path.join(__dirname, '../../', 'all.json');
41 let all = await fs.readJson(allJson);
42
43 // Check that package.json exists
44 if (!await fs.pathExists(packageJson)) {
45 console.log(`⚠️ Could not add your recipe: Please add your recipe to ${recipeSrc} and make sure that folder contains a "package.json".
46For more information on how to add your recipe visit https://github.com/getferdi/recipes/blob/master/docs/integration.md`);
47 return;
48 }
49
50 // Check that icons exist
51 const hasSvg = await fs.pathExists(svgIcon);
52 const hasPng = await fs.pathExists(pngIcon);
53 if (!hasSvg && !hasPng) {
54 console.log(`⚠️ Could not add your recipe: Please make sure your recipe contains an icon.png and an icon.svg file.
55Those icons should be the logo of the recipe you are trying to add.
56Please also make sure that your icons are 1024x1024px in size.
57For more information about recipe icons visit https://github.com/getferdi/recipes/blob/master/docs/integration.md#icons`);
58 return;
59 } else if (!hasSvg) {
60 console.log(`⚠️ Could not add your recipe: Please make sure your recipe contains an icon.svg file.
61Your recipe already contains an "icon.png" but it also requires an "icon.svg" to display properly.
62Please also make sure that your icons are 1024x1024px in size.
63For more information about recipe icons visit https://github.com/getferdi/recipes/blob/master/docs/integration.md#icons`);
64 return;
65 } else if (!hasPng) {
66 console.log(`⚠️ Could not add your recipe: Please make sure your recipe contains an icon.png file.
67Your recipe already contains an "icon.svg" but it also requires an "icon.png" to display properly.
68Please also make sure that your icons are 1024x1024px in size.
69For more information about recipe icons visit https://github.com/getferdi/recipes/blob/master/docs/integration.md#icons`);
70 return;
71 }
72
73 // Check that icons have the right dimensions
74 const pngSize = sizeOf(pngIcon);
75 const pngHasRightSize = pngSize.width === 1024 && pngSize.height === 1024;
76 const svgSize = sizeOf(svgIcon);
77 const svgHasRightSize = svgSize.width === 1024 && svgSize.height === 1024;
78 if (!pngHasRightSize && !svgHasRightSize) {
79 console.log(`⚠️ Could not add your recipe: Icons require to be 1024x1024px in size.
80Both your "icon.png" and "icon.svg" don't seem to have the right size.
81You can use software like Photoshop, GIMP or Photopea (https://www.photopea.com/) to resize your icons.
82For more information about recipe icons visit https://github.com/getferdi/recipes/blob/master/docs/integration.md#icons`);
83 return;
84 } else if (!pngHasRightSize) {
85 console.log(`⚠️ Could not add your recipe: "icon.png" should be to be 1024x1024px in size.
86Please make sure that your "icon.png" has the right size of 1024x1024px in size.
87You can use software like Photoshop, GIMP or Photopea (https://www.photopea.com/) to resize your icons.
88For more information about recipe icons visit https://github.com/getferdi/recipes/blob/master/docs/integration.md#icons`);
89 return;
90 } else if (!svgHasRightSize) {
91 console.log(`⚠️ Could not add your recipe: "icon.svg" should be to be 1024x1024px in size.
92Please make sure that your "icon.svg" has the right size of 1024x1024px in size.
93You can use software like Photoshop, GIMP or Photopea (https://www.photopea.com/) to resize your icons.
94For more information about recipe icons visit https://github.com/getferdi/recipes/blob/master/docs/integration.md#icons`);
95 return;
96 }
97
98 // Read package.json
99 const config = await fs.readJson(packageJson)
100
101 // Make sure it contains all required fields
102 if (!config) {
103 console.log(`⚠️ Could not add your recipe: We could not read or parse your "package.json" configuration.
104Please make sure your "package.json" contains valid JSON.
105For more information about the package.json file visit https://github.com/getferdi/recipes/blob/master/docs/configuration.md`);
106 return;
107 }
108 let configErrors = [];
109 if (!config.id) {
110 configErrors.push("Your package.json contains no 'id' field. This field should contain a unique ID made of lowercase letters (a-z) and hyphens (-)");
111 } else if (!/^[a-z._\-]+$/.test(config.id)) {
112 configErrors.push("Your package.json defines an invalid recipe ID. Please make sure the 'id' field only contains lowercase letters (a-z) and hyphens (-)");
113 }
114 if (!config.name) {
115 configErrors.push("Your package.json contains no 'name' field. This field should contain the name of the service (e.g. 'Google Keep')");
116 }
117 if (!config.version) {
118 configErrors.push("Your package.json contains no 'version' field. This field should contain the a semver-compatible version number for your recipe (e.g. '1.0.0')");
119 }
120 if (!config.config || typeof config.config !== "object") {
121 configErrors.push("Your package.json contains no 'config' object. This field should contain a configuration for your service.");
122 } else if (!config.config.serviceURL) {
123 configErrors.push("Your package.json contains a 'config' object with no 'serviceURL' field. This field should contain the URL of your service.");
124 }
125
126 if (configErrors.length > 0) {
127 console.log(`⚠️ Could not add your recipe: There were errors in your package.json:
128${configErrors.reduce((str, err) => `${str}\n${err}`)}
129For more information about the package.json file visit https://github.com/getferdi/recipes/blob/master/docs/configuration.md`);
130 return;
131 }
132
133 // Index of the current recipe in all.json
134 const packageIndex = all.findIndex(e => e.id === config.id)
135
136 if (packageIndex !== -1) {
137 const currentVersion = config.version;
138 const repoVersion = all[packageIndex].version;
139
140 if (semver.gte(repoVersion, currentVersion)) {
141 console.log(`⚠️ Could not add your recipe: It looks like your recipe is using the same version number as the current recipe.
142Please make sure to increase the version number inside your "package.json" everytime you want to repackage (e.g. '1.0.0' to '1.0.1').
143If you don't increase your version number, Ferdi cannot detect that you have made changes to the recipe.
144For more information about versioning of recipes visit https://github.com/getferdi/recipes/blob/master/docs/configuration.md#config-flags`);
145 return;
146 }
147 }
148
149 if (!await fs.exists(path.join(recipeSrc, 'webview.js'))) {
150 console.log(`⚠️ Could not add your recipe: It looks like your recipe doesn't contain a "webview.js" file.
151Please make sure to create that file and add your features to it.
152For more information about the webview.js file visit https://github.com/getferdi/recipes/blob/master/docs/integration.md#webviewjs and https://github.com/getferdi/recipes/blob/master/docs/frontend_api.md`);
153 return;
154 }
155 if (!await fs.exists(path.join(recipeSrc, 'index.js'))) {
156 console.log(`⚠️ Could not add your recipe: It looks like your recipe doesn't contain a "index.js" file.
157Please make sure to create that file and add your features to it. For most recipes it is enough to simply add the basic template found at https://github.com/getferdi/recipes/blob/master/docs/integration.md#indexjs
158For more information about the webview.js file visit https://github.com/getferdi/recipes/blob/master/docs/integration.md#indexjs and https://github.com/getferdi/recipes/blob/master/docs/backend_api.md`);
159 return;
160 }
161
162 // Package to .tar.gz
163 console.log(`[Info] Packaging ${config.id}...`);
164 compress(recipeSrc, path.join(__dirname, '../../', 'archives', `${config.id}.tar.gz`));
165
166 // Copy recipe src folder to /uncompressed/:id folder
167 console.log('[Info] Copying to uncompressed recipes');
168 await fs.copy('recipe_src', path.join(__dirname, '../../', 'uncompressed', `${config.id}`));
169
170 // Add recipe to all.json
171 console.log('[Info] Adding to all.json');
172 const packageInfo = {
173 "author": config.author || '',
174 "featured": false,
175 "id": config.id,
176 "name": config.name,
177 "version": config.version || '1.0.0',
178 "icons": {
179 "png": `${repo}${config.id}/icon.png`,
180 "svg": `${repo}${config.id}/icon.svg`,
181 },
182 };
183 // Check if package ID already exists
184 if (packageIndex !== -1) {
185 console.log('[Info] Recipe with ID already exists - overwriting');
186 all[packageIndex] = packageInfo;
187 } else {
188 console.log('[Info] No recipe with ID found - creating new.');
189 all.push(packageInfo);
190 }
191
192 // Sort package list alphabetically
193 all = all.sort((a, b) => {
194 var textA = a.id.toLowerCase();
195 var textB = b.id.toLowerCase();
196 return (textA < textB) ? -1 : (textA > textB) ? 1 : 0;
197 });
198 await fs.writeJson(allJson, all, {
199 spaces: 2,
200 EOL: '\n',
201 });
202
203 console.log(`✅ Successfully packaged and added new recipe "${config.id}"`);
204}; \ No newline at end of file