@@ -13,6 +13,7 @@ import (
13
13
"io/ioutil"
14
14
"log"
15
15
"sort"
16
+ "sync"
16
17
"text/tabwriter"
17
18
18
19
"github.com/golang/dep"
@@ -42,6 +43,17 @@ print an extended status output for each dependency of the project.
42
43
Status returns exit code zero if all dependencies are in a "good state".
43
44
`
44
45
46
+ const (
47
+ shortRev uint8 = iota
48
+ longRev
49
+ )
50
+
51
+ var (
52
+ errFailedUpdate = errors .New ("failed to fetch updates" )
53
+ errFailedListPkg = errors .New ("failed to list packages" )
54
+ errMultipleFailures = errors .New ("multiple sources of failure" )
55
+ )
56
+
45
57
func (cmd * statusCommand ) Name () string { return "status" }
46
58
func (cmd * statusCommand ) Args () string { return "[package...]" }
47
59
func (cmd * statusCommand ) ShortHelp () string { return statusShortHelp }
@@ -97,7 +109,7 @@ func (out *tableOutput) BasicLine(bs *BasicStatus) {
97
109
bs .getConsolidatedConstraint (),
98
110
formatVersion (bs .Version ),
99
111
formatVersion (bs .Revision ),
100
- formatVersion ( bs .Latest ),
112
+ bs .getConsolidatedLatest ( shortRev ),
101
113
bs .PackageCount ,
102
114
)
103
115
}
@@ -223,8 +235,21 @@ func (cmd *statusCommand) Run(ctx *dep.Ctx, args []string) error {
223
235
}
224
236
}
225
237
226
- digestMismatch , hasMissingPkgs , err := runStatusAll (ctx , out , p , sm )
238
+ digestMismatch , hasMissingPkgs , errCount , err := runStatusAll (ctx , out , p , sm )
227
239
if err != nil {
240
+ // If it's only update errors
241
+ if err == errFailedUpdate {
242
+ // Print the results with unknown data
243
+ ctx .Out .Println (buf .String ())
244
+
245
+ // Print the help when in non-verbose mode
246
+ if ! ctx .Verbose {
247
+ ctx .Out .Printf ("The status of %d projects are unknown due to errors. Rerun with `-v` flag to see details.\n " , errCount )
248
+ }
249
+ } else {
250
+ // List package failure or multiple failures
251
+ ctx .Out .Println ("Failed to get status. Rerun with `-v` flag to see details." )
252
+ }
228
253
return err
229
254
}
230
255
@@ -248,8 +273,8 @@ type rawStatus struct {
248
273
ProjectRoot string
249
274
Constraint string
250
275
Version string
251
- Revision gps. Revision
252
- Latest gps. Version
276
+ Revision string
277
+ Latest string
253
278
PackageCount int
254
279
}
255
280
@@ -264,6 +289,7 @@ type BasicStatus struct {
264
289
Latest gps.Version
265
290
PackageCount int
266
291
hasOverride bool
292
+ hasError bool
267
293
}
268
294
269
295
func (bs * BasicStatus ) getConsolidatedConstraint () string {
@@ -291,13 +317,31 @@ func (bs *BasicStatus) getConsolidatedVersion() string {
291
317
return version
292
318
}
293
319
320
+ func (bs * BasicStatus ) getConsolidatedLatest (revSize uint8 ) string {
321
+ latest := ""
322
+ if bs .Latest != nil {
323
+ switch revSize {
324
+ case shortRev :
325
+ latest = formatVersion (bs .Latest )
326
+ case longRev :
327
+ latest = bs .Latest .String ()
328
+ }
329
+ }
330
+
331
+ if bs .hasError {
332
+ latest += "unknown"
333
+ }
334
+
335
+ return latest
336
+ }
337
+
294
338
func (bs * BasicStatus ) marshalJSON () * rawStatus {
295
339
return & rawStatus {
296
340
ProjectRoot : bs .ProjectRoot ,
297
341
Constraint : bs .getConsolidatedConstraint (),
298
342
Version : formatVersion (bs .Version ),
299
- Revision : bs .Revision ,
300
- Latest : bs .Latest ,
343
+ Revision : string ( bs .Revision ) ,
344
+ Latest : bs .getConsolidatedLatest ( longRev ) ,
301
345
PackageCount : bs .PackageCount ,
302
346
}
303
347
}
@@ -308,18 +352,16 @@ type MissingStatus struct {
308
352
MissingPackages []string
309
353
}
310
354
311
- func runStatusAll (ctx * dep.Ctx , out outputter , p * dep.Project , sm gps.SourceManager ) (bool , bool , error ) {
312
- var digestMismatch , hasMissingPkgs bool
313
-
355
+ func runStatusAll (ctx * dep.Ctx , out outputter , p * dep.Project , sm gps.SourceManager ) (digestMismatch bool , hasMissingPkgs bool , errCount int , err error ) {
314
356
if p .Lock == nil {
315
- return digestMismatch , hasMissingPkgs , errors .Errorf ("no Gopkg.lock found. Run `dep ensure` to generate lock file" )
357
+ return false , false , 0 , errors .Errorf ("no Gopkg.lock found. Run `dep ensure` to generate lock file" )
316
358
}
317
359
318
360
// While the network churns on ListVersions() requests, statically analyze
319
361
// code from the current project.
320
362
ptree , err := pkgtree .ListPackages (p .ResolvedAbsRoot , string (p .ImportRoot ))
321
363
if err != nil {
322
- return digestMismatch , hasMissingPkgs , errors .Wrapf (err , "analysis of local packages failed" )
364
+ return false , false , 0 , errors .Wrapf (err , "analysis of local packages failed" )
323
365
}
324
366
325
367
// Set up a solver in order to check the InputHash.
@@ -339,12 +381,12 @@ func runStatusAll(ctx *dep.Ctx, out outputter, p *dep.Project, sm gps.SourceMana
339
381
}
340
382
341
383
if err := ctx .ValidateParams (sm , params ); err != nil {
342
- return digestMismatch , hasMissingPkgs , err
384
+ return false , false , 0 , err
343
385
}
344
386
345
387
s , err := gps .Prepare (params , sm )
346
388
if err != nil {
347
- return digestMismatch , hasMissingPkgs , errors .Wrapf (err , "could not set up solver for input hashing" )
389
+ return false , false , 0 , errors .Wrapf (err , "could not set up solver for input hashing" )
348
390
}
349
391
350
392
cm := collectConstraints (ptree , p , sm )
@@ -366,85 +408,156 @@ func runStatusAll(ctx *dep.Ctx, out outputter, p *dep.Project, sm gps.SourceMana
366
408
367
409
logger .Println ("Checking upstream projects:" )
368
410
411
+ // BasicStatus channel to collect all the BasicStatus.
412
+ bsCh := make (chan * BasicStatus , len (slp ))
413
+
414
+ // Error channels to collect different errors.
415
+ errListPkgCh := make (chan error , len (slp ))
416
+ errListVerCh := make (chan error , len (slp ))
417
+
418
+ var wg sync.WaitGroup
419
+
369
420
for i , proj := range slp {
421
+ wg .Add (1 )
370
422
logger .Printf ("(%d/%d) %s\n " , i + 1 , len (slp ), proj .Ident ().ProjectRoot )
371
423
372
- bs := BasicStatus {
373
- ProjectRoot : string (proj .Ident ().ProjectRoot ),
374
- PackageCount : len (proj .Packages ()),
375
- }
424
+ go func (proj gps.LockedProject ) {
425
+ bs := BasicStatus {
426
+ ProjectRoot : string (proj .Ident ().ProjectRoot ),
427
+ PackageCount : len (proj .Packages ()),
428
+ }
429
+
430
+ // Get children only for specific outputers
431
+ // in order to avoid slower status process.
432
+ switch out .(type ) {
433
+ case * dotOutput :
434
+ ptr , err := sm .ListPackages (proj .Ident (), proj .Version ())
376
435
377
- // Get children only for specific outputers
378
- // in order to avoid slower status process
379
- switch out .(type ) {
380
- case * dotOutput :
381
- ptr , err := sm .ListPackages (proj .Ident (), proj .Version ())
436
+ if err != nil {
437
+ bs .hasError = true
438
+ errListPkgCh <- err
439
+ }
382
440
383
- if err != nil {
384
- return digestMismatch , hasMissingPkgs , errors . Wrapf ( err , "analysis of %s package failed" , proj . Ident (). ProjectRoot )
441
+ prm , _ := ptr . ToReachMap ( true , false , false , nil )
442
+ bs . Children = prm . FlattenFn ( paths . IsStandardImportPath )
385
443
}
386
444
387
- prm , _ := ptr .ToReachMap (true , false , false , nil )
388
- bs .Children = prm .FlattenFn (paths .IsStandardImportPath )
389
- }
445
+ // Split apart the version from the lock into its constituent parts.
446
+ switch tv := proj .Version ().(type ) {
447
+ case gps.UnpairedVersion :
448
+ bs .Version = tv
449
+ case gps.Revision :
450
+ bs .Revision = tv
451
+ case gps.PairedVersion :
452
+ bs .Version = tv .Unpair ()
453
+ bs .Revision = tv .Revision ()
454
+ }
390
455
391
- // Split apart the version from the lock into its constituent parts
392
- switch tv := proj .Version ().(type ) {
393
- case gps.UnpairedVersion :
394
- bs .Version = tv
395
- case gps.Revision :
396
- bs .Revision = tv
397
- case gps.PairedVersion :
398
- bs .Version = tv .Unpair ()
399
- bs .Revision = tv .Revision ()
456
+ // Check if the manifest has an override for this project. If so,
457
+ // set that as the constraint.
458
+ if pp , has := p .Manifest .Ovr [proj .Ident ().ProjectRoot ]; has && pp .Constraint != nil {
459
+ bs .hasOverride = true
460
+ bs .Constraint = pp .Constraint
461
+ } else {
462
+ bs .Constraint = gps .Any ()
463
+ for _ , c := range cm [bs .ProjectRoot ] {
464
+ bs .Constraint = c .Intersect (bs .Constraint )
465
+ }
466
+ }
467
+
468
+ // Only if we have a non-rev and non-plain version do/can we display
469
+ // anything wrt the version's updateability.
470
+ if bs .Version != nil && bs .Version .Type () != gps .IsVersion {
471
+ c , has := p .Manifest .Constraints [proj .Ident ().ProjectRoot ]
472
+ if ! has {
473
+ c .Constraint = gps .Any ()
474
+ }
475
+ // TODO: This constraint is only the constraint imposed by the
476
+ // current project, not by any transitive deps. As a result,
477
+ // transitive project deps will always show "any" here.
478
+ bs .Constraint = c .Constraint
479
+
480
+ vl , err := sm .ListVersions (proj .Ident ())
481
+ if err == nil {
482
+ gps .SortPairedForUpgrade (vl )
483
+
484
+ for _ , v := range vl {
485
+ // Because we've sorted the version list for
486
+ // upgrade, the first version we encounter that
487
+ // matches our constraint will be what we want.
488
+ if c .Constraint .Matches (v ) {
489
+ bs .Latest = v .Revision ()
490
+ break
491
+ }
492
+ }
493
+ } else {
494
+ // Failed to fetch version list (could happen due to
495
+ // network issue).
496
+ bs .hasError = true
497
+ errListVerCh <- err
498
+ }
499
+ }
500
+
501
+ bsCh <- & bs
502
+
503
+ wg .Done ()
504
+ }(proj )
505
+ }
506
+
507
+ wg .Wait ()
508
+ close (bsCh )
509
+ close (errListPkgCh )
510
+ close (errListVerCh )
511
+
512
+ // Newline after printing the status progress output.
513
+ logger .Println ()
514
+
515
+ // List Packages errors. This would happen only for dot output.
516
+ if len (errListPkgCh ) > 0 {
517
+ err = errFailedListPkg
518
+ if ctx .Verbose {
519
+ for err := range errListPkgCh {
520
+ ctx .Err .Println (err .Error ())
521
+ }
522
+ ctx .Err .Println ()
400
523
}
524
+ }
401
525
402
- // Check if the manifest has an override for this project. If so,
403
- // set that as the constraint.
404
- if pp , has := p .Manifest .Ovr [proj .Ident ().ProjectRoot ]; has && pp .Constraint != nil {
405
- bs .hasOverride = true
406
- bs .Constraint = pp .Constraint
526
+ // List Version errors.
527
+ if len (errListVerCh ) > 0 {
528
+ if err == nil {
529
+ err = errFailedUpdate
407
530
} else {
408
- bs .Constraint = gps .Any ()
409
- for _ , c := range cm [bs .ProjectRoot ] {
410
- bs .Constraint = c .Intersect (bs .Constraint )
411
- }
531
+ err = errMultipleFailures
412
532
}
413
533
414
- // Only if we have a non-rev and non-plain version do/can we display
415
- // anything wrt the version's updateability.
416
- if bs .Version != nil && bs .Version .Type () != gps .IsVersion {
417
- c , has := p .Manifest .Constraints [proj .Ident ().ProjectRoot ]
418
- if ! has {
419
- c .Constraint = gps .Any ()
420
- }
421
- // TODO: This constraint is only the constraint imposed by the
422
- // current project, not by any transitive deps. As a result,
423
- // transitive project deps will always show "any" here.
424
- bs .Constraint = c .Constraint
425
-
426
- vl , err := sm .ListVersions (proj .Ident ())
427
- if err == nil {
428
- gps .SortPairedForUpgrade (vl )
429
-
430
- for _ , v := range vl {
431
- // Because we've sorted the version list for
432
- // upgrade, the first version we encounter that
433
- // matches our constraint will be what we want.
434
- if c .Constraint .Matches (v ) {
435
- bs .Latest = v .Revision ()
436
- break
437
- }
438
- }
534
+ // Count ListVersions error because we get partial results when
535
+ // this happens.
536
+ errCount = len (errListVerCh )
537
+ if ctx .Verbose {
538
+ for err := range errListVerCh {
539
+ ctx .Err .Println (err .Error ())
439
540
}
541
+ ctx .Err .Println ()
440
542
}
543
+ }
441
544
442
- out .BasicLine (& bs )
545
+ // A map of ProjectRoot and *BasicStatus. This is used in maintain the
546
+ // order of BasicStatus in output by collecting all the BasicStatus and
547
+ // then using them in order.
548
+ bsMap := make (map [string ]* BasicStatus )
549
+ for bs := range bsCh {
550
+ bsMap [bs .ProjectRoot ] = bs
443
551
}
444
- logger .Println ()
552
+
553
+ // Use the collected BasicStatus in outputter.
554
+ for _ , proj := range slp {
555
+ out .BasicLine (bsMap [string (proj .Ident ().ProjectRoot )])
556
+ }
557
+
445
558
out .BasicFooter ()
446
559
447
- return digestMismatch , hasMissingPkgs , nil
560
+ return false , false , errCount , err
448
561
}
449
562
450
563
// Hash digest mismatch may indicate that some deps are no longer
@@ -453,7 +566,6 @@ func runStatusAll(ctx *dep.Ctx, out outputter, p *dep.Project, sm gps.SourceMana
453
566
//
454
567
// It's possible for digests to not match, but still have a correct
455
568
// lock.
456
- digestMismatch = true
457
569
rm , _ := ptree .ToReachMap (true , true , false , nil )
458
570
459
571
external := rm .FlattenFn (paths .IsStandardImportPath )
@@ -486,7 +598,7 @@ func runStatusAll(ctx *dep.Ctx, out outputter, p *dep.Project, sm gps.SourceMana
486
598
ctx .Err .Printf ("\t %s: %s\n " , fail .ex , fail .err .Error ())
487
599
}
488
600
489
- return digestMismatch , hasMissingPkgs , errors .New ("address issues with undeducible import paths to get more status information" )
601
+ return true , false , 0 , errors .New ("address issues with undeducible import paths to get more status information" )
490
602
}
491
603
492
604
out .MissingHeader ()
@@ -506,7 +618,7 @@ outer:
506
618
}
507
619
out .MissingFooter ()
508
620
509
- return digestMismatch , hasMissingPkgs , nil
621
+ return true , hasMissingPkgs , 0 , nil
510
622
}
511
623
512
624
func formatVersion (v gps.Version ) string {
0 commit comments