运行 仅在 src 发生变化时构建
Run build only if there are changes in src
故事:
我们有一个测试人员团队致力于使用量角器为我们的内部 AngularJS 应用程序自动化端到端测试。这是他们通常 运行 用于 "local" 测试的任务:
grunt.registerTask('e2e:local', [
'build:prod',
'connect:test',
'protractor:local'
]);
它 运行 是 "build" 任务,启动网络服务器并 运行 是针对本地构建的 e2e 测试。
build:prod
任务本身定义为:
grunt.registerTask(
'build:prod', [
'clean',
'copy:all',
'copy:assets',
'wiredep',
'ngtemplates',
'useminPrepare',
'concat',
'ngAnnotate',
'autoprefixer',
'uglify',
'cssmin',
'copy:cssfix',
'usemin',
'copy:html',
'bowercopy',
'template:setProdVersion'
]
);
这里我们有很多子任务(它肯定可以改进,但这是现在的样子)。
问题:
目前,构建完成大约需要 25 秒。并且,每次有人 运行 进行端到端测试时,都会执行构建任务。
问题:
只有 src
目录发生变化时,我如何才能 运行 build:prod
任务?
请注意,这里的要求是对运行 测试的测试人员透明。我不希望他们记住什么时候需要执行构建,什么时候不需要。
换句话说,这个过程应该是自动化的。目标是自动检测是否需要构建。
请注意,理想情况下,我希望按原样保留构建任务,这样如果通过 grunt build:prod
直接调用它,它就会重建,而不管之前构建的日期戳。
思考与尝试:
有密切相关的grunt-newer
package,但是,由于我们有一个相当复杂的构建,开始时有一个clean
任务,我不确定如何在我的案例中应用它
我也在考虑的是,在e2e:local
任务中,手动检查dist
和src
中文件的时间戳,并根据在此基础上,决定是否需要调用 build:prod
。我认为这就是 grunt-newer
在内部所做的事情
我们开始使用 jit-grunt
这有助于提高性能
如果您使用 git,这里有一个想法:
如何使用 grunt-gitinfo 之类的东西并使用 HEAD 中的最后一次提交作为基础?
想法是:
- 您创建了一个新的 g运行t 任务来检查最新的提交哈希
- 您将此提交哈希保存在添加到
gitignore
的文件中(并且不在 clean
文件夹中,通常可以在 repo 的根目录中)
- 在保存到文件之前,它会检查其中已有的值(标准节点
fs
模块可以轻松完成 read/write)
- 如果哈希不匹配,运行
build:prod
任务然后保存新的提交哈希
- 测试人员构建将取决于您的新任务,而不是
build:prod
直接
另一个选项(仍然使用git):
您可以使用 grunt-githooks 之类的东西并创建一个 git 挂钩,在拉取后 运行s 并调用 git build:prod
,然后您可以从测试人员 运行.
的 g运行t 任务的依赖项中删除它
您可能有另一个代码来检查 git 挂钩并在需要时安装它,这对于测试人员来说可能是一次性的额外步骤,或者可能已融入 g运行t他们调用的任务。
我没有使用量角器的经验,但从概念上讲我认为这可行。
我的建议是在您的 ~/.cshrc 中为 运行 构建命令设置别名,仅当 diff
命令 returns 为真时。
#./cshrc
alias build_on_diff 'diff -r branch_dir_1 branch_dir_2\
if ( $status == 1 ) then\
build:prod\
endif'
只需将 diff
命令替换为 git 使用的任何命令,只要 returns 检测到差异的状态为 1,它就应该可以工作。我们在我的工作场所应用了类似的方法来避免重建未更改的文件。
用 grunt
不是你要找的答案,但用 gulp
会很容易。
var fs = require('fs');
var gulpif = require('gulp-if');
var sourceChanged = fs.statSync('build/directory').mtime > fs.statSync('source/directory').mtime;
gulp.task('build:prod', function() {
if (!sourceChanged) {
return false;
}
return gulp.src('./src/*.js')
.pipe(.... build ....)
.pipe(gulp.dest('./dist/'));
});
以下是我们如何为我们的构建完成一些 Git HEAD sha 工作。我们使用它来确定当前部署到我们的生产环境的版本 - 但我很确定您可以将其重新处理为 return 布尔值并在为真时触发构建。
Gruntfile.js
function getHeadSha() {
var curr, match, next = 'HEAD';
var repoDir = process.env.GIT_REPO_DIR || path.join(__dirname, '..');
try {
do {
curr = grunt.file.read(path.join(repoDir, '.git', next)).trim();
match = curr.match(/^ref: (.+)$/);
next = match && match[1];
} while (next);
} catch(ex) {
curr = 'not-found';
}
return curr;
}
grunt.initConfig({
replace: {
applicationVersion: {
src: '<%= config.dist %>/index.html',
overwrite: true,
replacements: [{
from: '{{APPLICATION_VERSION}}',
to: getHeadSha
}]
}
}
});
grunt.registerTask('build', {
'replace:applicationVersion',
/** other tasks **/
});
grunt.registerTask('e2e:local', {
'check_if_we_should_build',
/** other tasks **/
});
index.html
<html data-version="{{APPLICATION_VERSION}}">
<!-- -->
</html>
还有 git-info 包可以简化整个过程,我们正在考虑自己切换到它。
编辑; 我刚刚注意到@meligy 已经向您指出了 git-info 的方向。信用到期的信用。
我很惊讶没有人提到 grunt-contrib-watch yet (it's in the gruntjs.com 示例文件,我认为它是众所周知的!)。来自 github:"Run predefined tasks whenever watched file patterns are added, changed or deleted." - 这是一个示例 g运行t 文件,只要在 src/ 或 test/ 中修改任何 .js 文件,它就会 运行 您的任务,或者如果修改了 G运行t 文件。
var filesToWatch = ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'];
grunt.initConfig({
watch: {
files: filesToWatch,
tasks: ['build:prod',
'connect:test',
'protractor:local']
}
});
grunt.loadNpmTasks('grunt-contrib-watch');
您让您的开发人员在开始修改文件之前打开一个终端和 运行 grunt watch
,每次修改这些文件时,任务将自动 运行(不再进行每次都返回到 运行 grunt build:prod
的终端。
这是一个很棒的包,我建议你看看。 -- github -- npmjs.org
npm install grunt-contrib-watch --save-dev
我不确定它是否有帮助,但我们在我们的项目中使用 GULP framework 完成了同样的事情。我们在 gulp 中编写了一个持续检查源更改的观察程序,并 运行 一个快速构建项目的函数。它是量角器测试用例。
gulp.task('dome', function () {
gulp.src(["maintest.js"])
.pipe(notify("Change Found , Executing Scripts."))
.pipe(protractor({
configFile: "conf.js",
args: ['--baseUrl', 'http://127.0.0.1:8000']
})).on('error', function (e) {
throw e
});
})
gulp.task('default', function () {
gulp.watch('./webpages/*.js', ['dome']);
gulp.watch('maintest.js', ['dome']);
gulp.watch('conf.js', ['dome']);
});
故事:
我们有一个测试人员团队致力于使用量角器为我们的内部 AngularJS 应用程序自动化端到端测试。这是他们通常 运行 用于 "local" 测试的任务:
grunt.registerTask('e2e:local', [
'build:prod',
'connect:test',
'protractor:local'
]);
它 运行 是 "build" 任务,启动网络服务器并 运行 是针对本地构建的 e2e 测试。
build:prod
任务本身定义为:
grunt.registerTask(
'build:prod', [
'clean',
'copy:all',
'copy:assets',
'wiredep',
'ngtemplates',
'useminPrepare',
'concat',
'ngAnnotate',
'autoprefixer',
'uglify',
'cssmin',
'copy:cssfix',
'usemin',
'copy:html',
'bowercopy',
'template:setProdVersion'
]
);
这里我们有很多子任务(它肯定可以改进,但这是现在的样子)。
问题:
目前,构建完成大约需要 25 秒。并且,每次有人 运行 进行端到端测试时,都会执行构建任务。
问题:
只有 src
目录发生变化时,我如何才能 运行 build:prod
任务?
请注意,这里的要求是对运行 测试的测试人员透明。我不希望他们记住什么时候需要执行构建,什么时候不需要。
换句话说,这个过程应该是自动化的。目标是自动检测是否需要构建。
请注意,理想情况下,我希望按原样保留构建任务,这样如果通过 grunt build:prod
直接调用它,它就会重建,而不管之前构建的日期戳。
思考与尝试:
有密切相关的
grunt-newer
package,但是,由于我们有一个相当复杂的构建,开始时有一个clean
任务,我不确定如何在我的案例中应用它我也在考虑的是,在
e2e:local
任务中,手动检查dist
和src
中文件的时间戳,并根据在此基础上,决定是否需要调用build:prod
。我认为这就是grunt-newer
在内部所做的事情我们开始使用
jit-grunt
这有助于提高性能
如果您使用 git,这里有一个想法:
如何使用 grunt-gitinfo 之类的东西并使用 HEAD 中的最后一次提交作为基础?
想法是:
- 您创建了一个新的 g运行t 任务来检查最新的提交哈希
- 您将此提交哈希保存在添加到
gitignore
的文件中(并且不在clean
文件夹中,通常可以在 repo 的根目录中) - 在保存到文件之前,它会检查其中已有的值(标准节点
fs
模块可以轻松完成 read/write) - 如果哈希不匹配,运行
build:prod
任务然后保存新的提交哈希 - 测试人员构建将取决于您的新任务,而不是
build:prod
直接
另一个选项(仍然使用git):
您可以使用 grunt-githooks 之类的东西并创建一个 git 挂钩,在拉取后 运行s 并调用 git build:prod
,然后您可以从测试人员 运行.
您可能有另一个代码来检查 git 挂钩并在需要时安装它,这对于测试人员来说可能是一次性的额外步骤,或者可能已融入 g运行t他们调用的任务。
我没有使用量角器的经验,但从概念上讲我认为这可行。
我的建议是在您的 ~/.cshrc 中为 运行 构建命令设置别名,仅当 diff
命令 returns 为真时。
#./cshrc
alias build_on_diff 'diff -r branch_dir_1 branch_dir_2\
if ( $status == 1 ) then\
build:prod\
endif'
只需将 diff
命令替换为 git 使用的任何命令,只要 returns 检测到差异的状态为 1,它就应该可以工作。我们在我的工作场所应用了类似的方法来避免重建未更改的文件。
用 grunt
不是你要找的答案,但用 gulp
会很容易。
var fs = require('fs');
var gulpif = require('gulp-if');
var sourceChanged = fs.statSync('build/directory').mtime > fs.statSync('source/directory').mtime;
gulp.task('build:prod', function() {
if (!sourceChanged) {
return false;
}
return gulp.src('./src/*.js')
.pipe(.... build ....)
.pipe(gulp.dest('./dist/'));
});
以下是我们如何为我们的构建完成一些 Git HEAD sha 工作。我们使用它来确定当前部署到我们的生产环境的版本 - 但我很确定您可以将其重新处理为 return 布尔值并在为真时触发构建。
Gruntfile.js
function getHeadSha() {
var curr, match, next = 'HEAD';
var repoDir = process.env.GIT_REPO_DIR || path.join(__dirname, '..');
try {
do {
curr = grunt.file.read(path.join(repoDir, '.git', next)).trim();
match = curr.match(/^ref: (.+)$/);
next = match && match[1];
} while (next);
} catch(ex) {
curr = 'not-found';
}
return curr;
}
grunt.initConfig({
replace: {
applicationVersion: {
src: '<%= config.dist %>/index.html',
overwrite: true,
replacements: [{
from: '{{APPLICATION_VERSION}}',
to: getHeadSha
}]
}
}
});
grunt.registerTask('build', {
'replace:applicationVersion',
/** other tasks **/
});
grunt.registerTask('e2e:local', {
'check_if_we_should_build',
/** other tasks **/
});
index.html
<html data-version="{{APPLICATION_VERSION}}">
<!-- -->
</html>
还有 git-info 包可以简化整个过程,我们正在考虑自己切换到它。
编辑; 我刚刚注意到@meligy 已经向您指出了 git-info 的方向。信用到期的信用。
我很惊讶没有人提到 grunt-contrib-watch yet (it's in the gruntjs.com 示例文件,我认为它是众所周知的!)。来自 github:"Run predefined tasks whenever watched file patterns are added, changed or deleted." - 这是一个示例 g运行t 文件,只要在 src/ 或 test/ 中修改任何 .js 文件,它就会 运行 您的任务,或者如果修改了 G运行t 文件。
var filesToWatch = ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'];
grunt.initConfig({
watch: {
files: filesToWatch,
tasks: ['build:prod',
'connect:test',
'protractor:local']
}
});
grunt.loadNpmTasks('grunt-contrib-watch');
您让您的开发人员在开始修改文件之前打开一个终端和 运行 grunt watch
,每次修改这些文件时,任务将自动 运行(不再进行每次都返回到 运行 grunt build:prod
的终端。
这是一个很棒的包,我建议你看看。 -- github -- npmjs.org
npm install grunt-contrib-watch --save-dev
我不确定它是否有帮助,但我们在我们的项目中使用 GULP framework 完成了同样的事情。我们在 gulp 中编写了一个持续检查源更改的观察程序,并 运行 一个快速构建项目的函数。它是量角器测试用例。
gulp.task('dome', function () {
gulp.src(["maintest.js"])
.pipe(notify("Change Found , Executing Scripts."))
.pipe(protractor({
configFile: "conf.js",
args: ['--baseUrl', 'http://127.0.0.1:8000']
})).on('error', function (e) {
throw e
});
})
gulp.task('default', function () {
gulp.watch('./webpages/*.js', ['dome']);
gulp.watch('maintest.js', ['dome']);
gulp.watch('conf.js', ['dome']);
});