cob-cli 2.29.0 → 2.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/cob-cli.js CHANGED
@@ -36,6 +36,7 @@ program
36
36
  .option('-f --force', 'skips comparisons')
37
37
  .option('-c --cache', 'cache all customizations')
38
38
  .option('-l --local', 'use local files')
39
+ .option('-u --update', 'update all previsously added customizations')
39
40
  .description('Interactive prompt to customize an aspect of the server')
40
41
  .action( customize );
41
42
 
@@ -10,66 +10,106 @@ const { Transform } = require("stream");
10
10
  const fg = require("fast-glob");
11
11
  const fs = require("fs-extra");
12
12
 
13
+ const customizationsVersionsFile = "customizations.json";
14
+
13
15
  /* ************************************************************************ */
14
16
  async function customize(filter, args) {
15
17
  try {
16
18
  console.log("Customize...");
17
- checkVersion();
18
- if (!args.force) await checkWorkingCopyCleanliness();
19
+ if (!args.cache) checkVersion();
20
+ if (!args.force && !args.cache) await checkWorkingCopyCleanliness();
19
21
 
20
- let customizationRepo = await getCustomizationRepo(filter,args);
21
- if (!args.local) await getCustomizationFiles(customizationRepo);
22
- await applyCustomization(customizationRepo);
22
+ if (args.cache) {
23
+ await cacheAllCustomizations(filter, args);
24
+
25
+ } else if (args.update) {
26
+ await updatePreviouslyAppliedCostumizations(filter, args);
27
+
28
+ } else {
29
+ const customizationRepo = await getCustomizationRepo(filter, args);
30
+ await applyCustomization(customizationRepo, args);
31
+
32
+ console.log( colors.green("\nDone"), "\nCheck changes to your git working tree and try:" );
33
+ console.log( "\tcob-cli test\n" );
34
+ }
23
35
 
24
- console.log( colors.green("\nDone"), "\nCheck changes to your git working tree and try:" );
25
- console.log( "\tcob-cli test\n" );
26
36
  } catch (err) {
27
37
  console.error("\n", err.message);
28
38
  }
29
39
  }
30
40
  module.exports = customize;
31
41
 
42
+ /* ************************************************************************ */
43
+ async function updatePreviouslyAppliedCostumizations(filter, args) {
44
+ if (filter || args.cache) {
45
+ throw new Error("\nError: ".red + " incompatible options. Use of --update with filter or --cache.\n");
46
+ }
47
+ console.log("Updating all installed customizations...");
48
+
49
+ let customizationsVersions = {};
50
+ if(fs.existsSync(customizationsVersionsFile)) {
51
+ const customizationsVersionsRawData = fs.readFileSync(customizationsVersionsFile);
52
+ customizationsVersions = JSON.parse(customizationsVersionsRawData);
53
+ }
54
+
55
+ const customizationRepos = await getCustomizationRepo(null, args);
56
+ for (let customizationRepo of customizationRepos) {
57
+ if(customizationsVersions[customizationRepo.name]) {
58
+ await applyCustomization(customizationRepo, args);
59
+ }
60
+ }
61
+ }
62
+
63
+ /* ************************************************************************ */
64
+ async function cacheAllCustomizations(filter, args) {
65
+ if (filter || args.local || args.update) {
66
+ throw new Error("\nError: ".red + " incompatible options. Use --cache as single argument\n");
67
+ }
68
+
69
+ console.log("Caching all customizations...");
70
+ const customizationRepos = await getCustomizationRepo(null, args);
71
+ for (let customizationRepo of customizationRepos) {
72
+ await downloadCustomizationFiles(customizationRepo);
73
+ }
74
+
75
+ const { xdgData } = await import("xdg-basedir");
76
+ const cacheCustomizationsFile = path.resolve(xdgData, "cob-cli", "customizations.json")
77
+ fs.writeFileSync(cacheCustomizationsFile,
78
+ JSON.stringify(customizationRepos, null, 2),
79
+ (err) => {
80
+ if(err) throw new Error("\nError: ".red + " problem writing " + cacheCustomizationsFile + ":", err.message);
81
+ }
82
+ );
83
+ }
84
+
32
85
  /* ************************************************************************ */
