@@ -22,6 +22,7 @@ import (
2222 "context"
2323 "encoding/json"
2424 "fmt"
25+ "io"
2526 "log/slog"
2627 "os"
2728 "os/exec"
@@ -118,21 +119,26 @@ type GenerateRequest struct {
118119// ReleaseRequest contains all the information required for a language
119120// container to run the release command.
120121type ReleaseRequest struct {
121- // cfg is a pointer to the [config.Config] struct, holding general configuration
122+ // Cfg is a pointer to the [config.Config] struct, holding general configuration
122123 // values parsed from flags or environment variables.
123124 Cfg * config.Config
124- // state is a pointer to the [config.LibrarianState] struct, representing
125+ // GlobalConfig is a pointer to the [config.GlobalConfig] struct, holding
126+ // global files configuration in a language repository.
127+ GlobalConfig * config.GlobalConfig
128+ // State is a pointer to the [config.LibrarianState] struct, representing
125129 // the overall state of the generation and release pipeline.
126130 State * config.LibrarianState
127- // libraryID specifies the ID of the library to release.
131+ // LibraryID specifies the ID of the library to release.
128132 LibraryID string
129- // libraryID specifies the version of the library to release.
133+ // LibraryVersion specifies the version of the library to release.
130134 LibraryVersion string
131- // output specifies the empty output directory into which the command should
132- // generate code
135+ // Output specifies the empty output directory into which the command should
136+ // generate code.
133137 Output string
134- // RepoDir is the local root directory of the language repository.
135- RepoDir string
138+ // partialRepoDir is the local root directory of language repository contains
139+ // files that make up libraries and global files.
140+ // This is the directory that container can access.
141+ partialRepoDir string
136142}
137143
138144// New constructs a Docker instance which will invoke the specified
@@ -247,7 +253,7 @@ func (c *Docker) Configure(ctx context.Context, request *ConfigureRequest) (stri
247253
248254// ReleaseInit initiates a release for a given language repository.
249255func (c * Docker ) ReleaseInit (ctx context.Context , request * ReleaseRequest ) error {
250- requestFilePath := filepath .Join (request .RepoDir , config .LibrarianDir , config .ReleaseInitRequest )
256+ requestFilePath := filepath .Join (request .Cfg . Repo , config .LibrarianDir , config .ReleaseInitRequest )
251257 if err := writeLibrarianState (request .State , requestFilePath ); err != nil {
252258 return err
253259 }
@@ -269,10 +275,14 @@ func (c *Docker) ReleaseInit(ctx context.Context, request *ReleaseRequest) error
269275 commandArgs = append (commandArgs , fmt .Sprintf ("--library-version=%s" , request .LibraryVersion ))
270276 }
271277
272- librarianDir := filepath .Join (request .RepoDir , config .LibrarianDir )
278+ if err := setupPartialRepo (request ); err != nil {
279+ return err
280+ }
281+
282+ librarianDir := filepath .Join (request .partialRepoDir , config .LibrarianDir )
273283 mounts := []string {
274284 fmt .Sprintf ("%s:/librarian" , librarianDir ),
275- fmt .Sprintf ("%s:/repo:ro" , request .RepoDir ), // readonly volume
285+ fmt .Sprintf ("%s:/repo:ro" , request .partialRepoDir ), // readonly volume
276286 fmt .Sprintf ("%s:/output" , request .Output ),
277287 }
278288
@@ -337,6 +347,96 @@ func (c *Docker) runCommand(cmdName string, args ...string) error {
337347 return err
338348}
339349
350+ // setupPartialRepo copies the following files from the [config.Config.Repo] to
351+ // partialRepoDir in the given ReleaseRequest:
352+ //
353+ // 1. all directories that make up all libraries, or one library, if the library
354+ // ID is specified.
355+ //
356+ // 2. the .librarian directory.
357+ //
358+ // 3. global files declared in config.yaml.
359+ func setupPartialRepo (request * ReleaseRequest ) error {
360+ if request .partialRepoDir == "" {
361+ request .partialRepoDir = filepath .Join (request .Cfg .WorkRoot , "release-init" )
362+ }
363+ dst := request .partialRepoDir
364+ src := request .Cfg .Repo
365+ if err := os .MkdirAll (dst , 0755 ); err != nil {
366+ return fmt .Errorf ("failed to make directory: %w" , err )
367+ }
368+
369+ for _ , library := range request .State .Libraries {
370+ // Only copy files that make up one library.
371+ if request .LibraryID != "" {
372+ if library .ID == request .LibraryID {
373+ if err := copyOneLibrary (dst , src , library ); err != nil {
374+ return err
375+ }
376+ break
377+ }
378+ continue
379+ }
380+
381+ // Copy all files make up all libraries.
382+ if err := copyOneLibrary (dst , src , library ); err != nil {
383+ return err
384+ }
385+ }
386+
387+ // Copy the .librarian directory.
388+ if err := os .CopyFS (
389+ filepath .Join (dst , config .LibrarianDir ),
390+ os .DirFS (filepath .Join (src , config .LibrarianDir ))); err != nil {
391+ return fmt .Errorf ("failed to copy librarian dir to %s: %w" , dst , err )
392+ }
393+
394+ // Copy global files declared in global config.
395+ for _ , globalFile := range request .GlobalConfig .GlobalFilesAllowlist {
396+ dstPath := filepath .Join (dst , globalFile .Path )
397+ srcPath := filepath .Join (src , globalFile .Path )
398+ if err := copyFile (dstPath , srcPath ); err != nil {
399+ return err
400+ }
401+ }
402+
403+ return nil
404+ }
405+
406+ func copyOneLibrary (dst , src string , library * config.LibraryState ) error {
407+ for _ , srcRoot := range library .SourceRoots {
408+ dstPath := filepath .Join (dst , srcRoot )
409+ srcPath := filepath .Join (src , srcRoot )
410+ if err := os .CopyFS (dstPath , os .DirFS (srcPath )); err != nil {
411+ return fmt .Errorf ("failed to copy %s to %s: %w" , library .ID , dstPath , err )
412+ }
413+ }
414+
415+ return nil
416+ }
417+
418+ func copyFile (dst , src string ) (err error ) {
419+ sourceFile , err := os .Open (src )
420+ if err != nil {
421+ return fmt .Errorf ("failed to open file: %q: %w" , src , err )
422+ }
423+ defer sourceFile .Close ()
424+
425+ if err := os .MkdirAll (filepath .Dir (dst ), 0755 ); err != nil {
426+ return fmt .Errorf ("failed to make directory: %s" , src )
427+ }
428+
429+ destinationFile , err := os .Create (dst )
430+ if err != nil {
431+ return fmt .Errorf ("failed to create file: %s" , dst )
432+ }
433+ defer destinationFile .Close ()
434+
435+ _ , err = io .Copy (destinationFile , sourceFile )
436+
437+ return err
438+ }
439+
340440func writeLibraryState (state * config.LibrarianState , libraryID , jsonFilePath string ) error {
341441 if err := os .MkdirAll (filepath .Dir (jsonFilePath ), 0755 ); err != nil {
342442 return fmt .Errorf ("failed to make directory: %w" , err )
0 commit comments