From 26dd49d1baf02c93e0090deb3dd87f60902bc6f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20S=C3=B6derlund?= Date: Thu, 20 Jul 2023 17:00:09 +0200 Subject: [PATCH] feat: add sgpython and sgpoetry tools For Python projects managed by Poetry. --- tools/sgpoetry/command.go | 48 ++++++++++++++++++++++++++ tools/sgpython/command.go | 71 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 tools/sgpoetry/command.go create mode 100644 tools/sgpython/command.go diff --git a/tools/sgpoetry/command.go b/tools/sgpoetry/command.go new file mode 100644 index 00000000..637921cb --- /dev/null +++ b/tools/sgpoetry/command.go @@ -0,0 +1,48 @@ +package sgpoetry + +import ( + "context" + "os" + "os/exec" + "path/filepath" + + "go.einride.tech/sage/sg" + "go.einride.tech/sage/sgtool" + "go.einride.tech/sage/tools/sgpython" +) + +const ( + name = "poetry" + version = "1.5.1" +) + +func Command(ctx context.Context, args ...string) *exec.Cmd { + sg.Deps(ctx, PrepareCommand) + return sg.Command(ctx, sg.FromBinDir(name), args...) +} + +func PrepareCommand(ctx context.Context) error { + toolDir := sg.FromToolsDir(name, version) + poetry := filepath.Join(toolDir, "bin", name) + if _, err := os.Stat(poetry); err == nil { + if _, err := sgtool.CreateSymlink(poetry); err != nil { + return err + } + return nil + } + // See: https://python-poetry.org/docs/#installing-manually + if err := sgpython.Command(ctx, "-m", "venv", toolDir).Run(); err != nil { + return err + } + pip := filepath.Join(toolDir, "bin", "pip") + if err := sg.Command(ctx, pip, "install", "-U", "pip", "setuptools").Run(); err != nil { + return err + } + if err := sg.Command(ctx, pip, "install", name+"=="+version).Run(); err != nil { + return err + } + if _, err := sgtool.CreateSymlink(poetry); err != nil { + return err + } + return nil +} diff --git a/tools/sgpython/command.go b/tools/sgpython/command.go new file mode 100644 index 00000000..8ebfdee5 --- /dev/null +++ b/tools/sgpython/command.go @@ -0,0 +1,71 @@ +package sgpython + +import ( + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + + "go.einride.tech/sage/sg" + "go.einride.tech/sage/sgtool" + "go.einride.tech/sage/tools/sggit" +) + +const ( + name = "python" + version = "3.11.3" + pyenvVersion = "2.3.18" +) + +func Command(ctx context.Context, args ...string) *exec.Cmd { + sg.Deps(ctx, PrepareCommand) + return sg.Command(ctx, sg.FromBinDir(name), args...) +} + +func PrepareCommand(ctx context.Context) error { + toolDir := sg.FromToolsDir(name, version) + pyenvDir := filepath.Join(toolDir, "pyenv") + binDir := filepath.Join(pyenvDir, "versions", version, "bin") + pythonFromPyenv := filepath.Join(binDir, "python") + if _, err := os.Stat(pythonFromPyenv); err == nil { + if _, err := sgtool.CreateSymlink(pythonFromPyenv); err != nil { + return err + } + return nil + } else if systemPython3, err := exec.LookPath("python3"); err == nil { + // Special case: Avoid building from source if we already have Python 3 on the system. + symlink := filepath.Join(sg.FromBinDir(), name) + if _, err := os.Lstat(symlink); err == nil { + if err := os.Remove(symlink); err != nil { + return err + } + } + return os.Symlink(systemPython3, symlink) + } + if err := os.RemoveAll(pyenvDir); err != nil { + return err + } + if err := sggit.Command( + ctx, + "clone", + "--depth", + "1", + "--branch", + "v"+pyenvVersion, + "https://github.com/pyenv/pyenv.git", + pyenvDir, + ).Run(); err != nil { + return err + } + cmd := sg.Command(ctx, "bin/pyenv", "install", version) + cmd.Dir = pyenvDir + cmd.Env = append(cmd.Env, fmt.Sprintf("PYENV_ROOT=%s", pyenvDir)) + if err := cmd.Run(); err != nil { + return err + } + if _, err := sgtool.CreateSymlink(pythonFromPyenv); err != nil { + return err + } + return nil +}