mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
build: remove gulp publish tasks (#14594)
* The gulp publish script has been replaced with a standalone NodeJS script. This just made a lot of things easier and also could work with Bazel in the future.
This commit is contained in:
committed by
Jeremy Elbourn
parent
b12872e63e
commit
fe0c2a8ca4
@@ -28,7 +28,3 @@ import './tasks/lint';
|
||||
import './tasks/material-release';
|
||||
import './tasks/unit-test';
|
||||
import './tasks/universal';
|
||||
|
||||
import './tasks/publish/publish-task';
|
||||
import './tasks/publish/sanity-checks';
|
||||
import './tasks/publish/validate-release';
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import {spawnSync} from 'child_process';
|
||||
import {buildConfig} from 'material2-build-tools';
|
||||
|
||||
/** Regular expression that matches version names and the individual version segments. */
|
||||
export const versionNameRegex = /^(\d+)\.(\d+)\.(\d+)(?:-(alpha|beta|rc)\.(\d)+)?$/;
|
||||
|
||||
/** Checks if the specified version can be released from the current Git branch. */
|
||||
export function checkPublishBranch(version: string) {
|
||||
const versionType = getSemverVersionType(version);
|
||||
const branchName = spawnSync('git', ['symbolic-ref', '--short', 'HEAD'],
|
||||
{cwd: buildConfig.projectDir}).stdout.toString().trim();
|
||||
|
||||
|
||||
// TODO(devversion): also check the the local branch's HEAD sha matches upstream.
|
||||
// TODO(devversion): also check that the version is a single increment of the previous release.
|
||||
// TODO(devversion): minor releases can also be published from master if there isn't a minor
|
||||
// branch for that major range yet.
|
||||
const [major, minor] = version.split('.');
|
||||
|
||||
let expectedBranch = '';
|
||||
if (versionType === 'major') {
|
||||
expectedBranch = 'master';
|
||||
} else if (versionType === 'minor') {
|
||||
expectedBranch = `${major}.x`;
|
||||
} else if (versionType === 'patch') {
|
||||
expectedBranch = `${major}.${minor}.x`;
|
||||
}
|
||||
|
||||
if (branchName !== expectedBranch) {
|
||||
throw `A ${versionType} release must be done from the ${expectedBranch} branch.`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the type of the specified semver version. Can be either a major, minor or
|
||||
* patch version.
|
||||
*/
|
||||
export function getSemverVersionType(version: string): 'major' | 'minor' | 'patch' {
|
||||
const versionNameMatch = version.match(versionNameRegex);
|
||||
|
||||
if (!versionNameMatch) {
|
||||
throw `Could not parse version: ${version}. Cannot properly determine version type.`;
|
||||
}
|
||||
|
||||
const versionDigits = versionNameMatch.slice(1, 4);
|
||||
|
||||
if (versionDigits[1] === '0' && versionDigits[2] === '0') {
|
||||
return 'major';
|
||||
} else if (versionDigits[2] === '0') {
|
||||
return 'minor';
|
||||
} else {
|
||||
return 'patch';
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
import {green, grey, yellow} from 'chalk';
|
||||
import {spawn} from 'child_process';
|
||||
import {existsSync, statSync} from 'fs-extra';
|
||||
import {task} from 'gulp';
|
||||
import {buildConfig, sequenceTask} from 'material2-build-tools';
|
||||
import * as minimist from 'minimist';
|
||||
import {join} from 'path';
|
||||
import {execTask} from '../../util/task-helpers';
|
||||
|
||||
/** Packages that will be published to NPM by the release task. */
|
||||
export const releasePackages = [
|
||||
'cdk',
|
||||
'material',
|
||||
'cdk-experimental',
|
||||
'material-experimental',
|
||||
'material-moment-adapter'
|
||||
];
|
||||
|
||||
/** Parse command-line arguments for release task. */
|
||||
const argv = minimist(process.argv.slice(3));
|
||||
|
||||
task('publish', sequenceTask(
|
||||
':publish:sanity-checks',
|
||||
':publish:whoami',
|
||||
':publish:build-releases',
|
||||
'validate-release:check-bundles',
|
||||
':publish',
|
||||
':publish:logout',
|
||||
));
|
||||
|
||||
/** Task that builds all releases that will be published. */
|
||||
task(':publish:build-releases', sequenceTask(
|
||||
'clean',
|
||||
releasePackages.map(packageName => `${packageName}:build-release`)
|
||||
));
|
||||
|
||||
/** Make sure we're logged in. */
|
||||
task(':publish:whoami', execTask('npm', ['whoami'], {
|
||||
silent: true,
|
||||
errMessage: 'You must be logged in to publish.'
|
||||
}));
|
||||
|
||||
task(':publish:logout', execTask('npm', ['logout']));
|
||||
|
||||
task(':publish', async () => {
|
||||
const tag = argv['tag'];
|
||||
const version = buildConfig.projectVersion;
|
||||
const currentDir = process.cwd();
|
||||
|
||||
console.log();
|
||||
if (!tag) {
|
||||
console.log(grey('> You can specify the tag by passing --tag=labelName.\n'));
|
||||
console.log(green(`Publishing version "${version}" to the latest tag...`));
|
||||
} else {
|
||||
console.log(yellow(`Publishing version "${version}" to the ${tag} tag...`));
|
||||
}
|
||||
console.log();
|
||||
|
||||
if (releasePackages.length > 1) {
|
||||
console.warn(yellow('Warning: Multiple packages will be released.'));
|
||||
console.warn(yellow('Warning: Packages to be released:', releasePackages.join(', ')));
|
||||
console.warn();
|
||||
}
|
||||
|
||||
console.log(yellow('> Make sure to check the "requiredAngularVersion" in the package.json.'));
|
||||
console.log(yellow('> The version in the config defines the peer dependency of Angular.'));
|
||||
console.log();
|
||||
|
||||
// Iterate over every declared release package and publish it on NPM.
|
||||
for (const packageName of releasePackages) {
|
||||
await _execNpmPublish(tag, packageName);
|
||||
}
|
||||
|
||||
process.chdir(currentDir);
|
||||
});
|
||||
|
||||
function _execNpmPublish(tag: string, packageName: string): Promise<{}> | undefined {
|
||||
const packageDir = join(buildConfig.outputDir, 'releases', packageName);
|
||||
|
||||
if (!statSync(packageDir).isDirectory()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!existsSync(join(packageDir, 'package.json'))) {
|
||||
throw new Error(`"${packageDir}" does not have a package.json.`);
|
||||
}
|
||||
|
||||
if (!existsSync(join(packageDir, 'LICENSE'))) {
|
||||
throw new Error(`"${packageDir}" does not have a LICENSE file`);
|
||||
}
|
||||
|
||||
process.chdir(packageDir);
|
||||
console.log(green(`Publishing ${packageName}...`));
|
||||
|
||||
const command = 'npm';
|
||||
const args = ['publish', '--access', 'public'];
|
||||
|
||||
if (tag) {
|
||||
args.push('--tag', tag);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(grey(`Executing: ${command} ${args.join(' ')}`));
|
||||
if (argv['dry']) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
const childProcess = spawn(command, args);
|
||||
childProcess.stdout.on('data', (data: Buffer) => {
|
||||
console.log(` stdout: ${data.toString().split(/[\n\r]/g).join('\n ')}`);
|
||||
});
|
||||
childProcess.stderr.on('data', (data: Buffer) => {
|
||||
console.error(` stderr: ${data.toString().split(/[\n\r]/g).join('\n ')}`);
|
||||
});
|
||||
|
||||
childProcess.on('close', (code: number) => {
|
||||
if (code == 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`Could not publish ${packageName}, status: ${code}.`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import {red} from 'chalk';
|
||||
import {spawnSync} from 'child_process';
|
||||
import {task} from 'gulp';
|
||||
import {buildConfig} from 'material2-build-tools';
|
||||
import * as minimist from 'minimist';
|
||||
import {checkPublishBranch, versionNameRegex} from './branch-check';
|
||||
|
||||
const {projectDir, projectVersion} = buildConfig;
|
||||
|
||||
/** Git repository URL that has been read out from the project package.json file. */
|
||||
const repositoryGitUrl = require('../../../../package.json').repository.url;
|
||||
|
||||
/** Parse command-line arguments for release task. */
|
||||
const argv = minimist(process.argv.slice(3));
|
||||
|
||||
/** Task that runs various sanity checks before publishing. */
|
||||
task(':publish:sanity-checks', [
|
||||
':publish:check-project-version',
|
||||
':publish:check-remote-tag',
|
||||
':publish:check-publish-branch',
|
||||
]);
|
||||
|
||||
/** Task that checks the new project version. */
|
||||
task(':publish:check-project-version', () => {
|
||||
const tag = argv['tag'];
|
||||
|
||||
if (!projectVersion.match(versionNameRegex)) {
|
||||
console.error(red(`Error: Cannot publish due to an invalid version name. Version ` +
|
||||
`"${projectVersion}" is not following our semver format.`));
|
||||
console.error(red(`A version should follow this format: X.X.X, X.X.X-beta.X, ` +
|
||||
`X.X.X-alpha.X, X.X.X-rc.X`));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (projectVersion.match(/(alpha|beta|rc)/) && (!tag || tag === 'latest')) {
|
||||
console.error(red(`Publishing ${projectVersion} to the "latest" tag is not allowed.`));
|
||||
console.error(red(`Alpha, Beta or RC versions shouldn't be published to "latest".`));
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
/** Task that verifies that the new version can be published from the current branch. */
|
||||
task(':publish:check-publish-branch', () => checkPublishBranch(projectVersion));
|
||||
|
||||
/** Task that ensures that the new release tagged on GitHub before publishing to NPM. */
|
||||
task(':publish:check-remote-tag', () => {
|
||||
// Since we cannot assume that every developer uses `origin` as the default name for the upstream
|
||||
// remote, we just pass in the Git URL that refers to angular/material2 repository on Github.
|
||||
const tagCommitSha = spawnSync('git', ['ls-remote', '--tags', repositoryGitUrl, projectVersion],
|
||||
{cwd: projectDir}).stdout.toString().trim();
|
||||
|
||||
if (!tagCommitSha) {
|
||||
console.error(red(`Cannot publish v${projectVersion} because the release is not ` +
|
||||
`tagged on upstream yet. Please tag the release before publishing to NPM.`));
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
@@ -1,87 +0,0 @@
|
||||
import {green, red} from 'chalk';
|
||||
import {existsSync, readFileSync} from 'fs';
|
||||
import {sync as glob} from 'glob';
|
||||
import {task} from 'gulp';
|
||||
import {buildConfig, sequenceTask} from 'material2-build-tools';
|
||||
import {join} from 'path';
|
||||
import {releasePackages} from './publish-task';
|
||||
|
||||
const {outputDir} = buildConfig;
|
||||
|
||||
/** Path to the directory where all releases are created. */
|
||||
const releasesDir = join(outputDir, 'releases');
|
||||
|
||||
/** RegExp that matches Angular component inline styles that contain a sourcemap reference. */
|
||||
const inlineStylesSourcemapRegex = /styles: ?\[["'].*sourceMappingURL=.*["']/;
|
||||
|
||||
/** RegExp that matches Angular component metadata properties that refer to external resources. */
|
||||
const externalReferencesRegex = /(templateUrl|styleUrls): *["'[]/;
|
||||
|
||||
task('validate-release', sequenceTask(':publish:build-releases', 'validate-release:check-bundles'));
|
||||
|
||||
/** Task that checks the release bundles for any common mistakes before releasing to the public. */
|
||||
task('validate-release:check-bundles', () => {
|
||||
const releaseFailures = releasePackages
|
||||
.map(packageName => checkReleasePackage(packageName))
|
||||
.map((failures, index) => ({failures, packageName: releasePackages[index]}));
|
||||
|
||||
releaseFailures.forEach(({failures, packageName}) => {
|
||||
failures.forEach(failure => console.error(red(`Failure (${packageName}): ${failure}`)));
|
||||
});
|
||||
|
||||
if (releaseFailures.some(({failures}) => failures.length > 0)) {
|
||||
// Throw an error to notify Gulp about the failures that have been detected.
|
||||
throw 'Release output is not valid and not ready for being released.';
|
||||
} else {
|
||||
console.log(green('Release output has been checked and everything looks fine.'));
|
||||
}
|
||||
});
|
||||
|
||||
/** Task that validates the given release package before releasing. */
|
||||
function checkReleasePackage(packageName: string): string[] {
|
||||
return glob(join(releasesDir, packageName, 'esm2015/*.js'))
|
||||
.reduce((failures: string[], bundlePath: string) => {
|
||||
return failures.concat(checkEs2015ReleaseBundle(packageName, bundlePath));
|
||||
}, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks an ES2015 bundle inside of a release package. Secondary entry-point bundles will be
|
||||
* checked as well.
|
||||
*/
|
||||
function checkEs2015ReleaseBundle(packageName: string, bundlePath: string): string[] {
|
||||
const bundleContent = readFileSync(bundlePath, 'utf8');
|
||||
let failures: string[] = [];
|
||||
|
||||
if (inlineStylesSourcemapRegex.exec(bundleContent) !== null) {
|
||||
failures.push('Bundles contain sourcemap references in component styles.');
|
||||
}
|
||||
|
||||
if (externalReferencesRegex.exec(bundleContent) !== null) {
|
||||
failures.push('Bundles are including references to external resources (templates or styles)');
|
||||
}
|
||||
|
||||
if (packageName === 'material') {
|
||||
failures = failures.concat(checkMaterialPackage());
|
||||
}
|
||||
|
||||
return failures;
|
||||
}
|
||||
|
||||
/** Function that includes special checks for the Material package. */
|
||||
function checkMaterialPackage(): string[] {
|
||||
const packagePath = join(releasesDir, 'material');
|
||||
const prebuiltThemesPath = join(packagePath, 'prebuilt-themes');
|
||||
const themingFilePath = join(packagePath, '_theming.scss');
|
||||
const failures: string[] = [];
|
||||
|
||||
if (glob('*.css', {cwd: prebuiltThemesPath}).length === 0) {
|
||||
failures.push('Prebuilt themes are not present in the Material release output.');
|
||||
}
|
||||
|
||||
if (!existsSync(themingFilePath)) {
|
||||
failures.push('The theming SCSS file is not present in the Material release output.');
|
||||
}
|
||||
|
||||
return failures;
|
||||
}
|
||||
Reference in New Issue
Block a user