Skip to content

fix(ng-update): migrations not running for release candidate versions #17704

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
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
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
"@types/node": "^12.11.1",
"@types/parse5": "^5.0.0",
"@types/run-sequence": "^0.0.29",
"@types/semver": "^6.2.0",
"@types/send": "^0.14.5",
"autoprefixer": "^6.7.6",
"axe-webdriverjs": "^1.1.1",
Expand Down Expand Up @@ -147,6 +148,7 @@
"run-sequence": "^1.2.2",
"scss-bundle": "^2.5.1",
"selenium-webdriver": "^3.6.0",
"semver": "^6.3.0",
"send": "^0.17.1",
"shelljs": "^0.8.3",
"sorcery": "^0.10.0",
Expand Down
8 changes: 4 additions & 4 deletions src/cdk/schematics/migration.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@
"$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
"migration-v6": {
"version": "6",
"version": "6.0.0",
"description": "Updates the Angular CDK to v6",
"factory": "./ng-update/index#updateToV6"
},
"migration-v7": {
"version": "7",
"version": "7.0.0",
"description": "Updates the Angular CDK to v7",
"factory": "./ng-update/index#updateToV7"
},
"migration-v8": {
"version": "8-beta",
"version": "8.0.0",
"description": "Updates the Angular CDK to v8",
"factory": "./ng-update/index#updateToV8"
},
"migration-v9": {
"version": "9",
"version": "9.0.0-0",
"description": "Updates the Angular CDK to v9",
"factory": "./ng-update/index#updateToV9"
},
Expand Down
8 changes: 4 additions & 4 deletions src/material/schematics/migration.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@
"$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
"migration-v6": {
"version": "6",
"version": "6.0.0",
"description": "Updates Angular Material to v6",
"factory": "./ng-update/index#updateToV6"
},
"migration-v7": {
"version": "7",
"version": "7.0.0",
"description": "Updates Angular Material to v7",
"factory": "./ng-update/index#updateToV7"
},
"migration-v8": {
"version": "8-beta",
"version": "8.0.0",
"description": "Updates Angular Material to v8",
"factory": "./ng-update/index#updateToV8"
},
"migration-v9": {
"version": "9",
"version": "9.0.0-0",
"description": "Updates Angular Material to v9",
"factory": "./ng-update/index#updateToV9"
},
Expand Down
11 changes: 8 additions & 3 deletions tools/release/check-release-output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import chalk from 'chalk';
import {join} from 'path';
import {checkReleasePackage} from './release-output/check-package';
import {releasePackages} from './release-output/release-packages';
import {parseVersionName, Version} from './version-name/parse-version';

