Skip to content

build: prevent publishing from the wrong branch #12831

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions tools/gulp/gulpfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import './tasks/example-module';
import './tasks/lint';
import './tasks/material-release';
import './tasks/payload';
import './tasks/publish';
import './tasks/unit-test';
import './tasks/universal';
import './tasks/validate-release';

import './tasks/publish/publish-task';
import './tasks/publish/validate-release';
61 changes: 61 additions & 0 deletions tools/gulp/tasks/publish/branch-check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {bold} from 'chalk';
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)+)?/;

/** Regular expression that matches publish branch names and their Semver digits. */
const publishBranchNameRegex = /^([0-9]+)\.([x0-9]+)(?:\.([x0-9]+))?$/;

/** 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();

if (branchName === 'master') {
if (versionType === 'major') {
return;
}

throw `Publishing of "${versionType}" releases should not happen inside of the ` +
`${bold('master')} branch.`;
}

const branchNameMatch = branchName.match(publishBranchNameRegex) || [];
const branchDigits = branchNameMatch.slice(1, 4);

if (branchDigits[2] === 'x' && versionType !== 'patch') {
throw `Cannot publish a "${versionType}" release inside of a patch branch (${branchName})`;
}

if (branchDigits[1] === 'x' && versionType !== 'minor') {
throw `Cannot publish a "${versionType}" release inside of a minor branch (${branchName})`;
}

throw `Cannot publish a "${versionType}" release from branch: "${branchName}". Releases should `
+ `be published from "master" or the according publish branch (e.g. "6.x", "6.4.x")`;
}

/**
* 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';
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {green, grey, red, yellow} from 'chalk';
import {spawn} from 'child_process';
import {existsSync, statSync} from 'fs-extra';
import {join} from 'path';
import {task} from 'gulp';
import {execTask} from '../util/task_helpers';
import {buildConfig, sequenceTask} from 'material2-build-tools';
import {yellow, green, red, grey} from 'chalk';
import * as minimist from 'minimist';
import {join} from 'path';
import {execTask} from '../../util/task_helpers';
import {checkPublishBranch, versionNameRegex} from './branch-check';

/** Packages that will be published to NPM by the release task. */
export const releasePackages = [
Expand All @@ -16,9 +17,6 @@ export const releasePackages = [
'material-moment-adapter'
];

/** Regular Expression that matches valid version numbers of Angular Material. */
export const validVersionRegex = /^\d+\.\d+\.\d+(-(alpha|beta|rc)\.\d+)?$/;

/** Parse command-line arguments for release task. */
const argv = minimist(process.argv.slice(3));

Expand Down Expand Up @@ -50,38 +48,39 @@ task(':publish', async () => {
const version = buildConfig.projectVersion;
const currentDir = process.cwd();

if (!version.match(validVersionRegex)) {
console.log(red(`Error: Cannot publish due to an invalid version name. Version "${version}" ` +
`is not following our semver format.`));
console.log(yellow(`A version should follow this format: X.X.X, X.X.X-beta.X, X.X.X-alpha.X, ` +
`X.X.X-rc.X`));
if (!version.match(versionNameRegex)) {
console.error(red(`Error: Cannot publish due to an invalid version name. Version ` +
`"${version}" is not following our semver format.`));
console.error(yellow(`A version should follow this format: X.X.X, X.X.X-beta.X, ` +
`X.X.X-alpha.X, X.X.X-rc.X`));
return;
}

console.log('');
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('');

console.log();

if (version.match(/(alpha|beta|rc)/) && (!tag || tag === 'latest')) {
console.log(red(`Publishing ${version} to the "latest" tag is not allowed.`));
console.log(red(`Alpha, Beta or RC versions shouldn't be published to "latest".`));
console.log();
console.error(red(`Publishing ${version} to the "latest" tag is not allowed.`));
console.error(red(`Alpha, Beta or RC versions shouldn't be published to "latest".`));
console.error();
return;
}

if (releasePackages.length > 1) {
console.warn(red('Warning: Multiple packages will be released if proceeding.'));
console.warn(red('Warning: Packages to be released:', releasePackages.join(', ')));
console.log();
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 "angularVersion" in the build config.'));
checkPublishBranch(version);

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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import {task} from 'gulp';
import {readFileSync, existsSync} from 'fs';
import {join} from 'path';
import {green, red} from 'chalk';
import {releasePackages} from './publish';
import {releasePackages} from './publish-task';
import {sync as glob} from 'glob';
import {spawnSync} from 'child_process';
import {buildConfig, sequenceTask} from 'material2-build-tools';

const {projectDir, projectVersion, outputDir} = buildConfig;

/** Git repository URL that has been read out from the project package.json file. */
const repositoryGitUrl = require('../../../package.json').repository.url;
const repositoryGitUrl = require('../../../../package.json').repository.url;

/** Path to the directory where all releases are created. */
const releasesDir = join(outputDir, 'releases');
Expand Down