@@ -275,6 +275,72 @@ func (sw SafeWriter) validate(root string, sm gps.SourceManager) error {
275
275
return nil
276
276
}
277
277
278
+ // createStagingLinksForPackage looks into <pkgPath>/staging/src and symlinks those staging
279
+ // repositories into vendor/. If packages only contain subpackages, a deeper level is linked.
280
+ // E.g. if <pkgPath>/staging/src/github.com has no real files and only directories the algorithm
281
+ // looks at a deeper level, e.g. at pkgPath/staging/src/github.com/foo. If foo has files (for
282
+ // example *.go files), a relative symlink is created:
283
+ //
284
+ // tempVendor/github.com/foo -> .../../../staging/src/github.com/foo
285
+ //
286
+ // The tempVendor directory corresponds logically to root/vendor. But, because th caller of
287
+ // this func creates vendor/ dirs as temporary directories first, we have to support this
288
+ // distinction.
289
+ func createStagingLinksForPackage (root , pkgPath string , tempVendor string ) error {
290
+ stagingRoot := filepath .Join (pkgPath , "staging" , "src" )
291
+ if _ , err := os .Lstat (stagingRoot ); os .IsNotExist (err ) {
292
+ return nil
293
+ }
294
+
295
+ logicalVendor := filepath .Join (root , "vendor" )
296
+ return filepath .Walk (stagingRoot , func (wp string , fi os.FileInfo , err error ) error {
297
+ if err != nil && err != filepath .SkipDir {
298
+ return err
299
+ }
300
+ if ! fi .IsDir () || strings .HasPrefix (fi .Name (), "." ) {
301
+ return filepath .SkipDir
302
+ }
303
+
304
+ // find out whether the directory has directories only
305
+ f , err := os .Open (wp )
306
+ if err != nil {
307
+ return err
308
+ }
309
+ ffis , err := f .Readdir (0 )
310
+ f .Close ()
311
+ if err != nil {
312
+ return err
313
+ }
314
+ filesFound := false
315
+ for _ , ffi := range ffis {
316
+ if ! ffi .IsDir () && ! strings .HasPrefix (ffi .Name (), "." ) {
317
+ filesFound = true
318
+ break
319
+ }
320
+ }
321
+
322
+ // dive deeper if no files where found, only more subdirectories
323
+ if ! filesFound {
324
+ return nil
325
+ }
326
+
327
+ // files were found. Let's create a symlink in the vendor dir
328
+ ip := strings .TrimPrefix (wp , stagingRoot )
329
+ if err := os .MkdirAll (filepath .Join (tempVendor , filepath .Dir (ip )), 0755 ); err != nil {
330
+ return err
331
+ }
332
+ rel , err := filepath .Rel (filepath .Join (logicalVendor , filepath .Dir (ip )), wp )
333
+ if err != nil {
334
+ return err
335
+ }
336
+ if err := os .Symlink (rel , filepath .Join (tempVendor , ip )); err != nil {
337
+ return err
338
+ }
339
+
340
+ return filepath .SkipDir
341
+ })
342
+ }
343
+
278
344
// Write saves some combination of config yaml, lock, and a vendor tree.
279
345
// root is the absolute path of root dir in which to write.
280
346
// sm is only required if vendor is being written.
@@ -336,6 +402,20 @@ func (sw *SafeWriter) Write(root string, sm gps.SourceManager, noExamples bool)
336
402
if err != nil {
337
403
return errors .Wrap (err , "error while writing out vendor tree" )
338
404
}
405
+
406
+ // symlink staging repos of the root package
407
+ if err := createStagingLinksForPackage (root , root , filepath .Join (td , "vendor" )); err != nil {
408
+ return errors .Wrap (err , "error creating staging symlinks" )
409
+ }
410
+
411
+ // symlink staging repos of the vendored packages. These are exported into the temporary
412
+ // directory td. So we can use that as the root.
413
+ for _ , p := range sw .Lock .Projects () {
414
+ pkgPath := filepath .Join (td , "vendor" , string (p .Ident ().ProjectRoot ))
415
+ if err := createStagingLinksForPackage (td , pkgPath , filepath .Join (td , "vendor" )); err != nil {
416
+ return errors .Wrap (err , fmt .Sprintf ("error creating staging symlinks for vendored package %q" , p .Ident ().ProjectRoot ))
417
+ }
418
+ }
339
419
}
340
420
341
421
// Ensure vendor/.git is preserved if present
0 commit comments