33
86
  async function getCustomizationRepo(filter, args) {
34
87
  const { xdgData } = await import("xdg-basedir");
35
- const cacheCustomizations = path.resolve(xdgData,"cob-cli","customizations.json")
88
+ const cacheCustomizationsFile = path.resolve(xdgData,"cob-cli","customizations.json")
36
89
 
37
90
  let customizationRepos;
38
91
  let customizationNameQuery = "customize. " + (filter ? filter : "");
39
92
  if (args.local) {
40
- customizationRepos = JSON.parse(fs.readFileSync(cacheCustomizations))
93
+ customizationRepos = JSON.parse(fs.readFileSync(cacheCustomizationsFile))
41
94
  if (filter) {
42
95
  customizationRepos = customizationRepos.filter(customizationRepo => customizationRepo.name.indexOf(filter) != -1)
43
96
  }
44
97
  } else {
45
98
  // Get list of relevant customizations from github
46
- let response = await axios.get(
47
- "https://api.github.com/search/repositories?q=" +
48
- customizationNameQuery + "+in:name+org:cob",
49
- { headers: { Accept: "application/json", "Accept-Encoding": "identity" } }
50
- )
99
+ const response = await axios.get( "https://api.github.com/search/repositories?q=" + customizationNameQuery + "+in:name+org:cob", { headers: { Accept: "application/json", "Accept-Encoding": "identity" } } )
51
100
  customizationRepos = response.data.items
52
101
  }
53
102
 
54
- // Check if there's at least one customization
55
- if (customizationRepos.length == 0) throw new Error("\nError: ".red + " no customization found\n");
103
+ //If the goal is to cache or update customizations just return all customizations
104
+ if (args.cache || args.update) {
105
+ return customizationRepos;
106
+ }
56
107
 
57
- // Asks for customization to apply
58
- if (args.cache && args.local) {
59
- throw new Error("\nError: ".red + " incompatible options, --local AND --cache\n");
60
- } else if(args.cache) {
61
- console.log("Caching all customizations...");
62
- for( let customizationRepo of customizationRepos) {
63
- await getCustomizationFiles(customizationRepo);
64
- }
65
- fs.writeFileSync( cacheCustomizations,
66
- JSON.stringify(customizationRepos, null, 2),
67
- (err) => {
68
- if (err) console.log( "Error writing " + cacheCustomizations + ":", err.message);
69
- }
70
- )
71
- }
72
108
 
109
+ // Throw error if there's no customization
110
+ if (customizationRepos.length == 0) throw new Error("\nError: ".red + " no customization found\n");
111
+
112
+ // Otherwise asks for customization to apply
73
113
  let answer = await inquirer.prompt([
74
114
  {
75
115
  type: "list",
@@ -84,7 +124,7 @@ async function getCustomizationRepo(filter, args) {
84
124
  }
85
125
 
86
126
  /* ************************************************************************ */
87
- async function getCustomizationFiles(customizationRepo) {
127
+ async function downloadCustomizationFiles(customizationRepo) {
88
128
  const { xdgData } = await import("xdg-basedir");
89
129
  const baseDir = process.cwd();
90
130
  const cacheDir = path.resolve(xdgData,"cob-cli")
@@ -97,16 +137,20 @@ async function getCustomizationFiles(customizationRepo) {
97
137
  } else {
98
138
  console.log(" git pull " + customizationRepo.ssh_url);
99
139
  process.chdir(customizationRepo.name);
100
- await git().pull();
140
+ try {
141
+ await git().pull();
142
+ } catch (error) { }
101
143
  }
102
144
  process.chdir(baseDir);
103
145
  }
104
146
 
105
147
  /* ************************************************************************ */
106
- async function applyCustomization(customizationRepo) {
148
+ async function applyCustomization(customizationRepo, args) {
107
149
  console.log("\nApplying " + customizationRepo.name + " customization ...");
108
150
 
109
151
  // Get customization info from convention defined file (customize.js)
152
+ if (!args.local) await downloadCustomizationFiles(customizationRepo);
153
+
110
154
  const { xdgData } = await import("xdg-basedir");
111
155
  const customizationPath = path.resolve( xdgData, "cob-cli", customizationRepo.name);
112
156
  const customizationFile = path.resolve( customizationPath, "customize.js");
@@ -126,7 +170,7 @@ async function applyCustomization(customizationRepo) {
126
170
  customization.actions(customizationRepo.name, answers, copyAndMerge);
127
171
  } else {
128
172
  // Default actions
129
- copyAndMerge(customizationRepo.name, answers);
173
+ await copyAndMerge(customizationRepo.name, answers);
130
174
  }
131
175
 
132
176
  // Update customizations.json file
@@ -166,50 +210,53 @@ async function copyAndMerge(customizationRepoName, substitutions = {}) {
166
210
  // Source is on cob-cli customizationRepo and Destination on the server repo
167
211
  const target = process.cwd() // Always copy to directory where command is being executed, ie, the root directory of the server repo
168
212
  const substitutionRegex = /__(((?!word).)*)__/g;
169
- ncp(
170
- source,
171
- target,
172
- {
173
- clobber: true,
174
- filter: (src) => src.match(excludedFiles) == null,
175
- rename: function(target) {
176
- // Don't rename __MERGE__ templates, they will be handled by the merge method
177
- if (target.match(/__MERGE__/)) return target;
178
-
179
- //get finalTarget from target with any existing substitution
180
- const finalTarget = target.replace(substitutionRegex, (match,g1) => substitutions[g1] ? substitutions[g1] : match);
181
-
182
- //if the directory of finalTarget doesn't exists it means that a replacement ocurred on the dirpath (ie, there was a /__.+__/ was on the dirpath), in which case we need to create the desired directory and remove the one just created by ncp
183
- if (!fs.existsSync(path.dirname(finalTarget))) {
184
- fs.mkdirSync(path.dirname(finalTarget), { recursive: true })
185
- fs.rmdirSync(target.substring(0,target.lastIndexOf("__")+2), { recursive: true,force: false }) //NOTE: won't handle more than 1 substitution on the same dirpath
213
+ return new Promise((resolve, reject) => {
214
+ ncp(
215
+ source,
216
+ target,
217
+ {
218
+ clobber: true,
219
+ filter: (src) => src.match(excludedFiles) == null,
220
+ rename: function(target) {
221
+ // Don't rename __MERGE__ templates, they will be handled by the merge method
222
+ if (target.match(/__MERGE__/)) return target;
223
+
224
+ //get finalTarget from target with any existing substitution
225
+ const finalTarget = target.replace(substitutionRegex, (match,g1) => substitutions[g1] ? substitutions[g1] : match);
226
+
227
+ //if the directory of finalTarget doesn't exists it means that a replacement ocurred on the dirpath (ie, there was a /__.+__/ was on the dirpath), in which case we need to create the desired directory and remove the one just created by ncp
228
+ if (!fs.existsSync(path.dirname(finalTarget))) {
229
+ fs.mkdirSync(path.dirname(finalTarget), { recursive: true })
230
+ fs.rmdirSync(target.substring(0,target.lastIndexOf("__")+2), { recursive: true,force: false }) //NOTE: won't handle more than 1 substitution on the same dirpath
231
+ }
232
+ return finalTarget;
233
+ },
234
+ transform(read, write) {
235
+ const replaceVarsTransformFunction = new Transform({
236
+ transform: (chunk, encoding, done) => {
237
+ if(/\ufffd/.test(chunk) === true) {
238
+ // If chunk is binary don't change anything
239
+ done(null, chunk)
240
+ } else {
241
+ // Otherwise change any existing substitution
242
+ done(null,chunk.toString().replace(substitutionRegex, (match,g1) => substitutions[g1] ? substitutions[g1] : match))
243
+ }
244
+ }
245
+ })
246
+ read.pipe(replaceVarsTransformFunction).pipe(write)
186
247
  }
187
- return finalTarget;
188
248
  },
189
- transform(read, write) {
190
- const replaceVarsTransformFunction = new Transform({
191
- transform: (chunk, encoding, done) => {
192
- if(/\ufffd/.test(chunk) === true) {
193
- // If chunk is binary don't change anything
194
- done(null, chunk)
195
- } else {
196
- // Otherwise change any existing substitution
197
- done(null,chunk.toString().replace(substitutionRegex, (match,g1) => substitutions[g1] ? substitutions[g1] : match))
198
- }
199
- }
200
- })
201
- read.pipe(replaceVarsTransformFunction).pipe(write)
202
- }
203
- },
204
- (error) => {
205
- // If no error occurred then proced with merging files
206
- if(!error) {
207
- mergeFiles(customizationRepoName);
208
- } else {
209
- throw new Error(error.map((e) => e.message).join("\n"))
249
+ async (error) => {
250
+ // If no error occurred then proced with merging files
251
+ if(!error) {
252
+ await mergeFiles(customizationRepoName);
253
+ resolve();
254
+ } else {
255
+ reject(error.map((e) => e.message).join("\n"))
256
+ }
210
257
  }
211
- }
212
- )
258
+ )
259
+ });
213
260
  }
214
261
 
215
262
  /* ************************************************************************ */
@@ -256,13 +303,16 @@ async function mergeFiles(block) {
256
303
 
257
304
  /* ************************************************************************ */
258
305
  function updateCustomizationsVersions(customizationKey, version) {
259
- const customizationsVersionsFile = "customizations.json";
260
306
  let customizationsVersions = {};
261
307
  if(fs.existsSync(customizationsVersionsFile)) {
262
308
  const customizationsVersionsRawData = fs.readFileSync(customizationsVersionsFile);
263
309
  customizationsVersions = JSON.parse(customizationsVersionsRawData);
264
310
  }
265
- customizationsVersions = {[customizationKey]: version, ...customizationsVersions}
311
+ if(customizationsVersions[customizationKey]) {
312
+ customizationsVersions[customizationKey] = version
313
+ } else {
314
+ customizationsVersions = {[customizationKey]: version, ...customizationsVersions}
315
+ }
266
316
 
267
317
  fs.writeFileSync(
268
318
  customizationsVersionsFile,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cob-cli",
3
- "version": "2.29.0",
3
+ "version": "2.30.0",
4
4
  "description": "A command line utility to help Cult of Bits partners develop with higher speed and reusing common code and best practices.",
5
5
  "preferGlobal": true,
6
6
  "repository": {