diff --git a/tools/gulp/gulpfile.ts b/tools/gulp/gulpfile.ts index 734ebc4cb1a..e2f5adcde75 100644 --- a/tools/gulp/gulpfile.ts +++ b/tools/gulp/gulpfile.ts @@ -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'; diff --git a/tools/gulp/tasks/publish/branch-check.ts b/tools/gulp/tasks/publish/branch-check.ts deleted file mode 100644 index 522601d64cb..00000000000 --- a/tools/gulp/tasks/publish/branch-check.ts +++ /dev/null @@ -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'; - } -} diff --git a/tools/gulp/tasks/publish/publish-task.ts b/tools/gulp/tasks/publish/publish-task.ts deleted file mode 100644 index 0e86ca36de0..00000000000 --- a/tools/gulp/tasks/publish/publish-task.ts +++ /dev/null @@ -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}.`)); - } - }); - }); -} diff --git a/tools/gulp/tasks/publish/sanity-checks.ts b/tools/gulp/tasks/publish/sanity-checks.ts deleted file mode 100644 index 0ed4f3c7580..00000000000 --- a/tools/gulp/tasks/publish/sanity-checks.ts +++ /dev/null @@ -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); - } -}); diff --git a/tools/gulp/tasks/publish/validate-release.ts b/tools/gulp/tasks/publish/validate-release.ts deleted file mode 100644 index 996887d4961..00000000000 --- a/tools/gulp/tasks/publish/validate-release.ts +++ /dev/null @@ -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; -}