fix(bazel): Read latest versions from latest-versions.ts & use semver check (#27526)
When @angular/bazel is installed, a postinstall script is run to make sure that the npm version is *exactly* the same as the Angular repository install by Bazel. This check is overly stringent. Instead, it should enforce that the version satisfies the range check instead. This is consistent with the range defined in angular-cli/packages/schematics/angular/utility/latest-versions.ts. This commit also fixes the Bazel workspace to use the same Rxjs version if it's already installed. PR Close #27526
This commit is contained in:
parent
50687e11cf
commit
30a3b49830
|
@ -7,8 +7,8 @@
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @fileoverview This script runs as a postinstall in the published npm packages
|
* @fileoverview This script runs as a postinstall in the published npm packages
|
||||||
* and checks that the version of the build_bazel_rules_typescript external
|
* and checks that the version of the Angular external repository matches that
|
||||||
* repository matches that of the published npm package.
|
* of the published npm package.
|
||||||
*
|
*
|
||||||
* Note, this check is only performed with bazel managed deps when the yarn or
|
* Note, this check is only performed with bazel managed deps when the yarn or
|
||||||
* npm install is from a yarn_install or npm_install repository rule. For self
|
* npm install is from a yarn_install or npm_install repository rule. For self
|
||||||
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const semver = require('semver');
|
||||||
|
|
||||||
// Version in package.bzl should match the npm package version
|
// Version in package.bzl should match the npm package version
|
||||||
// but this should be tolerant of development stamped versions such as
|
// but this should be tolerant of development stamped versions such as
|
||||||
|
@ -56,14 +57,11 @@ if (isBazelManagedDeps()) {
|
||||||
}
|
}
|
||||||
// Be tolerant of versions such as "0.17.0-7-g76dc057"
|
// Be tolerant of versions such as "0.17.0-7-g76dc057"
|
||||||
const angularPackageVersion = contents.version.split('-')[0];
|
const angularPackageVersion = contents.version.split('-')[0];
|
||||||
if (npmPackageVersion !== angularPackageVersion) {
|
const range = `~${angularPackageVersion}`; // should match patch version
|
||||||
// TODO: we might need to support a range here.
|
if (!semver.satisfies(npmPackageVersion, range)) {
|
||||||
// For example, if you end up with @angular/bazel@6.1.8 and
|
throw new Error(
|
||||||
// @angular/bazel@6.1.9 both installed one of the postinstalls is
|
`Expected angular npm version ${npmPackageVersion} to satisfy ${range}. ` +
|
||||||
// guaranteed to fail since there's only one version of
|
`Please update ANGULAR_VERSION in WORKSPACE file to match ${npmPackageVersion}`);
|
||||||
// angular
|
|
||||||
throw new Error(`Expected angular repository to be version ${
|
|
||||||
npmPackageVersion} but found ${angularPackageVersion}`);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No version check
|
// No version check
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
"@bazel/typescript": "^0.21.0",
|
"@bazel/typescript": "^0.21.0",
|
||||||
"@schematics/angular": "^7.0.4",
|
"@schematics/angular": "^7.0.4",
|
||||||
"@types/node": "6.0.84",
|
"@types/node": "6.0.84",
|
||||||
|
"semver": "^5.6.0",
|
||||||
"shelljs": "0.8.2",
|
"shelljs": "0.8.2",
|
||||||
"tsickle": "0.34.0"
|
"tsickle": "0.34.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,25 +11,27 @@
|
||||||
import {strings} from '@angular-devkit/core';
|
import {strings} from '@angular-devkit/core';
|
||||||
import {Rule, SchematicContext, SchematicsException, Tree, apply, applyTemplates, mergeWith, move, url} from '@angular-devkit/schematics';
|
import {Rule, SchematicContext, SchematicsException, Tree, apply, applyTemplates, mergeWith, move, url} from '@angular-devkit/schematics';
|
||||||
import {getWorkspace} from '@schematics/angular/utility/config';
|
import {getWorkspace} from '@schematics/angular/utility/config';
|
||||||
|
import {latestVersions} from '@schematics/angular/utility/latest-versions';
|
||||||
import {validateProjectName} from '@schematics/angular/utility/validation';
|
import {validateProjectName} from '@schematics/angular/utility/validation';
|
||||||
|
|
||||||
import {Schema as BazelWorkspaceOptions} from './schema';
|
import {Schema as BazelWorkspaceOptions} from './schema';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Look for package.json file for @angular/core in node_modules and extract its
|
* Look for package.json file for package with `packageName` in node_modules and
|
||||||
* version.
|
* extract its version.
|
||||||
*/
|
*/
|
||||||
function findAngularVersion(options: BazelWorkspaceOptions, host: Tree): string|null {
|
function findVersion(projectName: string, packageName: string, host: Tree): string|null {
|
||||||
// Need to look in multiple locations because we could be working in a subtree.
|
// Need to look in multiple locations because we could be working in a subtree.
|
||||||
const candidates = [
|
const candidates = [
|
||||||
'node_modules/@angular/core/package.json',
|
`node_modules/${packageName}/package.json`,
|
||||||
`${options.name}/node_modules/@angular/core/package.json`,
|
`${projectName}/node_modules/${packageName}/package.json`,
|
||||||
];
|
];
|
||||||
for (const candidate of candidates) {
|
for (const candidate of candidates) {
|
||||||
if (host.exists(candidate)) {
|
if (host.exists(candidate)) {
|
||||||
try {
|
try {
|
||||||
const packageJson = JSON.parse(host.read(candidate).toString());
|
const packageJson = JSON.parse(host.read(candidate).toString());
|
||||||
if (packageJson.name === '@angular/core' && packageJson.version) {
|
if (packageJson.name === packageName && packageJson.version) {
|
||||||
return packageJson.version;
|
return packageJson.version;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -39,6 +41,15 @@ function findAngularVersion(options: BazelWorkspaceOptions, host: Tree): string|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean the version string and return version in the form "1.2.3". Return
|
||||||
|
* null if version string is invalid. This is similar to semver.clean() but
|
||||||
|
* takes characters like '^' and '~' into account.
|
||||||
|
*/
|
||||||
|
export function clean(version: string): string|null {
|
||||||
|
const matches = version.match(/(\d+\.\d+\.\d+)/);
|
||||||
|
return matches ? matches.pop() : null;
|
||||||
|
}
|
||||||
|
|
||||||
export default function(options: BazelWorkspaceOptions): Rule {
|
export default function(options: BazelWorkspaceOptions): Rule {
|
||||||
return (host: Tree, context: SchematicContext) => {
|
return (host: Tree, context: SchematicContext) => {
|
||||||
|
@ -54,13 +65,25 @@ export default function(options: BazelWorkspaceOptions): Rule {
|
||||||
}
|
}
|
||||||
const appDir = `${newProjectRoot}/${options.name}`;
|
const appDir = `${newProjectRoot}/${options.name}`;
|
||||||
|
|
||||||
// If user already has angular installed, Bazel should use that version
|
// If the project already has some deps installed, Bazel should use existing
|
||||||
const existingAngularVersion = findAngularVersion(options, host);
|
// versions.
|
||||||
|
const existingVersions = {
|
||||||
|
Angular: findVersion(options.name, '@angular/core', host),
|
||||||
|
RxJs: findVersion(options.name, 'rxjs', host),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const name of Object.keys(existingVersions)) {
|
||||||
|
const version = existingVersions[name];
|
||||||
|
if (version) {
|
||||||
|
context.logger.info(`Bazel will reuse existing version for ${name}: ${version}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const workspaceVersions = {
|
const workspaceVersions = {
|
||||||
'ANGULAR_VERSION': existingAngularVersion || '7.1.1',
|
'ANGULAR_VERSION': existingVersions.Angular || clean(latestVersions.Angular),
|
||||||
'RULES_SASS_VERSION': '1.14.1',
|
'RXJS_VERSION': existingVersions.RxJs || clean(latestVersions.RxJs),
|
||||||
'RXJS_VERSION': '6.3.3',
|
// TODO(kyliau): Consider moving this to latest-versions.ts
|
||||||
|
'RULES_SASS_VERSION': '1.15.1',
|
||||||
};
|
};
|
||||||
|
|
||||||
return mergeWith(apply(url('./files'), [
|
return mergeWith(apply(url('./files'), [
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import {HostTree} from '@angular-devkit/schematics';
|
import {HostTree} from '@angular-devkit/schematics';
|
||||||
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
|
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
|
||||||
|
import {clean} from './index';
|
||||||
|
|
||||||
describe('Bazel-workspace Schematic', () => {
|
describe('Bazel-workspace Schematic', () => {
|
||||||
const schematicRunner =
|
const schematicRunner =
|
||||||
|
@ -59,3 +60,14 @@ describe('Bazel-workspace Schematic', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('clean', () => {
|
||||||
|
[['1.2.3', '1.2.3'], [' 1.2.3', '1.2.3'], ['1.2.3 ', '1.2.3'], ['~1.2.3', '1.2.3'],
|
||||||
|
['^1.2.3', '1.2.3'], ['v1.2.3', '1.2.3'], ['1.2', null], ['a.b.c', null],
|
||||||
|
].forEach(([version, want]) => {
|
||||||
|
it(`should match ${version} with ${want}`, () => {
|
||||||
|
const got = clean(version);
|
||||||
|
expect(got).toBe(want);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -35,8 +35,9 @@ function addDevDependenciesToPackageJson(options: Schema) {
|
||||||
|
|
||||||
const devDependencies: {[k: string]: string} = {
|
const devDependencies: {[k: string]: string} = {
|
||||||
'@angular/bazel': angularCoreVersion,
|
'@angular/bazel': angularCoreVersion,
|
||||||
'@bazel/karma': '0.21.0',
|
// TODO(kyliau): Consider moving this to latest-versions.ts
|
||||||
'@bazel/typescript': '0.21.0',
|
'@bazel/karma': '^0.21.0',
|
||||||
|
'@bazel/typescript': '^0.21.0',
|
||||||
};
|
};
|
||||||
|
|
||||||
const recorder = host.beginUpdate(packageJson);
|
const recorder = host.beginUpdate(packageJson);
|
||||||
|
|
|
@ -41,6 +41,16 @@ describe('Ng-new Schematic', () => {
|
||||||
expect(json.dependencies[core]).toBe(json.devDependencies[bazel]);
|
expect(json.dependencies[core]).toBe(json.devDependencies[bazel]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should add @bazel/* dev dependencies', () => {
|
||||||
|
const options = {...defaultOptions};
|
||||||
|
const host = schematicRunner.runSchematic('ng-new', options);
|
||||||
|
const content = host.readContent('/demo/package.json');
|
||||||
|
const json = JSON.parse(content);
|
||||||
|
const devDeps = Object.keys(json.devDependencies);
|
||||||
|
expect(devDeps).toContain('@bazel/karma');
|
||||||
|
expect(devDeps).toContain('@bazel/typescript');
|
||||||
|
});
|
||||||
|
|
||||||
it('should create Bazel workspace file', () => {
|
it('should create Bazel workspace file', () => {
|
||||||
const options = {...defaultOptions};
|
const options = {...defaultOptions};
|
||||||
const host = schematicRunner.runSchematic('ng-new', options);
|
const host = schematicRunner.runSchematic('ng-new', options);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue