This patch adds the gulp command of `validate-commit-messages` which will validate the range of commits messages present in the active branch. This check now runs on CI as part of the linting checks. Allowed commit message types and scopes are controlled via commit-message.json file and documented at https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-guidelines This solution is based on old Vojta's code that he wrote for angular/angular.js, that was later adjusted by @matsko in #13815. Ideally we should switch over to something like https://www.npmjs.com/package/commitplease as suggested in #9953 but that package currently doesn't support strict scope checking, which is one of the primarily goal of this PR. Note that this PR removes support for "chore" which was previously overused by everyone on the team. Closes #13815 Fixes #3337
269 lines
8.6 KiB
JavaScript
269 lines
8.6 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE
|
|
// This is to ensure that we catch env issues before we error while requiring other dependencies.
|
|
require('./tools/check-environment')({
|
|
requiredNpmVersion: '>=3.5.3 <4.0.0',
|
|
requiredNodeVersion: '>=5.4.1 <7.0.0',
|
|
});
|
|
|
|
const gulp = require('gulp');
|
|
const path = require('path');
|
|
const os = require('os');
|
|
|
|
// clang-format entry points
|
|
const srcsToFmt = [
|
|
'modules/@angular/**/*.{js,ts}',
|
|
'modules/benchmarks/**/*.{js,ts}',
|
|
'modules/e2e_util/**/*.{js,ts}',
|
|
'modules/playground/**/*.{js,ts}',
|
|
'tools/**/*.{js,ts}',
|
|
'!tools/public_api_guard/**/*.d.ts',
|
|
'./*.{js,ts}',
|
|
'!shims_for_IE.js',
|
|
];
|
|
|
|
// Check source code for formatting errors (clang-format)
|
|
gulp.task('format:enforce', () => {
|
|
const format = require('gulp-clang-format');
|
|
const clangFormat = require('clang-format');
|
|
return gulp.src(srcsToFmt).pipe(
|
|
format.checkFormat('file', clangFormat, {verbose: true, fail: true}));
|
|
});
|
|
|
|
// Format the source code with clang-format (see .clang-format)
|
|
gulp.task('format', () => {
|
|
const format = require('gulp-clang-format');
|
|
const clangFormat = require('clang-format');
|
|
return gulp.src(srcsToFmt, {base: '.'})
|
|
.pipe(format.format('file', clangFormat))
|
|
.pipe(gulp.dest('.'));
|
|
});
|
|
|
|
const entrypoints = [
|
|
'dist/packages-dist/core/index.d.ts',
|
|
'dist/packages-dist/core/testing/index.d.ts',
|
|
'dist/packages-dist/common/index.d.ts',
|
|
'dist/packages-dist/common/testing/index.d.ts',
|
|
// The API surface of the compiler is currently unstable - all of the important APIs are exposed
|
|
// via @angular/core, @angular/platform-browser or @angular/platform-browser-dynamic instead.
|
|
//'dist/packages-dist/compiler/index.d.ts',
|
|
//'dist/packages-dist/compiler/testing.d.ts',
|
|
'dist/packages-dist/upgrade/index.d.ts',
|
|
'dist/packages-dist/upgrade/static.d.ts',
|
|
'dist/packages-dist/platform-browser/index.d.ts',
|
|
'dist/packages-dist/platform-browser/testing/index.d.ts',
|
|
'dist/packages-dist/platform-browser-dynamic/index.d.ts',
|
|
'dist/packages-dist/platform-browser-dynamic/testing/index.d.ts',
|
|
'dist/packages-dist/platform-webworker/index.d.ts',
|
|
'dist/packages-dist/platform-webworker-dynamic/index.d.ts',
|
|
'dist/packages-dist/platform-server/index.d.ts',
|
|
'dist/packages-dist/platform-server/testing/index.d.ts',
|
|
'dist/packages-dist/http/index.d.ts',
|
|
'dist/packages-dist/http/testing/index.d.ts',
|
|
'dist/packages-dist/forms/index.d.ts',
|
|
'dist/packages-dist/router/index.d.ts',
|
|
];
|
|
const publicApiDir = path.normalize('tools/public_api_guard');
|
|
const publicApiArgs = [
|
|
'--rootDir',
|
|
'dist/packages-dist',
|
|
'--stripExportPattern',
|
|
'^__',
|
|
'--allowModuleIdentifiers',
|
|
'jasmine',
|
|
'--allowModuleIdentifiers',
|
|
'protractor',
|
|
'--allowModuleIdentifiers',
|
|
'angular',
|
|
'--onStabilityMissing',
|
|
'error',
|
|
].concat(entrypoints);
|
|
|
|
// Build angular
|
|
gulp.task('build.sh', (done) => {
|
|
const childProcess = require('child_process');
|
|
|
|
childProcess.exec(path.join(__dirname, 'build.sh'), done);
|
|
});
|
|
|
|
// Enforce that the public API matches the golden files
|
|
// Note that these two commands work on built d.ts files instead of the source
|
|
gulp.task('public-api:enforce', (done) => {
|
|
const childProcess = require('child_process');
|
|
|
|
childProcess
|
|
.spawn(
|
|
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
|
|
['--verifyDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
|
.on('close', (errorCode) => {
|
|
if (errorCode !== 0) {
|
|
done(new Error(
|
|
'Public API differs from golden file. Please run `gulp public-api:update`.'));
|
|
} else {
|
|
done();
|
|
}
|
|
});
|
|
});
|
|
|
|
// Generate the public API golden files
|
|
gulp.task('public-api:update', ['build.sh'], (done) => {
|
|
const childProcess = require('child_process');
|
|
|
|
childProcess
|
|
.spawn(
|
|
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
|
|
['--outDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
|
.on('close', done);
|
|
});
|
|
|
|
// Check the coding standards and programming errors
|
|
gulp.task('lint', ['format:enforce', 'tools:build', 'validate-commit-messages'], () => {
|
|
const tslint = require('gulp-tslint');
|
|
// Built-in rules are at
|
|
// https://palantir.github.io/tslint/rules/
|
|
const tslintConfig = require('./tslint.json');
|
|
return gulp
|
|
.src([
|
|
// todo(vicb): add .js files when supported
|
|
// see https://github.com/palantir/tslint/pull/1515
|
|
'./modules/**/*.ts',
|
|
'./tools/**/*.ts',
|
|
'./*.ts',
|
|
|
|
// Ignore TypeScript mocks because it's not managed by us
|
|
'!./tools/@angular/tsc-wrapped/test/typescript.mocks.ts',
|
|
|
|
// Ignore generated files due to lack of copyright header
|
|
// todo(alfaproject): make generated files lintable
|
|
'!**/*.d.ts',
|
|
'!**/*.ngfactory.ts',
|
|
])
|
|
.pipe(tslint({
|
|
tslint: require('tslint').default,
|
|
configuration: tslintConfig,
|
|
formatter: 'prose',
|
|
}))
|
|
.pipe(tslint.report({emitError: true}));
|
|
});
|
|
|
|
gulp.task('validate-commit-messages', () => {
|
|
const validateCommitMessage = require('./tools/validate-commit-message');
|
|
const childProcess = require('child_process');
|
|
|
|
// We need to fetch origin explicitly because it might be stale.
|
|
// I couldn't find a reliable way to do this without fetch.
|
|
childProcess.exec(
|
|
'git fetch origin master && git log --reverse --format=%s HEAD ^origin/master',
|
|
(error, stdout, stderr) => {
|
|
if (error) {
|
|
console.log(stderr);
|
|
process.exit(1);
|
|
}
|
|
|
|
let someCommitsInvalid = false;
|
|
let commitsByLine = stdout.trim().split(/\n/);
|
|
|
|
console.log(`Examining ${commitsByLine.length} commits between HEAD and master`);
|
|
|
|
if (commitsByLine.length == 0) {
|
|
console.log('There are zero new commits between this HEAD and master');
|
|
}
|
|
|
|
someCommitsInvalid = !commitsByLine.every(validateCommitMessage);
|
|
|
|
if (someCommitsInvalid) {
|
|
console.log('Please fix the failing commit messages before continuing...');
|
|
console.log(
|
|
'Commit message guidelines: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-guidelines');
|
|
process.exit(1);
|
|
}
|
|
});
|
|
});
|
|
|
|
gulp.task('tools:build', (done) => { tsc('tools/', done); });
|
|
|
|
// Check for circular dependency in the source code
|
|
gulp.task('check-cycle', (done) => {
|
|
const madge = require('madge');
|
|
|
|
const dependencyObject = madge(['dist/all/'], {
|
|
format: 'cjs',
|
|
extensions: ['.js'],
|
|
onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, '//'); }
|
|
});
|
|
const circularDependencies = dependencyObject.circular().getArray();
|
|
if (circularDependencies.length > 0) {
|
|
console.log('Found circular dependencies!');
|
|
console.log(circularDependencies);
|
|
process.exit(1);
|
|
}
|
|
done();
|
|
});
|
|
|
|
// Serve the built files
|
|
gulp.task('serve', () => {
|
|
const connect = require('gulp-connect');
|
|
const cors = require('cors');
|
|
|
|
connect.server({
|
|
root: `${__dirname}/dist`,
|
|
port: 8000,
|
|
livereload: false,
|
|
open: false,
|
|
middleware: (connect, opt) => [cors()],
|
|
});
|
|
});
|
|
|
|
// Serve the examples
|
|
gulp.task('serve-examples', () => {
|
|
const connect = require('gulp-connect');
|
|
const cors = require('cors');
|
|
|
|
connect.server({
|
|
root: `${__dirname}/dist/examples`,
|
|
port: 8001,
|
|
livereload: false,
|
|
open: false,
|
|
middleware: (connect, opt) => [cors()],
|
|
});
|
|
});
|
|
|
|
|
|
// Update the changelog with the latest changes
|
|
gulp.task('changelog', () => {
|
|
const conventionalChangelog = require('gulp-conventional-changelog');
|
|
|
|
return gulp.src('CHANGELOG.md')
|
|
.pipe(conventionalChangelog({preset: 'angular', releaseCount: 1}, {
|
|
// Conventional Changelog Context
|
|
// We have to manually set version number so it doesn't get prefixed with `v`
|
|
// See https://github.com/conventional-changelog/conventional-changelog-core/issues/10
|
|
currentTag: require('./package.json').version
|
|
}))
|
|
.pipe(gulp.dest('./'));
|
|
});
|
|
|
|
function tsc(projectPath, done) {
|
|
const childProcess = require('child_process');
|
|
|
|
childProcess
|
|
.spawn(
|
|
path.normalize(platformScriptPath(`${__dirname}/node_modules/.bin/tsc`)),
|
|
['-p', path.join(__dirname, projectPath)], {stdio: 'inherit'})
|
|
.on('close', done);
|
|
}
|
|
|
|
// returns the script path for the current platform
|
|
function platformScriptPath(path) {
|
|
return /^win/.test(os.platform()) ? `${path}.cmd` : path;
|
|
}
|