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