Grunt task containing multiple spawn tasks will only run the first task - javascript

I have the following three tasks set up which use grunt.util.spawn to handle various Git tasks:
grunt.registerTask('git-add', function() {
grunt.util.spawn({
cmd : 'git',
args: ['add', '.'],
});
});
grunt.registerTask('git-commit', function(message) {
grunt.util.spawn({
cmd : 'git',
args: ['commit', '-m', message],
});
});
grunt.registerTask('git-push', function(origin, branch) {
grunt.util.spawn({
cmd : 'git',
args: ['push', origin, branch],
});
});
Running each of these individually works as aspected, so by running:
$ grunt git-add
$ grunt git-commit:"commit message"
$ grunt git-push:origin:"branch name"
I can successfully commit and push my changes. So how come when combining these 3 tasks into their own task like so, only the first task (git-add) gets run?
var target = grunt.option('target');
grunt.registerTask('push-feature', [
'git-add',
'git-commit:' + target,
'git-push:origin:feature/' + target
]);
I should be able to run $ grunt push-feature --target=12345, assuming my branch is called 12345, to have all those 3 tasks run, but only the first git-add task runs. If I remove the git-add task, the next one (git-commit) is the only task which executes.
What am I missing to just be able to have these 3 tasks run in sequence?

This might be due to async trouble.
Try to mark your tasks as async when declaring them, and use the callback option for spawn. Here's an example with your first task:
grunt.registerTask('git-add', function () {
var done = this.async(); // Set the task as async.
grunt.util.spawn({
cmd: 'git',
args: ['add', '.'] // See comment below about this line
}, done); // Add done as the second argument here.
});
Also note you have an additional comma, that may be interfering with operation:
args: ['add', '.'], // <- this comma should be dropped.

Related

Refactored watch task using gulp v4 doesn't work

