@@ -37,12 +37,14 @@ type Command string
3737
3838// The set of commands passed to the language container, in a single place to avoid typos.
3939const (
40- // CommandGenerate performs generation for a configured library.
41- CommandGenerate Command = "generate"
4240 // CommandBuild builds a library.
4341 CommandBuild Command = "build"
4442 // CommandConfigure configures a new API as a library.
4543 CommandConfigure Command = "configure"
44+ // CommandGenerate performs generation for a configured library.
45+ CommandGenerate Command = "generate"
46+ // CommandReleaseInit performs release for a library.
47+ CommandReleaseInit Command = "release-init"
4648)
4749
4850// Docker contains all the information required to run language-specific
@@ -61,44 +63,41 @@ type Docker struct {
6163 run func (args ... string ) error
6264}
6365
64- // GenerateRequest contains all the information required for a language
65- // container to run the generate command.
66- type GenerateRequest struct {
66+ // BuildRequest contains all the information required for a language
67+ // container to run the build command.
68+ type BuildRequest struct {
6769 // cfg is a pointer to the [config.Config] struct, holding general configuration
6870 // values parsed from flags or environment variables.
6971 Cfg * config.Config
7072 // state is a pointer to the [config.LibrarianState] struct, representing
7173 // the overall state of the generation and release pipeline.
7274 State * config.LibrarianState
73- // apiRoot specifies the root directory of the API specification repo.
74- ApiRoot string
75- // libraryID specifies the ID of the library to generate
75+ // libraryID specifies the ID of the library to build.
7676 LibraryID string
77- // output specifies the empty output directory into which the command should
78- // generate code
79- Output string
8077 // RepoDir is the local root directory of the language repository.
8178 RepoDir string
8279}
8380
84- // BuildRequest contains all the information required for a language
85- // container to run the build command.
86- type BuildRequest struct {
81+ // ConfigureRequest contains all the information required for a language
82+ // container to run the configure command.
83+ type ConfigureRequest struct {
8784 // cfg is a pointer to the [config.Config] struct, holding general configuration
8885 // values parsed from flags or environment variables.
8986 Cfg * config.Config
9087 // state is a pointer to the [config.LibrarianState] struct, representing
9188 // the overall state of the generation and release pipeline.
9289 State * config.LibrarianState
93- // libraryID specifies the ID of the library to build
90+ // apiRoot specifies the root directory of the API specification repo.
91+ ApiRoot string
92+ // libraryID specifies the ID of the library to configure.
9493 LibraryID string
9594 // RepoDir is the local root directory of the language repository.
9695 RepoDir string
9796}
9897
99- // ConfigureRequest contains all the information required for a language
100- // container to run the configure command.
101- type ConfigureRequest struct {
98+ // GenerateRequest contains all the information required for a language
99+ // container to run the generate command.
100+ type GenerateRequest struct {
102101 // cfg is a pointer to the [config.Config] struct, holding general configuration
103102 // values parsed from flags or environment variables.
104103 Cfg * config.Config
@@ -107,8 +106,31 @@ type ConfigureRequest struct {
107106 State * config.LibrarianState
108107 // apiRoot specifies the root directory of the API specification repo.
109108 ApiRoot string
110- // libraryID specifies the ID of the library to generate
109+ // libraryID specifies the ID of the library to generate.
111110 LibraryID string
111+ // output specifies the empty output directory into which the command should
112+ // generate code
113+ Output string
114+ // RepoDir is the local root directory of the language repository.
115+ RepoDir string
116+ }
117+
118+ // ReleaseRequest contains all the information required for a language
119+ // container to run the release command.
120+ type ReleaseRequest struct {
121+ // cfg is a pointer to the [config.Config] struct, holding general configuration
122+ // values parsed from flags or environment variables.
123+ Cfg * config.Config
124+ // state is a pointer to the [config.LibrarianState] struct, representing
125+ // the overall state of the generation and release pipeline.
126+ State * config.LibrarianState
127+ // libraryID specifies the ID of the library to release.
128+ LibraryID string
129+ // libraryID specifies the version of the library to release.
130+ LibraryVersion string
131+ // output specifies the empty output directory into which the command should
132+ // generate code
133+ Output string
112134 // RepoDir is the local root directory of the language repository.
113135 RepoDir string
114136}
@@ -132,7 +154,7 @@ func New(workRoot, image, uid, gid string) (*Docker, error) {
132154// library.
133155func (c * Docker ) Generate (ctx context.Context , request * GenerateRequest ) error {
134156 jsonFilePath := filepath .Join (request .RepoDir , config .LibrarianDir , config .GenerateRequest )
135- if err := writeRequest (request .State , request .LibraryID , jsonFilePath ); err != nil {
157+ if err := writeLibraryState (request .State , request .LibraryID , jsonFilePath ); err != nil {
136158 return err
137159 }
138160 defer func (name string ) {
@@ -165,7 +187,7 @@ func (c *Docker) Generate(ctx context.Context, request *GenerateRequest) error {
165187// the Librarian state file for the repository with a root of repoRoot.
166188func (c * Docker ) Build (ctx context.Context , request * BuildRequest ) error {
167189 jsonFilePath := filepath .Join (request .RepoDir , config .LibrarianDir , config .BuildRequest )
168- if err := writeRequest (request .State , request .LibraryID , jsonFilePath ); err != nil {
190+ if err := writeLibraryState (request .State , request .LibraryID , jsonFilePath ); err != nil {
169191 return err
170192 }
171193 defer func (name string ) {
@@ -194,7 +216,7 @@ func (c *Docker) Build(ctx context.Context, request *BuildRequest) error {
194216// Returns the configured library id if the command succeeds.
195217func (c * Docker ) Configure (ctx context.Context , request * ConfigureRequest ) (string , error ) {
196218 requestFilePath := filepath .Join (request .RepoDir , config .LibrarianDir , config .ConfigureRequest )
197- if err := writeRequest (request .State , request .LibraryID , requestFilePath ); err != nil {
219+ if err := writeLibraryState (request .State , request .LibraryID , requestFilePath ); err != nil {
198220 return "" , err
199221 }
200222 defer func () {
@@ -223,6 +245,44 @@ func (c *Docker) Configure(ctx context.Context, request *ConfigureRequest) (stri
223245 return request .LibraryID , nil
224246}
225247
248+ // ReleaseInit initiates a release for a given language repository.
249+ func (c * Docker ) ReleaseInit (ctx context.Context , request * ReleaseRequest ) error {
250+ requestFilePath := filepath .Join (request .RepoDir , config .LibrarianDir , config .ReleaseInitRequest )
251+ if err := writeLibrarianState (request .State , requestFilePath ); err != nil {
252+ return err
253+ }
254+ defer func () {
255+ err := os .Remove (requestFilePath )
256+ if err != nil {
257+ slog .Warn ("fail to remove file" , slog .String ("name" , requestFilePath ), slog .Any ("err" , err ))
258+ }
259+ }()
260+ commandArgs := []string {
261+ "--librarian=/librarian" ,
262+ "--repo=/repo" ,
263+ "--output=/output" ,
264+ }
265+ if request .LibraryID != "" {
266+ commandArgs = append (commandArgs , fmt .Sprintf ("--library=%s" , request .LibraryID ))
267+ }
268+ if request .LibraryVersion != "" {
269+ commandArgs = append (commandArgs , fmt .Sprintf ("--library-version=%s" , request .LibraryVersion ))
270+ }
271+
272+ librarianDir := filepath .Join (request .RepoDir , config .LibrarianDir )
273+ mounts := []string {
274+ fmt .Sprintf ("%s:/librarian" , librarianDir ),
275+ fmt .Sprintf ("%s:/repo:ro" , request .RepoDir ), // readonly volume
276+ fmt .Sprintf ("%s:/output" , request .Output ),
277+ }
278+
279+ if err := c .runDocker (ctx , request .Cfg , CommandReleaseInit , mounts , commandArgs ); err != nil {
280+ return err
281+ }
282+
283+ return nil
284+ }
285+
226286func (c * Docker ) runDocker (_ context.Context , cfg * config.Config , command Command , mounts []string , commandArgs []string ) (err error ) {
227287 mounts = maybeRelocateMounts (cfg , mounts )
228288
@@ -277,13 +337,13 @@ func (c *Docker) runCommand(cmdName string, args ...string) error {
277337 return err
278338}
279339
280- func writeRequest (state * config.LibrarianState , libraryID , jsonFilePath string ) error {
340+ func writeLibraryState (state * config.LibrarianState , libraryID , jsonFilePath string ) error {
281341 if err := os .MkdirAll (filepath .Dir (jsonFilePath ), 0755 ); err != nil {
282342 return fmt .Errorf ("failed to make directory: %w" , err )
283343 }
284344 jsonFile , err := os .Create (jsonFilePath )
285345 if err != nil {
286- return fmt .Errorf ("failed to create generate request JSON file: %w" , err )
346+ return fmt .Errorf ("failed to create JSON file: %w" , err )
287347 }
288348 defer jsonFile .Close ()
289349
@@ -304,3 +364,23 @@ func writeRequest(state *config.LibrarianState, libraryID, jsonFilePath string)
304364
305365 return nil
306366}
367+
368+ func writeLibrarianState (state * config.LibrarianState , jsonFilePath string ) error {
369+ if err := os .MkdirAll (filepath .Dir (jsonFilePath ), 0755 ); err != nil {
370+ return fmt .Errorf ("failed to make directory: %w" , err )
371+ }
372+ jsonFile , err := os .Create (jsonFilePath )
373+ if err != nil {
374+ return fmt .Errorf ("failed to create JSON file: %w" , err )
375+ }
376+ defer jsonFile .Close ()
377+
378+ data , err := json .MarshalIndent (state , "" , " " )
379+ if err != nil {
380+ return fmt .Errorf ("failed to marshal state to JSON: %w" , err )
381+ }
382+
383+ _ , err = jsonFile .Write (data )
384+
385+ return err
386+ }
0 commit comments