Reduce shell calls to single steps
[cacert-boardvoting.git] / semantic / tasks / install.js
1 /*******************************
2 Install Task
3 *******************************/
4
5 /*
6 Install tasks
7
8 For more notes
9
10 * Runs automatically after npm update (hooks)
11 * (NPM) Install - Will ask for where to put semantic (outside pm folder)
12 * (NPM) Upgrade - Will look for semantic install, copy over files and update if new version
13 * Standard installer runs asking for paths to site files etc
14
15 */
16
17 var
18 gulp = require('gulp'),
19
20 // node dependencies
21 console = require('better-console'),
22 extend = require('extend'),
23 fs = require('fs'),
24 mkdirp = require('mkdirp'),
25 path = require('path'),
26 runSequence = require('run-sequence'),
27
28 // gulp dependencies
29 chmod = require('gulp-chmod'),
30 del = require('del'),
31 jsonEditor = require('gulp-json-editor'),
32 plumber = require('gulp-plumber'),
33 prompt = require('gulp-prompt'),
34 rename = require('gulp-rename'),
35 replace = require('gulp-replace'),
36 requireDotFile = require('require-dot-file'),
37 wrench = require('wrench-sui'),
38
39 // install config
40 install = require('./config/project/install'),
41
42 // user config
43 config = require('./config/user'),
44
45 // release config (name/title/etc)
46 release = require('./config/project/release'),
47
48 // shorthand
49 questions = install.questions,
50 files = install.files,
51 folders = install.folders,
52 regExp = install.regExp,
53 settings = install.settings,
54 source = install.source
55 ;
56
57 // Export install task
58 module.exports = function (callback) {
59
60 var
61 currentConfig = requireDotFile('semantic.json'),
62 manager = install.getPackageManager(),
63 rootQuestions = questions.root,
64 installFolder = false,
65 answers
66 ;
67
68 console.clear();
69
70 /* Test NPM install
71 manager = {
72 name : 'NPM',
73 root : path.normalize(__dirname + '/../')
74 };
75 */
76
77
78 /* Don't do end user config if SUI is a sub-module */
79 if( install.isSubModule() ) {
80 console.info('SUI is a sub-module, skipping end-user install');
81 return;
82 }
83
84 /*-----------------
85 Update SUI
86 -----------------*/
87
88 // run update scripts if semantic.json exists
89 if(currentConfig && manager.name === 'NPM') {
90
91 var
92 updateFolder = path.join(manager.root, currentConfig.base),
93 updatePaths = {
94 config : path.join(manager.root, files.config),
95 tasks : path.join(updateFolder, folders.tasks),
96 themeImport : path.join(updateFolder, folders.themeImport),
97 definition : path.join(currentConfig.paths.source.definitions),
98 site : path.join(currentConfig.paths.source.site),
99 theme : path.join(currentConfig.paths.source.themes),
100 defaultTheme : path.join(currentConfig.paths.source.themes, folders.defaultTheme)
101 }
102 ;
103
104 // duck-type if there is a project installed
105 if( fs.existsSync(updatePaths.definition) ) {
106
107 // perform update if new version
108 if(currentConfig.version !== release.version) {
109 console.log('Updating Semantic UI from ' + currentConfig.version + ' to ' + release.version);
110
111 console.info('Updating ui definitions...');
112 wrench.copyDirSyncRecursive(source.definitions, updatePaths.definition, settings.wrench.overwrite);
113
114 console.info('Updating default theme...');
115 wrench.copyDirSyncRecursive(source.themes, updatePaths.theme, settings.wrench.merge);
116 wrench.copyDirSyncRecursive(source.defaultTheme, updatePaths.defaultTheme, settings.wrench.overwrite);
117
118 console.info('Updating tasks...');
119 wrench.copyDirSyncRecursive(source.tasks, updatePaths.tasks, settings.wrench.overwrite);
120
121 console.info('Updating gulpfile.js');
122 gulp.src(source.userGulpFile)
123 .pipe(plumber())
124 .pipe(gulp.dest(updateFolder))
125 ;
126
127 // copy theme import
128 console.info('Updating theme import file');
129 gulp.src(source.themeImport)
130 .pipe(plumber())
131 .pipe(gulp.dest(updatePaths.themeImport))
132 ;
133
134 console.info('Adding new site theme files...');
135 wrench.copyDirSyncRecursive(source.site, updatePaths.site, settings.wrench.merge);
136
137 console.info('Updating version...');
138
139 // update version number in semantic.json
140 gulp.src(updatePaths.config)
141 .pipe(plumber())
142 .pipe(rename(settings.rename.json)) // preserve file extension
143 .pipe(jsonEditor({
144 version: release.version
145 }))
146 .pipe(gulp.dest(manager.root))
147 ;
148
149 console.info('Update complete! Run "\x1b[92mgulp build\x1b[0m" to rebuild dist/ files.');
150
151 return;
152 }
153 else {
154 console.log('Current version of Semantic UI already installed');
155 return;
156 }
157
158 }
159 else {
160 console.error('Cannot locate files to update at path: ', updatePaths.definition);
161 console.log('Running installer');
162 }
163
164 }
165
166 /*--------------
167 Determine Root
168 ---------------*/
169
170 // PM that supports Build Tools (NPM Only Now)
171 if(manager.name == 'NPM') {
172 rootQuestions[0].message = rootQuestions[0].message
173 .replace('{packageMessage}', 'We detected you are using \x1b[92m' + manager.name + '\x1b[0m. Nice! ')
174 .replace('{root}', manager.root)
175 ;
176 // set default path to detected PM root
177 rootQuestions[0].default = manager.root;
178 rootQuestions[1].default = manager.root;
179
180 // insert PM questions after "Install Type" question
181 Array.prototype.splice.apply(questions.setup, [2, 0].concat(rootQuestions));
182
183 // omit cleanup questions for managed install
184 questions.cleanup = [];
185 }
186
187
188 /*--------------
189 Create SUI
190 ---------------*/
191
192 gulp.task('run setup', function() {
193
194 // If auto-install is switched on, we skip the configuration section and simply reuse the configuration from semantic.json
195 if(install.shouldAutoInstall()) {
196 answers = {
197 overwrite : 'yes',
198 install : 'auto',
199 useRoot : true,
200 semanticRoot : currentConfig.base
201 };
202 }
203 else {
204 return gulp
205 .src('gulpfile.js')
206 .pipe(prompt.prompt(questions.setup, function(setupAnswers) {
207 // hoist
208 answers = setupAnswers;
209 }))
210 ;
211 }
212 });
213
214 gulp.task('create install files', function(callback) {
215
216 /*--------------
217 Exit Conditions
218 ---------------*/
219
220 // if config exists and user specifies not to proceed
221 if(answers.overwrite !== undefined && answers.overwrite == 'no') {
222 return;
223 }
224 console.clear();
225 if(install.shouldAutoInstall()) {
226 console.log('Auto-Installing (Without User Interaction)');
227 }
228 else {
229 console.log('Installing');
230 }
231 console.log('------------------------------');
232
233
234 /*--------------
235 Paths
236 ---------------*/
237
238 var
239 installPaths = {
240 config : files.config,
241 configFolder : folders.config,
242 site : answers.site || folders.site,
243 themeConfig : files.themeConfig,
244 themeConfigFolder : folders.themeConfig
245 }
246 ;
247
248 /*--------------
249 NPM Install
250 ---------------*/
251
252 // Check if PM install
253 if(manager && (answers.useRoot || answers.customRoot)) {
254
255 // Set root to custom root path if set
256 if(answers.customRoot) {
257 if(answers.customRoot === '') {
258 console.log('Unable to proceed, invalid project root');
259 return;
260 }
261 manager.root = answers.customRoot;
262 }
263
264 // special install paths only for PM install
265 installPaths = extend(false, {}, installPaths, {
266 definition : folders.definitions,
267 lessImport : folders.lessImport,
268 tasks : folders.tasks,
269 theme : folders.themes,
270 defaultTheme : path.join(folders.themes, folders.defaultTheme),
271 themeImport : folders.themeImport
272 });
273
274 // add project root to semantic root
275 installFolder = path.join(manager.root, answers.semanticRoot);
276
277 // add install folder to all output paths
278 for(var destination in installPaths) {
279 if( installPaths.hasOwnProperty(destination) ) {
280 // config goes in project root, rest in install folder
281 installPaths[destination] = (destination == 'config' || destination == 'configFolder')
282 ? path.normalize( path.join(manager.root, installPaths[destination]) )
283 : path.normalize( path.join(installFolder, installPaths[destination]) )
284 ;
285 }
286 }
287
288 // create project folders
289 try {
290 mkdirp.sync(installFolder);
291 mkdirp.sync(installPaths.definition);
292 mkdirp.sync(installPaths.theme);
293 mkdirp.sync(installPaths.tasks);
294 }
295 catch(error) {
296 console.error('NPM does not have permissions to create folders at your specified path. Adjust your folders permissions and run "npm install" again');
297 }
298
299 console.log('Installing to \x1b[92m' + answers.semanticRoot + '\x1b[0m');
300
301 console.info('Copying UI definitions');
302 wrench.copyDirSyncRecursive(source.definitions, installPaths.definition, settings.wrench.overwrite);
303
304 console.info('Copying UI themes');
305 wrench.copyDirSyncRecursive(source.themes, installPaths.theme, settings.wrench.merge);
306 wrench.copyDirSyncRecursive(source.defaultTheme, installPaths.defaultTheme, settings.wrench.overwrite);
307
308 console.info('Copying gulp tasks');
309 wrench.copyDirSyncRecursive(source.tasks, installPaths.tasks, settings.wrench.overwrite);
310
311 // copy theme import
312 console.info('Adding theme files');
313 gulp.src(source.themeImport)
314 .pipe(plumber())
315 .pipe(gulp.dest(installPaths.themeImport))
316 ;
317 gulp.src(source.lessImport)
318 .pipe(plumber())
319 .pipe(gulp.dest(installPaths.lessImport))
320 ;
321
322 // create gulp file
323 console.info('Creating gulpfile.js');
324 gulp.src(source.userGulpFile)
325 .pipe(plumber())
326 .pipe(gulp.dest(installFolder))
327 ;
328
329 }
330
331
332 /*--------------
333 Site Theme
334 ---------------*/
335
336 // Copy _site templates folder to destination
337 if( fs.existsSync(installPaths.site) ) {
338 console.info('Site folder exists, merging files (no overwrite)', installPaths.site);
339 }
340 else {
341 console.info('Creating site theme folder', installPaths.site);
342 }
343 wrench.copyDirSyncRecursive(source.site, installPaths.site, settings.wrench.merge);
344
345 /*--------------
346 Theme Config
347 ---------------*/
348
349 gulp.task('create theme.config', function() {
350 var
351 // determine path to site theme folder from theme config
352 // force CSS path variable to use forward slashes for paths
353 pathToSite = path.relative(path.resolve(installPaths.themeConfigFolder), path.resolve(installPaths.site)).replace(/\\/g,'/'),
354 siteVariable = "@siteFolder : '" + pathToSite + "/';"
355 ;
356
357 // rewrite site variable in theme.less
358 console.info('Adjusting @siteFolder to: ', pathToSite + '/');
359
360 if(fs.existsSync(installPaths.themeConfig)) {
361 console.info('Modifying src/theme.config (LESS config)', installPaths.themeConfig);
362 return gulp.src(installPaths.themeConfig)
363 .pipe(plumber())
364 .pipe(replace(regExp.siteVariable, siteVariable))
365 .pipe(gulp.dest(installPaths.themeConfigFolder))
366 ;
367 }
368 else {
369 console.info('Creating src/theme.config (LESS config)', installPaths.themeConfig);
370 return gulp.src(source.themeConfig)
371 .pipe(plumber())
372 .pipe(rename({ extname : '' }))
373 .pipe(replace(regExp.siteVariable, siteVariable))
374 .pipe(gulp.dest(installPaths.themeConfigFolder))
375 ;
376 }
377 });
378
379 /*--------------
380 Semantic.json
381 ---------------*/
382
383 gulp.task('create semantic.json', function() {
384
385 var
386 jsonConfig = install.createJSON(answers)
387 ;
388
389 // adjust variables in theme.less
390 if( fs.existsSync(installPaths.config) ) {
391 console.info('Extending config file (semantic.json)', installPaths.config);
392 return gulp.src(installPaths.config)
393 .pipe(plumber())
394 .pipe(rename(settings.rename.json)) // preserve file extension
395 .pipe(jsonEditor(jsonConfig))
396 .pipe(gulp.dest(installPaths.configFolder))
397 ;
398 }
399 else {
400 console.info('Creating config file (semantic.json)', installPaths.config);
401 return gulp.src(source.config)
402 .pipe(plumber())
403 .pipe(rename({ extname : '' })) // remove .template from ext
404 .pipe(jsonEditor(jsonConfig))
405 .pipe(gulp.dest(installPaths.configFolder))
406 ;
407 }
408
409 });
410
411 runSequence(
412 'create theme.config',
413 'create semantic.json',
414 callback
415 );
416
417 });
418
419 gulp.task('clean up install', function() {
420
421 // Completion Message
422 if(installFolder && !install.shouldAutoInstall()) {
423 console.log('\n Setup Complete! \n Installing Peer Dependencies. \x1b[0;31mPlease refrain from ctrl + c\x1b[0m... \n After completion navigate to \x1b[92m' + answers.semanticRoot + '\x1b[0m and run "\x1b[92mgulp build\x1b[0m" to build');
424 process.exit(0);
425 }
426 else {
427 console.log('');
428 console.log('');
429 }
430
431 // If auto-install is switched on, we skip the configuration section and simply build the dependencies
432 if(install.shouldAutoInstall()) {
433 return gulp.start('build');
434 }
435 else {
436 return gulp
437 .src('gulpfile.js')
438 .pipe(prompt.prompt(questions.cleanup, function(answers) {
439 if(answers.cleanup == 'yes') {
440 del(install.setupFiles);
441 }
442 if(answers.build == 'yes') {
443 gulp.start('build');
444 }
445 }))
446 ;
447 }
448
449
450 });
451
452 runSequence(
453 'run setup',
454 'create install files',
455 'clean up install',
456 callback
457 );
458
459 };