I'm refactoring my gulpfile now I'm using gulp v4 and am having an issue with gulp watch not running my stylesCompileIncremental function. Any help or pointers would be much appreciated.
My refactoring includes:
Switching to using functions instead of gulp.task
Using series and parallel as per the docs
Exporting public tasks at the bottom of my gulpfile ie exports.stylesWatch = stylesWatch;
Adding callbacks in functions to tell Gulp the function is complete
The code for the affected tasks is as follows (directory paths are stored in package.json file hence pathConfig.ui... values):
// Compile only particular Sass file that has import of changed file
function stylesCompileIncremental(cb) {
sassCompile({
source: getResultedFilesList(changedFilePath),
dest: pathConfig.ui.core.sass.dest,
alsoSearchIn: [pathConfig.ui.lib.resources]
});
cb();
}
// Compile all Sass files and watch for changes
function stylesWatch(cb) {
createImportsGraph();
var watcher = gulp.watch(pathConfig.ui.core.sass.src + '**/*.scss', gulp.parallel(devServReloadStyles));
watcher.on('change', function(event) {
changedFilePath = event;
});
cb();
}
// reload css separated into own function. No callback needed as returning event stream
function reloadCss() {
return gulp.src(generateFilePath)
.pipe($.connect.reload()); // css only reload
}
function devServReloadStyles(cb) {
gulp.series(stylesCompileIncremental, reloadCss);
cb();
}
When I run gulp stylesWatch using my refactored code I get the below output (notice the stylesCompileIncremental task is not run):
So my watch tasking is successfully running but there's something wrong when the devServReloadStyles is run for the stylesCompileIncremental function to not kick in.
The original code before refactoring (when using gulp v3) is below:
// Compile only particular Sass file that has import of changed file
gulp.task('styles:compile:incremental', () => {
return sassCompile({
source: getResultedFilesList(changedFilePath),
dest: pathConfig.ui.core.sass.dest,
alsoSearchIn: [pathConfig.ui.lib.resources]
});
});
// Compile all Sass files and watch for changes
gulp.task('styles:watch', () => {
createImportsGraph();
gulp.watch(
pathConfig.ui.core.sass.src + '**/*.scss',
['devServ:reload:styles']
).on('change', event => changedFilePath = event.path);
});
// Reload the CSS links right after 'styles:compile:incremental' task is returned
gulp.task('devServ:reload:styles', ['styles:compile:incremental'], () => {
return gulp.src(generateFilePath) // css only reload
.pipe($.connect.reload());
});
The original task output when running styles:watch is this:
And this is the sassCompile variable used inside stylesCompileIncremental which I've currently not changed in anyway.
/**
* Configurable Sass compilation
* #param {Object} config
*/
const sassCompile = config => {
const sass = require('gulp-sass');
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const postProcessors = [
autoprefixer({
flexbox: 'no-2009'
})
];
return gulp.src(config.source)
.pipe($.sourcemaps.init({
loadMaps: true,
largeFile: true
}))
.pipe(sass({
includePaths: config.alsoSearchIn,
sourceMap: false,
outputStyle: 'compressed',
indentType: 'tab',
indentWidth: '1',
linefeed: 'lf',
precision: 10,
errLogToConsole: true
}))
.on('error', function (error) {
$.util.log('\x07');
$.util.log(error.message);
this.emit('end');
})
.pipe(postcss(postProcessors))
.pipe($.sourcemaps.write('.'))
.pipe(gulp.dest(config.dest));
};
UPDATE
This is due to an issue with my devServReloadStyles function, although I'm still unsure why. If I change my stylesWatch function to use the original devServ:reload:styles task stylesCompileIncremental gets run.
// Compile all Sass files and watch for changes
function stylesWatch(cb) {
createImportsGraph();
var watcher = gulp.watch(pathConfig.ui.core.sass.src + '**/*.scss', gulp.parallel('devServ:reload:styles'));
watcher.on('change', function(event) {
changedFilePath = event;
});
cb();
}
It would still be good to not use the old task and have this as a function though.
Can anybody tell me why my refactored version doesn't work and have any suggestions as to how this should look?
I've fixed this now.
gulp.series and gulp.parallel return functions so there was no need to wrap stylesCompileIncremental and reloadCss inside another function ie. devServReloadStyles.
As per Blaine's comment here.
So my function:
function devServReloadStyles(cb) {
gulp.series(stylesCompileIncremental, reloadCss);
cb();
}
Can just be assigned to a variable:
const devServReloadStyles = gulp.series(stylesCompileIncremental, reloadCss);
And my stylesWatch task is already calling devServReloadStyles:
// Compile all Sass files and watch for changes
function stylesWatch(cb) {
createImportsGraph();
var watcher = gulp.watch(pathConfig.ui.core.sass.src + '**/*.scss', gulp.parallel(devServReloadStyles));
watcher.on('change', function(event) {
changedFilePath = event;
});
cb();
}
So running gulp stylesWatch now runs the stylesCompileIncremental job (notice how devServReloadStyles doesn't show as it's not a function).

Callback or event undefined in gulp 4 watch task when using gulp.series

Inside my gulpfile I have the following task, where I would like to log the changed file's name:
gulp.task('watch', function() {
var stylesWatcher = gulp.watch(resolvePath(paths().source.scss) + '/**/*.scss', { awaitWriteFinish: true });
stylesWatcher.on('change', gulp.series('styles', function(cb) {
// Log filename here
cb();
}));
});
Keep in mind that 'styles' is a gulp task.
I have tried to log it in multiple ways, i.e. using function(path, stats) instead of function(cb), like it's shown in the gulp 4 documentation.
An example of a result I get when I console.log the path while using the path and stats parameters like in the docs is:
function done() {
d.removeListener('error', onError);
d.exit();
return cb.apply(null, arguments);
}
When I remove the 'styles' and the gulp.series though and just leave a function like this:
stylesWatcher.on('change', function(path, stats) {
console.log(path);
});
The path is indeed logged in the console.
Is there any way to console log the path when using gulp.series?

Grunt-scaffold after() function access to prompt answers

