5
5
* Use of this source code is governed by an MIT-style license that can be
6
6
* found in the LICENSE file at https://angular.io/license
7
7
*/
8
+ import { normalize , virtualFs } from '@angular-devkit/core' ;
9
+ import { NodeJsSyncHost } from '@angular-devkit/core/node' ;
10
+ import { UnsuccessfulWorkflowExecution } from '@angular-devkit/schematics' ;
11
+ import { NodeWorkflow , validateOptionsWithSchema } from '@angular-devkit/schematics/tools' ;
8
12
import { execSync } from 'child_process' ;
9
13
import * as fs from 'fs' ;
10
14
import * as path from 'path' ;
11
15
import * as semver from 'semver' ;
12
- import { Arguments , Option } from '../models/interface' ;
13
- import { SchematicCommand } from '../models/schematic-command' ;
16
+ import { Command } from '../models/command' ;
17
+ import { Arguments } from '../models/interface' ;
18
+ import { colors } from '../utilities/color' ;
14
19
import { getPackageManager } from '../utilities/package-manager' ;
15
20
import {
16
21
PackageIdentifier ,
@@ -28,11 +33,96 @@ const npa = require('npm-package-arg');
28
33
29
34
const oldConfigFileNames = [ '.angular-cli.json' , 'angular-cli.json' ] ;
30
35
31
- export class UpdateCommand extends SchematicCommand < UpdateCommandSchema > {
36
+ export class UpdateCommand extends Command < UpdateCommandSchema > {
32
37
public readonly allowMissingWorkspace = true ;
33
38
34
- async parseArguments ( _schematicOptions : string [ ] , _schema : Option [ ] ) : Promise < Arguments > {
35
- return { } ;
39
+ private workflow : NodeWorkflow ;
40
+
41
+ async initialize ( ) {
42
+ this . workflow = new NodeWorkflow (
43
+ new virtualFs . ScopedHost ( new NodeJsSyncHost ( ) , normalize ( this . workspace . root ) ) ,
44
+ {
45
+ packageManager : await getPackageManager ( this . workspace . root ) ,
46
+ root : normalize ( this . workspace . root ) ,
47
+ } ,
48
+ ) ;
49
+
50
+ this . workflow . engineHost . registerOptionsTransform (
51
+ validateOptionsWithSchema ( this . workflow . registry ) ,
52
+ ) ;
53
+ }
54
+
55
+ async executeSchematic (
56
+ collection : string ,
57
+ schematic : string ,
58
+ options = { } ,
59
+ ) : Promise < { success : boolean ; files : Set < string > } > {
60
+ let error = false ;
61
+ const logs : string [ ] = [ ] ;
62
+ const files = new Set < string > ( ) ;
63
+
64
+ const reporterSubscription = this . workflow . reporter . subscribe ( event => {
65
+ // Strip leading slash to prevent confusion.
66
+ const eventPath = event . path . startsWith ( '/' ) ? event . path . substr ( 1 ) : event . path ;
67
+
68
+ switch ( event . kind ) {
69
+ case 'error' :
70
+ error = true ;
71
+ const desc = event . description == 'alreadyExist' ? 'already exists' : 'does not exist.' ;
72
+ this . logger . warn ( `ERROR! ${ eventPath } ${ desc } .` ) ;
73
+ break ;
74
+ case 'update' :
75
+ logs . push ( `${ colors . whiteBright ( 'UPDATE' ) } ${ eventPath } (${ event . content . length } bytes)` ) ;
76
+ files . add ( eventPath ) ;
77
+ break ;
78
+ case 'create' :
79
+ logs . push ( `${ colors . green ( 'CREATE' ) } ${ eventPath } (${ event . content . length } bytes)` ) ;
80
+ files . add ( eventPath ) ;
81
+ break ;
82
+ case 'delete' :
83
+ logs . push ( `${ colors . yellow ( 'DELETE' ) } ${ eventPath } ` ) ;
84
+ files . add ( eventPath ) ;
85
+ break ;
86
+ case 'rename' :
87
+ logs . push ( `${ colors . blue ( 'RENAME' ) } ${ eventPath } => ${ event . to } ` ) ;
88
+ files . add ( eventPath ) ;
89
+ break ;
90
+ }
91
+ } ) ;
92
+
93
+ const lifecycleSubscription = this . workflow . lifeCycle . subscribe ( event => {
94
+ if ( event . kind == 'end' || event . kind == 'post-tasks-start' ) {
95
+ if ( ! error ) {
96
+ // Output the logging queue, no error happened.
97
+ logs . forEach ( log => this . logger . info ( log ) ) ;
98
+ }
99
+ }
100
+ } ) ;
101
+
102
+ // TODO: Allow passing a schematic instance directly
103
+ try {
104
+ await this . workflow
105
+ . execute ( {
106
+ collection,
107
+ schematic,
108
+ options,
109
+ logger : this . logger ,
110
+ } )
111
+ . toPromise ( ) ;
112
+
113
+ reporterSubscription . unsubscribe ( ) ;
114
+ lifecycleSubscription . unsubscribe ( ) ;
115
+
116
+ return { success : true , files } ;
117
+ } catch ( e ) {
118
+ if ( e instanceof UnsuccessfulWorkflowExecution ) {
119
+ this . logger . error ( 'The update failed. See above.' ) ;
120
+ } else {
121
+ this . logger . fatal ( e . message ) ;
122
+ }
123
+
124
+ return { success : ! error , files } ;
125
+ }
36
126
}
37
127
38
128
async executeMigrations (
@@ -41,11 +131,11 @@ export class UpdateCommand extends SchematicCommand<UpdateCommandSchema> {
41
131
range : semver . Range ,
42
132
commit = false ,
43
133
) {
44
- const collection = this . getEngine ( ) . createCollection ( collectionPath ) ;
134
+ const collection = this . workflow . engine . createCollection ( collectionPath ) ;
45
135
46
136
const migrations = [ ] ;
47
137
for ( const name of collection . listSchematicNames ( ) ) {
48
- const schematic = this . getEngine ( ) . createSchematic ( name , collection ) ;
138
+ const schematic = this . workflow . engine . createSchematic ( name , collection ) ;
49
139
const description = schematic . description as typeof schematic . description & {
50
140
version ?: string ;
51
141
} ;
@@ -71,16 +161,8 @@ export class UpdateCommand extends SchematicCommand<UpdateCommandSchema> {
71
161
`** Executing migrations for version ${ migration . version } of package '${ packageName } ' **` ,
72
162
) ;
73
163
74
- // TODO: run the schematic directly; most of the logic in the following is unneeded
75
- const result = await this . runSchematic ( {
76
- collectionName : migration . collection . name ,
77
- schematicName : migration . name ,
78
- force : false ,
79
- showNothingDone : false ,
80
- } ) ;
81
-
82
- // 1 = failure
83
- if ( result === 1 ) {
164
+ const result = await this . executeSchematic ( migration . collection . name , migration . name ) ;
165
+ if ( ! result . success ) {
84
166
if ( startingGitSha !== null ) {
85
167
const currentGitSha = this . findCurrentGitSha ( ) ;
86
168
if ( currentGitSha !== startingGitSha ) {
@@ -97,7 +179,8 @@ export class UpdateCommand extends SchematicCommand<UpdateCommandSchema> {
97
179
if ( migration . description ) {
98
180
message += '\n' + migration . description ;
99
181
}
100
- this . createCommit ( [ ] , message ) ;
182
+ // TODO: Use result.files once package install tasks are accounted
183
+ this . createCommit ( message , [ ] ) ;
101
184
}
102
185
}
103
186
}
@@ -192,19 +275,15 @@ export class UpdateCommand extends SchematicCommand<UpdateCommandSchema> {
192
275
193
276
if ( options . all || packages . length === 0 ) {
194
277
// Either update all packages or show status
195
- return this . runSchematic ( {
196
- collectionName : '@schematics/update' ,
197
- schematicName : 'update' ,
198
- dryRun : ! ! options . dryRun ,
199
- showNothingDone : false ,
200
- additionalOptions : {
201
- force : options . force || false ,
202
- next : options . next || false ,
203
- verbose : options . verbose || false ,
204
- packageManager,
205
- packages : options . all ? Object . keys ( rootDependencies ) : [ ] ,
206
- } ,
278
+ const { success } = await this . executeSchematic ( '@schematics/update' , 'update' , {
279
+ force : options . force || false ,
280
+ next : options . next || false ,
281
+ verbose : options . verbose || false ,
282
+ packageManager,
283
+ packages : options . all ? Object . keys ( rootDependencies ) : [ ] ,
207
284
} ) ;
285
+
286
+ return success ? 0 : 1 ;
208
287
}
209
288
210
289
if ( options . migrateOnly ) {
@@ -408,18 +487,14 @@ export class UpdateCommand extends SchematicCommand<UpdateCommandSchema> {
408
487
return 0 ;
409
488
}
410
489
411
- return this . runSchematic ( {
412
- collectionName : '@schematics/update' ,
413
- schematicName : 'update' ,
414
- dryRun : ! ! options . dryRun ,
415
- showNothingDone : false ,
416
- additionalOptions : {
417
- verbose : options . verbose || false ,
418
- force : options . force || false ,
419
- packageManager,
420
- packages : packagesToUpdate ,
421
- } ,
490
+ const { success } = await this . executeSchematic ( '@schematics/update' , 'update' , {
491
+ verbose : options . verbose || false ,
492
+ force : options . force || false ,
493
+ packageManager,
494
+ packages : packagesToUpdate ,
422
495
} ) ;
496
+
497
+ return success ? 0 : 1 ;
423
498
}
424
499
425
500
checkCleanGit ( ) {
@@ -445,7 +520,7 @@ export class UpdateCommand extends SchematicCommand<UpdateCommandSchema> {
445
520
return true ;
446
521
}
447
522
448
- createCommit ( files : string [ ] , message : string ) {
523
+ createCommit ( message : string , files : string [ ] ) {
449
524
try {
450
525
execSync ( 'git add -A ' + files . join ( ' ' ) , { encoding : 'utf8' , stdio : 'pipe' } ) ;
451
526
0 commit comments