Skip to content

Commit f960a8e

Browse files
author
Angular Builds
committed
3abd87a test: update update-7.0 E2E to reflect 8.0-rc.4 changes
1 parent 13a86cf commit f960a8e

File tree

11 files changed

+362
-86
lines changed

11 files changed

+362
-86
lines changed

commands/update-impl.d.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import { SchematicCommand } from '../models/schematic-command';
33
import { Schema as UpdateCommandSchema } from './update';
44
export declare class UpdateCommand extends SchematicCommand<UpdateCommandSchema> {
55
readonly allowMissingWorkspace = true;
6-
collectionName: string;
7-
schematicName: string;
8-
parseArguments(schematicOptions: string[], schema: Option[]): Promise<Arguments>;
6+
parseArguments(_schematicOptions: string[], _schema: Option[]): Promise<Arguments>;
97
run(options: UpdateCommandSchema & Arguments): Promise<number | void>;
108
}

commands/update-impl.js

Lines changed: 197 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,54 +7,217 @@ Object.defineProperty(exports, "__esModule", { value: true });
77
* Use of this source code is governed by an MIT-style license that can be
88
* found in the LICENSE file at https://angular.io/license
99
*/
10-
const core_1 = require("@angular-devkit/core");
10+
const path = require("path");
11+
const semver = require("semver");
1112
const schematic_command_1 = require("../models/schematic-command");
1213
const find_up_1 = require("../utilities/find-up");
1314
const package_manager_1 = require("../utilities/package-manager");
15+
const package_metadata_1 = require("../utilities/package-metadata");
16+
const package_tree_1 = require("../utilities/package-tree");
17+
const npa = require('npm-package-arg');
18+
const oldConfigFileNames = [
19+
'.angular-cli.json',
20+
'angular-cli.json',
21+
];
1422
class UpdateCommand extends schematic_command_1.SchematicCommand {
1523
constructor() {
1624
super(...arguments);
1725
this.allowMissingWorkspace = true;
18-
this.collectionName = '@schematics/update';
19-
this.schematicName = 'update';
2026
}
21-
async parseArguments(schematicOptions, schema) {
22-
const args = await super.parseArguments(schematicOptions, schema);
23-
const maybeArgsLeftovers = args['--'];
24-
if (maybeArgsLeftovers
25-
&& maybeArgsLeftovers.length == 1
26-
&& maybeArgsLeftovers[0] == '@angular/cli'
27-
&& args.migrateOnly === undefined
28-
&& args.from === undefined) {
29-
// Check for a 1.7 angular-cli.json file.
30-
const oldConfigFileNames = [
31-
core_1.normalize('.angular-cli.json'),
32-
core_1.normalize('angular-cli.json'),
33-
];
34-
const oldConfigFilePath = find_up_1.findUp(oldConfigFileNames, process.cwd())
35-
|| find_up_1.findUp(oldConfigFileNames, __dirname);
36-
if (oldConfigFilePath) {
37-
args.migrateOnly = true;
38-
args.from = '1.0.0';
39-
}
40-
}
41-
// Move `--` to packages.
42-
if (args.packages == undefined && args['--']) {
43-
args.packages = args['--'];
44-
delete args['--'];
45-
}
46-
return args;
27+
async parseArguments(_schematicOptions, _schema) {
28+
return {};
4729
}
30+
// tslint:disable-next-line:no-big-function
4831
async run(options) {
32+
const packages = [];
33+
for (const request of options['--'] || []) {
34+
try {
35+
const packageIdentifier = npa(request);
36+
// only registry identifiers are supported
37+
if (!packageIdentifier.registry) {
38+
this.logger.error(`Package '${request}' is not a registry package identifer.`);
39+
return 1;
40+
}
41+
if (packages.some(v => v.name === packageIdentifier.name)) {
42+
this.logger.error(`Duplicate package '${packageIdentifier.name}' specified.`);
43+
return 1;
44+
}
45+
// If next option is used and no specifier supplied, use next tag
46+
if (options.next && !packageIdentifier.rawSpec) {
47+
packageIdentifier.fetchSpec = 'next';
48+
}
49+
packages.push(packageIdentifier);
50+
}
51+
catch (e) {
52+
this.logger.error(e.message);
53+
return 1;
54+
}
55+
}
56+
if (options.all && packages.length > 0) {
57+
this.logger.error('Cannot specify packages when using the "all" option.');
58+
return 1;
59+
}
60+
else if (options.all && options.migrateOnly) {
61+
this.logger.error('Cannot use "all" option with "migrate-only" option.');
62+
return 1;
63+
}
64+
else if (!options.migrateOnly && (options.from || options.to)) {
65+
this.logger.error('Can only use "from" or "to" options with "migrate-only" option.');
66+
return 1;
67+
}
4968
const packageManager = package_manager_1.getPackageManager(this.workspace.root);
69+
this.logger.info(`Using package manager: '${packageManager}'`);
70+
// Special handling for Angular CLI 1.x migrations
71+
if (options.migrateOnly === undefined && options.from === undefined) {
72+
if (!options.all && packages.length === 1 && packages[0].name === '@angular/cli') {
73+
const oldConfigFilePath = find_up_1.findUp(oldConfigFileNames, process.cwd());
74+
if (oldConfigFilePath) {
75+
options.migrateOnly = true;
76+
options.from = '1.0.0';
77+
}
78+
}
79+
}
80+
this.logger.info('Collecting installed dependencies...');
81+
const packageTree = await package_tree_1.readPackageTree(this.workspace.root);
82+
const rootDependencies = package_tree_1.findNodeDependencies(packageTree);
83+
this.logger.info(`Found ${Object.keys(rootDependencies).length} dependencies.`);
84+
if (options.all || packages.length === 0) {
85+
// Either update all packages or show status
86+
return this.runSchematic({
87+
collectionName: '@schematics/update',
88+
schematicName: 'update',
89+
dryRun: !!options.dryRun,
90+
showNothingDone: false,
91+
additionalOptions: {
92+
force: options.force || false,
93+
next: options.next || false,
94+
packageManager,
95+
packages: options.all ? Object.keys(rootDependencies) : [],
96+
},
97+
});
98+
}
99+
if (options.migrateOnly) {
100+
if (!options.from) {
101+
this.logger.error('"from" option is required when using the "migrate-only" option.');
102+
return 1;
103+
}
104+
else if (packages.length !== 1) {
105+
this.logger.error('A single package must be specified when using the "migrate-only" option.');
106+
return 1;
107+
}
108+
if (options.next) {
109+
this.logger.warn('"next" option has no effect when using "migrate-only" option.');
110+
}
111+
const packageName = packages[0].name;
112+
let packageNode = rootDependencies[packageName];
113+
if (typeof packageNode === 'string') {
114+
this.logger.error('Package found in package.json but is not installed.');
115+
return 1;
116+
}
117+
else if (!packageNode) {
118+
// Allow running migrations on transitively installed dependencies
119+
// There can technically be nested multiple versions
120+
// TODO: If multiple, this should find all versions and ask which one to use
121+
const child = packageTree.children.find(c => c.name === packageName);
122+
if (child) {
123+
// A link represents a symlinked package so use the actual in this case
124+
packageNode = child.isLink ? child.target : child;
125+
}
126+
if (!packageNode) {
127+
this.logger.error('Package is not installed.');
128+
return 1;
129+
}
130+
}
131+
const updateMetadata = packageNode.package['ng-update'];
132+
let migrations = updateMetadata && updateMetadata.migrations;
133+
if (migrations === undefined) {
134+
this.logger.error('Package does not provide migrations.');
135+
return 1;
136+
}
137+
else if (typeof migrations !== 'string') {
138+
this.logger.error('Package contains a malformed migrations field.');
139+
return 1;
140+
}
141+
// if not non-relative, add package name
142+
if (migrations.startsWith('.') || migrations.startsWith('/')) {
143+
migrations = path.join(packageName, migrations);
144+
}
145+
return this.runSchematic({
146+
collectionName: '@schematics/update',
147+
schematicName: 'migrate',
148+
dryRun: !!options.dryRun,
149+
force: false,
150+
showNothingDone: false,
151+
additionalOptions: {
152+
package: packageName,
153+
collection: migrations,
154+
from: options.from,
155+
to: options.to || packageNode.package.version,
156+
},
157+
});
158+
}
159+
const requests = [];
160+
// Validate packages actually are part of the workspace
161+
for (const pkg of packages) {
162+
const node = rootDependencies[pkg.name];
163+
if (!node) {
164+
this.logger.error(`Package '${pkg.name}' is not a dependency.`);
165+
return 1;
166+
}
167+
// If a specific version is requested and matches the installed version, skip.
168+
if (pkg.type === 'version' &&
169+
typeof node === 'object' &&
170+
node.package.version === pkg.fetchSpec) {
171+
this.logger.info(`Package '${pkg.name}' is already at '${pkg.fetchSpec}'.`);
172+
continue;
173+
}
174+
requests.push(pkg);
175+
}
176+
if (requests.length === 0) {
177+
return 0;
178+
}
179+
this.logger.info('Fetching dependency metadata from registry...');
180+
for (const requestIdentifier of requests) {
181+
let metadata;
182+
try {
183+
// Metadata requests are internally cached; multiple requests for same name
184+
// does not result in additional network traffic
185+
metadata = await package_metadata_1.fetchPackageMetadata(requestIdentifier.name, this.logger);
186+
}
187+
catch (e) {
188+
this.logger.error(`Error fetching metadata for '${requestIdentifier.name}': ` + e.message);
189+
return 1;
190+
}
191+
// Try to find a package version based on the user requested package specifier
192+
// registry specifier types are either version, range, or tag
193+
let manifest;
194+
if (requestIdentifier.type === 'version') {
195+
manifest = metadata.versions.get(requestIdentifier.fetchSpec);
196+
}
197+
else if (requestIdentifier.type === 'range') {
198+
const maxVersion = semver.maxSatisfying(Array.from(metadata.versions.keys()), requestIdentifier.fetchSpec);
199+
if (maxVersion) {
200+
manifest = metadata.versions.get(maxVersion);
201+
}
202+
}
203+
else if (requestIdentifier.type === 'tag') {
204+
manifest = metadata.tags[requestIdentifier.fetchSpec];
205+
}
206+
if (!manifest) {
207+
this.logger.error(`Package specified by '${requestIdentifier.raw}' does not exist within the registry.`);
208+
return 1;
209+
}
210+
}
50211
return this.runSchematic({
51-
collectionName: this.collectionName,
52-
schematicName: this.schematicName,
53-
schematicOptions: options['--'],
212+
collectionName: '@schematics/update',
213+
schematicName: 'update',
54214
dryRun: !!options.dryRun,
55-
force: false,
56215
showNothingDone: false,
57-
additionalOptions: { packageManager },
216+
additionalOptions: {
217+
force: options.force || false,
218+
packageManager,
219+
packages: requests.map(p => p.toString()),
220+
},
58221
});
59222
}
60223
}

commands/update.d.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,41 @@
22
* Updates your application and its dependencies. See https://update.angular.io/
33
*/
44
export interface Schema {
5+
/**
6+
* Whether to update all packages in package.json.
7+
*/
8+
all?: boolean;
9+
/**
10+
* If false, will error out if installed packages are incompatible with the update.
11+
*/
12+
force?: boolean;
13+
/**
14+
* Version from which to migrate from. Only available with a single package being updated,
15+
* and only on migration only.
16+
*/
17+
from?: string;
518
/**
619
* Shows a help message for this command in the console.
720
*/
821
help?: HelpUnion;
22+
/**
23+
* Only perform a migration, does not update the installed version.
24+
*/
25+
migrateOnly?: boolean;
26+
/**
27+
* Use the largest version, including beta and RCs.
28+
*/
29+
next?: boolean;
30+
/**
31+
* The names of package(s) to update.
32+
*/
33+
packages?: string[];
34+
/**
35+
* Version up to which to apply migrations. Only available with a single package being
36+
* updated, and only on migrations only. Requires from to be specified. Default to the
37+
* installed version detected.
38+
*/
39+
to?: string;
940
}
1041
/**
1142
* Shows a help message for this command in the console.

commands/update.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,48 @@
1313
"allOf": [
1414
{
1515
"$ref": "./definitions.json#/definitions/base"
16+
},
17+
{
18+
"type": "object",
19+
"properties": {
20+
"packages": {
21+
"description": "The names of package(s) to update.",
22+
"type": "array",
23+
"items": {
24+
"type": "string"
25+
},
26+
"$default": {
27+
"$source": "argv"
28+
}
29+
},
30+
"force": {
31+
"description": "If false, will error out if installed packages are incompatible with the update.",
32+
"default": false,
33+
"type": "boolean"
34+
},
35+
"all": {
36+
"description": "Whether to update all packages in package.json.",
37+
"default": false,
38+
"type": "boolean"
39+
},
40+
"next": {
41+
"description": "Use the largest version, including beta and RCs.",
42+
"default": false,
43+
"type": "boolean"
44+
},
45+
"migrateOnly": {
46+
"description": "Only perform a migration, does not update the installed version.",
47+
"type": "boolean"
48+
},
49+
"from": {
50+
"description": "Version from which to migrate from. Only available with a single package being updated, and only on migration only.",
51+
"type": "string"
52+
},
53+
"to": {
54+
"description": "Version up to which to apply migrations. Only available with a single package being updated, and only on migrations only. Requires from to be specified. Default to the installed version detected.",
55+
"type": "string"
56+
}
57+
}
1658
}
1759
]
1860
}

0 commit comments

Comments
 (0)