The docs for the NPM package grunt-scaffold lacked any information really on its after() property/function.. I have a grunt file which creates a new directory for a new script and copies boilerplate files into it from the designated template folder.. The desire is to finish the grunt scaffold:new_script command and have it log out the location of the newly generated folder.
Gruntfile.js
module.exports = function(grunt) {
grunt.initConfig({
scaffold: {
new_script: {
options: {
questions: [{
name: 'script_name',
type: 'input',
message: 'Script name, catterpillar_case?(e.g. \'new_script\'):'
}],
template: {
"scripts/etl_template/": "scripts/{{script_name}}/",
},
after: function(){
console.log("New script generated in new folder scripts/{{script_name}}")
}
}
}
}
});
grunt.loadNpmTasks('grunt-scaffold');
grunt.registerTask('default', ['scaffold']);
};
However, the ouput is
-bash-4.1$ grunt scaffold:new_script
Running "scaffold:new_script" (scaffold) task
? Script name, catterpillar_case?(e.g. 'new_script'): test_grunt
New script generated in new folder scripts/{{script_name}}
Done.
This did not do the string replacing as it did when it created the scripts/test_grunt folder! As you can see the documentation almost doesn't exist for that after() functionality, and I'm wondering if I can use javascript"system argume
An example was not given in the documentation for the after() function, but if you use the same result parameter as in the example given for filter(), you can access the answer values via their names.
Gruntfile.js
module.exports = function(grunt) {
grunt.initConfig({
scaffold: {
new_script: {
options: {
questions: [{
name: 'script_name',
type: 'input',
message: 'Script name, catterpillar_case?(e.g. \'new_script\'):'
}],
template: {
"scripts/etl_template/": "scripts/{{script_name}}/",
},
after: function(result){
console.log("New script generated in new folder scripts/" + result.script_name)
}
}
}
}
});
grunt.loadNpmTasks('grunt-scaffold');
grunt.registerTask('default', ['scaffold']);
};
Output
-bash-4.1$ grunt scaffold:new_script
Running "scaffold:new_script" (scaffold) task
? Script name, catterpillar_case?(e.g. 'new_script'): test_grunt
New script generated in new folder scripts/test_grunt
Done.

Defining custom Grunt tasks and chain them with others

I have the following Gruntfile.js:
module.exports = function(grunt) {
var config = {
shell: {
...
},
copy: {
...
}
};
grunt.initConfig(config);
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-shell');
grunt.registerTask('default', ['shell:compile', 'copy:jsfiles']);
};
I am using grunt-contrib-x components which can be configured and then registered in a task as part of a chain.
What about a custom task?
I need to add another task whose work is performed by a function:
var customTask = function() {
// This will do something...
};
I need to run it after shell:compile and copy:jsfiles as part of another task, and also in other chains. I would like to have the same pattern and being able to do something like:
module.exports = function(grunt) {
var config = {
shell: { ... }, copy: { ... },
customTask: function() {
// Doing stuff
}
};
// ... some code ...
grunt.registerTask('default', ['shell:compile', 'copy:jsfiles']);
grunt.registerTask('advanced', ['shell:compile', 'copy:jsfiles', 'customTask']);
grunt.registerTask('advanced2', ['shell:compileComponent', 'copy:jsfilesComponent', 'customTask']);
};
The goal is having the possibility to create task chains and include my custom task as part of a list of sequential tasks to be executed.
How can I achieve this?
Call grunt.registerTask and pass in a name as the first argument and the function to run as the last argument.
grunt.registerTask('myTask', function () {
//do some stuff
});
Then you can chain it
grunt.registerTask('advanced', ['shell:compile', 'copy:jsfiles', 'myTask']);
Basically it's the same as in your example, except you define your custom task as a parameter to grunt.registerTask, not as a property in the config.
http://gruntjs.com/creating-tasks#custom-tasks

Gruntfile getting error codes from programs serially

I want to create a grunt file that runs 3 grunt tasks serially one after another regardless of whether they fail or pass. If one of the grunts task fails, I want to return the last error code.
I tried:
grunt.task.run('task1', 'task2', 'task3');
with the --force option when running.
The problem with this is that when --force is specified it returns errorcode 0 regardless of errors.
Thanks
Use grunt.util.spawn: http://gruntjs.com/api/grunt.util#grunt.util.spawn
grunt.registerTask('serial', function() {
var done = this.async();
var tasks = {'task1': 0, 'task2': 0, 'task3': 0};
grunt.util.async.forEachSeries(Object.keys(tasks), function(task, next) {
grunt.util.spawn({
grunt: true, // use grunt to spawn
args: [task], // spawn this task
opts: { stdio: 'inherit' }, // print to the same stdout
}, function(err, result, code) {
tasks[task] = code;
next();
});
}, function() {
// Do something with tasks now that each
// contains their respective error code
done();
});
});

Categories

Resources