@@ -568,25 +568,47 @@ func (s *Runtime) Init(ctx context.Context, req *runtimev0.InitRequest) (*runtim
568568 }
569569 s .Wool .Debug ("successful init of runner" )
570570
571+ if err := s .initOpenAPI (ctx , env , cacheLocation ); err != nil {
572+ return s .Base .Runtime .InitErrorf (err , "cannot generate Open API document" )
573+ }
574+ return s .Base .Runtime .InitResponse ()
575+ }
576+
577+ // openAPIScript is the project's OpenAPI generator, relative to its source
578+ // location: the scaffold ships it, and it dumps the schema of the app's REST
579+ // routes to openapi/api.swagger.json.
580+ const openAPIScript = "src/openapi.py"
581+
582+ // initOpenAPI regenerates the OpenAPI document when the project ships the
583+ // generator and src/main.py changed since the last run. A project without the
584+ // generator publishes no OpenAPI document: its HTTP endpoint serves the probes
585+ // and its API is on another face (gRPC, Connect), so there is nothing to
586+ // generate and nothing is run.
587+ func (s * Runtime ) initOpenAPI (ctx context.Context , env runners.RunnerEnvironment , cacheLocation string ) error {
588+ script := path .Join (s .Service .SourceLocation , openAPIScript )
589+ hasScript , err := shared .FileExists (ctx , script )
590+ if err != nil {
591+ return err
592+ }
593+ if ! hasScript {
594+ s .Wool .Debug ("no OpenAPI generator: the HTTP endpoint publishes no OpenAPI document" , wool .Field ("path" , script ))
595+ return nil
596+ }
571597 openAPI := builders .NewDependencies ("api" ,
572598 builders .NewDependency (path .Join (s .Service .SourceLocation , "src/main.py" ))).WithCache (cacheLocation )
573- openApiUpdate , err := openAPI .Updated (ctx )
574- if err != nil {
575- return s . Base . Runtime . InitError ( err )
599+ openAPIUpdate , err := openAPI .Updated (ctx )
600+ if err != nil || ! openAPIUpdate {
601+ return err
576602 }
577-
578- if openApiUpdate {
579- s .Infof ("generating Open API document" )
580- if err := s .GenerateOpenAPI (ctx , env ); err != nil {
581- return s .Base .Runtime .InitErrorf (err , "cannot generate Open API document" )
582- }
583- if err := openAPI .UpdateCache (ctx ); err != nil {
584- s .Wool .Warn ("cannot update openapi cache" , wool .ErrField (err ))
585- }
586- s .Wool .Debug ("generate Open API done" )
603+ s .Infof ("generating Open API document" )
604+ if err := s .GenerateOpenAPI (ctx , env ); err != nil {
605+ return err
587606 }
588-
589- return s .Base .Runtime .InitResponse ()
607+ if err := openAPI .UpdateCache (ctx ); err != nil {
608+ s .Wool .Warn ("cannot update openapi cache" , wool .ErrField (err ))
609+ }
610+ s .Wool .Debug ("generate Open API done" )
611+ return nil
590612}
591613
592614func (s * Runtime ) Start (ctx context.Context , req * runtimev0.StartRequest ) (* runtimev0.StartResponse , error ) {
@@ -1358,7 +1380,7 @@ func (s *Runtime) EventHandler(event code.Change) error {
13581380// the OpenAPI spec. Convention: the project ships a small openapi.py that
13591381// imports src.main and dumps the schema. See templates/factory.
13601382func (s * Runtime ) GenerateOpenAPI (ctx context.Context , env runners.RunnerEnvironment ) error {
1361- proc , err := env .NewProcess ("uv" , "run" , "python" , "src/openapi.py" )
1383+ proc , err := env .NewProcess ("uv" , "run" , "python" , openAPIScript )
13621384 if err != nil {
13631385 return s .Wool .Wrapf (err , "cannot create openapi runner" )
13641386 }
0 commit comments