/**
* Checks the release output by running the release-output validations for each
* release package.
*/
export function checkReleaseOutput(releaseOutputDir: string) {
export function checkReleaseOutput(releaseOutputDir: string, currentVersion: Version) {
let hasFailed = false;

releasePackages.forEach(packageName => {
if (!checkReleasePackage(releaseOutputDir, packageName)) {
if (!checkReleasePackage(releaseOutputDir, packageName, currentVersion)) {
hasFailed = true;
}
});
Expand All @@ -29,5 +30,9 @@ export function checkReleaseOutput(releaseOutputDir: string) {


if (require.main === module) {
checkReleaseOutput(join(__dirname, '../../dist/releases'));
const currentVersion = parseVersionName(require('../../package.json').version);
if (currentVersion === null) {
throw Error('Version in project "package.json" is invalid.');
}
checkReleaseOutput(join(__dirname, '../../dist/releases'), currentVersion);
}
2 changes: 1 addition & 1 deletion tools/release/publish-release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class PublishReleaseTask extends BaseReleaseTask {
console.info(chalk.green(` ✓ Built the release output.`));

// Checks all release packages against release output validations before releasing.
checkReleaseOutput(this.releaseOutputPath);
checkReleaseOutput(this.releaseOutputPath, this.currentVersion);

// Extract the release notes for the new version from the changelog file.
const extractedReleaseNotes = extractReleaseNotes(
Expand Down
8 changes: 7 additions & 1 deletion tools/release/release-output/check-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import chalk from 'chalk';
import {existsSync} from 'fs';
import {sync as glob} from 'glob';
import {join} from 'path';
import {Version} from '../version-name/parse-version';

import {
checkCdkPackage,
checkMaterialPackage,
checkPackageJsonFile,
checkPackageJsonMigrations,
checkReleaseBundle,
checkTypeDefinitionFile
} from './output-validations';
Expand All @@ -32,7 +34,8 @@ type PackageFailures = Map<string, string[]>;
* unexpected release output (e.g. the theming bundle is no longer generated)
* @returns Whether the package passed all checks or not.
*/
export function checkReleasePackage(releasesPath: string, packageName: string): boolean {
export function checkReleasePackage(
releasesPath: string, packageName: string, currentVersion: Version): boolean {
const packagePath = join(releasesPath, packageName);
const failures = new Map() as PackageFailures;
const addFailure = (message, filePath?) => {
Expand Down Expand Up @@ -79,6 +82,9 @@ export function checkReleasePackage(releasesPath: string, packageName: string):
addFailure('No "README.md" file found in package output.');
}

checkPackageJsonMigrations(join(packagePath, 'package.json'), currentVersion)
.forEach(f => addFailure(f));

// In case there are failures for this package, we want to print those
// and return a value that implies that there were failures.
if (failures.size) {
Expand Down
50 changes: 49 additions & 1 deletion tools/release/release-output/output-validations.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {existsSync, readFileSync} from 'fs';
import {sync as glob} from 'glob';
import {basename, dirname, isAbsolute, join} from 'path';
import * as semver from 'semver';
import * as ts from 'typescript';
import {Version} from '../version-name/parse-version';

/** RegExp that matches Angular component inline styles that contain a sourcemap reference. */
const inlineStylesSourcemapRegex = /styles: ?\[["'].*sourceMappingURL=.*["']/;
Expand Down Expand Up @@ -101,6 +103,21 @@ export function checkTypeDefinitionFile(filePath: string): string[] {
return failures;
}

/**
* Checks the ng-update migration setup for the specified "package.json"
* file if present.
*/
export function checkPackageJsonMigrations(
packageJsonPath: string, currentVersion: Version): string[] {
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));

if (packageJson['ng-update'] && packageJson['ng-update'].migrations) {
return checkMigrationCollection(
packageJson['ng-update'].migrations, dirname(packageJsonPath), currentVersion);
}
return [];
}

/**
* Checks the Angular Material release package and ensures that prebuilt themes
* and the theming bundle are built properly.
Expand All @@ -126,8 +143,39 @@ export function checkMaterialPackage(packagePath: string): string[] {
*/
export function checkCdkPackage(packagePath: string): string[] {
const prebuiltFiles = glob('*-prebuilt.css', {cwd: packagePath}).map(path => basename(path));

return ['overlay', 'a11y', 'text-field']
.filter(name => !prebuiltFiles.includes(`${name}-prebuilt.css`))
.map(name => `Could not find the prebuilt ${name} styles.`);
}

/**
* Checks if the migration collected referenced in the specified "package.json"
* has a migration set up for the given target version.
*/
function checkMigrationCollection(
collectionPath: string, packagePath: string, targetVersion: Version): string[] {
const collection = JSON.parse(readFileSync(join(packagePath, collectionPath), 'utf8'));
if (!collection.schematics) {
return ['No schematics found in migration collection.'];
}

const failures: string[] = [];
const lowerBoundaryVersion = `${targetVersion.major}.0.0-0`;
const schematics = collection.schematics;
const targetSchematics = Object.keys(schematics).filter(name => {
const schematicVersion = schematics[name].version;
try {
return schematicVersion && semver.gte(schematicVersion, lowerBoundaryVersion) &&
semver.lte(schematicVersion, targetVersion.format());
} catch {
failures.push(`Could not parse version for migration: ${name}`);
}
});

if (targetSchematics.length === 0) {
failures.push(`No migration configured that handles versions: ^${lowerBoundaryVersion}`);
} else if (targetSchematics.length > 1) {
failures.push(`Multiple migrations targeting the same major version: ${targetVersion.major}`);
}
return failures;
}
57 changes: 42 additions & 15 deletions tools/release/stage-release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {promptAndGenerateChangelog} from './changelog';
import {GitClient} from './git/git-client';
import {getGithubBranchCommitsUrl} from './git/github-urls';
import {promptForNewVersion} from './prompt/new-version-prompt';
import {checkPackageJsonMigrations} from './release-output/output-validations';
import {releasePackages} from './release-output/release-packages';
import {parseVersionName, Version} from './version-name/parse-version';

/** Default filename for the changelog. */
Expand Down Expand Up @@ -51,7 +53,8 @@ class StageReleaseTask extends BaseReleaseTask {
githubApi: OctokitApi;

constructor(
public projectDir: string, public repositoryOwner: string, public repositoryName: string) {
public projectDir: string, public packagesDir: string, public repositoryOwner: string,
public repositoryName: string) {
super(new GitClient(projectDir, `https://github.com/${repositoryOwner}/${repositoryName}.git`));

this.packageJsonPath = join(projectDir, 'package.json');
Expand Down Expand Up @@ -93,6 +96,7 @@ class StageReleaseTask extends BaseReleaseTask {

this.verifyLocalCommitsMatchUpstream(publishBranch);
this._verifyAngularPeerDependencyVersion(newVersion);
this._checkUpdateMigrationCollection(newVersion);
await this._verifyPassingGithubStatus(publishBranch);

if (!this.git.checkoutNewBranch(stagingBranch)) {
Expand Down Expand Up @@ -159,17 +163,19 @@ class StageReleaseTask extends BaseReleaseTask {
*/
private _verifyAngularPeerDependencyVersion(newVersion: Version) {
const currentVersionRange = this._getAngularVersionPlaceholderOrExit();
const isMajorWithPrerelease = newVersion.minor === 0 && newVersion.patch === 0 &&
newVersion.prereleaseLabel !== null;
const isMajorWithPrerelease =
newVersion.minor === 0 && newVersion.patch === 0 && newVersion.prereleaseLabel !== null;
const requiredRange = isMajorWithPrerelease ?
`^${newVersion.major}.0.0-0 || ^${newVersion.major + 1}.0.0-0` :
`^${newVersion.major}.0.0 || ^${newVersion.major + 1}.0.0-0`;
`^${newVersion.major}.0.0-0 || ^${newVersion.major + 1}.0.0-0` :
`^${newVersion.major}.0.0 || ^${newVersion.major + 1}.0.0-0`;

if (requiredRange !== currentVersionRange) {
console.error(chalk.red(` ✘ Cannot stage release. The required Angular version range ` +
`is invalid. The version range should be: ${requiredRange}`));
console.error(chalk.red(` Please manually update the version range ` +
`in: ${BAZEL_RELEASE_CONFIG_PATH}`));
console.error(chalk.red(
` ✘ Cannot stage release. The required Angular version range ` +
`is invalid. The version range should be: ${requiredRange}`));
console.error(chalk.red(
` Please manually update the version range ` +
`in: ${BAZEL_RELEASE_CONFIG_PATH}`));
return process.exit(1);
}
}
Expand All @@ -181,17 +187,19 @@ class StageReleaseTask extends BaseReleaseTask {
private _getAngularVersionPlaceholderOrExit(): string {
const bzlConfigPath = join(this.projectDir, BAZEL_RELEASE_CONFIG_PATH);
if (!existsSync(bzlConfigPath)) {
console.error(chalk.red(` ✘ Cannot stage release. Could not find the file which sets ` +
`the Angular peerDependency placeholder value. Looked for: ${bzlConfigPath}`));
console.error(chalk.red(
` ✘ Cannot stage release. Could not find the file which sets ` +
`the Angular peerDependency placeholder value. Looked for: ${bzlConfigPath}`));
return process.exit(1);
}

const configFileContent = readFileSync(bzlConfigPath, 'utf8');
const matches = configFileContent.match(/ANGULAR_PACKAGE_VERSION = ["']([^"']+)/);
if (!matches || !matches[1]) {
console.error(chalk.red(` ✘ Cannot stage release. Could not find the ` +
`"ANGULAR_PACKAGE_VERSION" variable. Please ensure this variable exists. ` +
`Looked in: ${bzlConfigPath}`));
console.error(chalk.red(
` ✘ Cannot stage release. Could not find the ` +
`"ANGULAR_PACKAGE_VERSION" variable. Please ensure this variable exists. ` +
`Looked in: ${bzlConfigPath}`));
return process.exit(1);
}
return matches[1];
Expand Down Expand Up @@ -236,9 +244,28 @@ class StageReleaseTask extends BaseReleaseTask {

console.info(chalk.green(` ✓ Upstream commit is passing all github status checks.`));
}

/**
* Checks if the update migration collections are set up to properly
* handle the given new version.
*/
private _checkUpdateMigrationCollection(newVersion: Version) {
const failures: string[] = [];
releasePackages.forEach(packageName => {
failures.push(...checkPackageJsonMigrations(
join(this.packagesDir, packageName, 'package.json'), newVersion)
.map(f => chalk.yellow(` ⮑ ${chalk.bold(packageName)}: ${f}`)));
});
if (failures.length) {
console.error(chalk.red(` ✘ Failures in ng-update migration collection detected:`));
failures.forEach(f => console.error(f));
process.exit(1);
}
}
}

/** Entry-point for the release staging script. */
if (require.main === module) {
new StageReleaseTask(join(__dirname, '../../'), 'angular', 'components').run();
const projectDir = join(__dirname, '../../');
new StageReleaseTask(projectDir, join(projectDir, 'src/'), 'angular', 'components').run();
}
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1405,6 +1405,11 @@
resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.13.tgz#deb799c641773c5e367abafc92d1e733d62cddd7"
integrity sha512-rI0LGoMiZGUM+tjDakQpwZOvcmQoubiJ7hxqrYU12VRxBuGGvOThxrBOU/QmJKlKg1WG6FMzuvcEyLffvVSsmw==

"@types/semver@^6.2.0":
version "6.2.0"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-6.2.0.tgz#d688d574400d96c5b0114968705366f431831e1a"
integrity sha512-1OzrNb4RuAzIT7wHSsgZRlMBlNsJl+do6UblR7JMW4oB7bbR+uBEYtUh7gEc/jM84GGilh68lSOokyM/zNUlBA==

"@types/send@^0.14.5":
version "0.14.5"
resolved "https://registry.yarnpkg.com/@types/send/-/send-0.14.5.tgz#653f7d25b93c3f7f51a8994addaf8a229de022a7"
Expand Down