Skip to content

Commit

Permalink
analysis/build: Add assistant plugin
Browse files Browse the repository at this point in the history
The user can specify an assistant directory with some python code in which is
called in a container with the app directory mounted.

This assistant script can generate or modify code in the app directory
before it is further analysed and ran.
  • Loading branch information
richiejp committed Sep 2, 2024
1 parent 4f1788b commit 1858679
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 8 deletions.
File renamed without changes.
File renamed without changes.
File renamed without changes.
21 changes: 21 additions & 0 deletions examples/assistants/test/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import os

file_path = '/app/__main__.py'
file_content = """from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run(debug=True, host="0.0.0.0")
"""

os.makedirs(os.path.dirname(file_path), exist_ok=True)

with open(file_path, 'w') as file:
file.write(file_content)

print(f"File written to {file_path}")
Empty file.
12 changes: 7 additions & 5 deletions go/cmd/ay/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ type Globals struct {
}

type PushCmd struct {
Path string `arg:"" optional:"" name:"path" help:"Path to the source code to be pushed" type:"path"`
Path string `arg:"" optional:"" name:"path" help:"Path to the source code to be pushed" type:"path"`
Assistant string `env:"AYUP_ASSISTANT_PATH" help:"The location of the assistant plugin source if any" type:"path"`

Host string `env:"AYUP_PUSH_HOST" default:"localhost:50051" help:"The location of a service we can push to"`
P2pPrivKey string `env:"AYUP_CLIENT_P2P_PRIV_KEY" help:"Secret encryption key produced by 'ay key new'"`
Expand All @@ -45,10 +46,11 @@ type PushCmd struct {
func (s *PushCmd) Run(g Globals) (err error) {
pprof.Do(g.Ctx, pprof.Labels("command", "push"), func(ctx context.Context) {
p := push.Pusher{
Tracer: g.Tracer,
Host: s.Host,
P2pPrivKey: s.P2pPrivKey,
SrcDir: s.Path,
Tracer: g.Tracer,
Host: s.Host,
P2pPrivKey: s.P2pPrivKey,
AssistantDir: s.Assistant,
SrcDir: s.Path,
}

err = p.Run(pprof.WithLabels(g.Ctx, pprof.Labels("command", "push")))
Expand Down
7 changes: 4 additions & 3 deletions go/cmd/ay/srv_supported.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ func (s *DaemonStartCmd) Run(g Globals) (err error) {
}

r := srv.Srv{
SrcDir: filepath.Join(tmp, "src"),
Host: s.Host,
P2pPrivKey: s.P2pPrivKey,
AssistantDir: filepath.Join(tmp, "ass"),
SrcDir: filepath.Join(tmp, "src"),
Host: s.Host,
P2pPrivKey: s.P2pPrivKey,
}

var authedClients []peer.ID
Expand Down
77 changes: 77 additions & 0 deletions go/srv/analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,77 @@ func (s *logWriter) Close() error {

var ErrUserCancelled = errors.New("user cancelled")

func (s *Srv) assistantLlb(ctx context.Context) (*llb.Definition, error) {
assLocal := llb.Local("assistant")
appLocal := llb.Local("app")
st := pythonSlimLlb().
File(llb.Mkdir("/assistant", 0755)).
Dir("/assistant")

st = st.File(llb.Copy(assLocal, "requirements.txt", "."))

appMnt := llb.AddMount(
"/app",
appLocal,
)
st = pythonSlimPip(st, "install -r requirements.txt").
File(llb.Copy(assLocal, ".", ".")).
Run(llb.Shlex("python __main__.py"), appMnt).Root()

dt, err := st.Marshal(ctx, llb.LinuxAmd64)
if err != nil {
return nil, terror.Errorf(ctx, "marshal: %w", err)
}

return dt, nil
}

func (s *Srv) callAssistant(ctx context.Context, stream pb.Srv_AnalysisServer, c *client.Client) error {
ctx, span := trace.Span(ctx, "assistant")
defer span.End()

internalError := mkInternalError(ctx, stream)

b := func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
def, err := s.assistantLlb(ctx)
if err != nil {
return nil, internalError("mkllb: %w", err)
}

r, err := c.Solve(ctx, gateway.SolveRequest{
Definition: def.ToPB(),
})
if err != nil {
return nil, internalError("client solve: %w", err)
}

return r, nil
}

statusChan := buildkitStatusSender(ctx, stream)
assistantFS, err := fsutil.NewFS(s.AssistantDir)
if err != nil {
return internalError("fsutil newfs: %w", err)
}
appFS, err := fsutil.NewFS(s.SrcDir)
if err != nil {
return internalError("fsutil newfs: %w", err)
}

_, err = c.Build(ctx, client.SolveOpt{
LocalMounts: map[string]fsutil.FS{
"assistant": assistantFS,
"app": appFS,
},
}, "ayup", b, statusChan)

if err != nil {
return internalError("client build: %w", err)
}

return nil
}

func (s *Srv) Analysis(stream pb.Srv_AnalysisServer) error {
ctx := stream.Context()
span := tr.SpanFromContext(ctx)
Expand Down Expand Up @@ -240,6 +311,12 @@ func (s *Srv) Analysis(stream pb.Srv_AnalysisServer) error {
return sendError("premature choice")
}

if s.AssistantDir != "" {
if err := s.callAssistant(ctx, stream, c); err != nil {
return err
}
}

requirements_path := filepath.Join(s.SrcDir, "requirements.txt")

if ok, err := s.useDockerfile(ctx, stream); ok || err != nil {
Expand Down

0 comments on commit 1858679

Please sign in to comment.