From 00b83fd9d3e5c8a55cfaf9baca0fd43d993b34f7 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Fri, 13 Oct 2023 16:44:38 -0700 Subject: [PATCH] use json interface of nix-editor --- internal/backends/python/python.go | 15 ++--- internal/nix/nix.go | 100 ++++++++++++++++++++++------- internal/nix/nix_test.go | 20 ++---- 3 files changed, 90 insertions(+), 45 deletions(-) diff --git a/internal/backends/python/python.go b/internal/backends/python/python.go index f5ce6677..a7aa0805 100644 --- a/internal/backends/python/python.go +++ b/internal/backends/python/python.go @@ -306,21 +306,20 @@ func pythonMakeBackend(name string, python string) api.LanguageBackend { }), Guess: func() (map[api.PkgName]bool, bool) { return guess(python) }, InstallReplitNixSystemDependencies: func(pkgs []api.PkgName) { + ops := []nix.NixEditorOp{} for _, pkg := range pkgs { deps := nix.PythonNixDeps(string(pkg)) - cmds := nix.ReplitNixAddToNixEditorCmds(deps) - nix.RunNixEditorCmds(cmds) + ops = append(ops, nix.ReplitNixAddToNixEditorOps(deps)...) } - specfilePkgs, err := listSpecfile() - if err != nil { - return - } + // Ignore the error here, because if we can't read the specfile, + // we still want to add the deps from above at least. + specfilePkgs, _ := listSpecfile() for pkg := range specfilePkgs { deps := nix.PythonNixDeps(string(pkg)) - cmds := nix.ReplitNixAddToNixEditorCmds(deps) - nix.RunNixEditorCmds(cmds) + ops = append(ops, nix.ReplitNixAddToNixEditorOps(deps)...) } + nix.RunNixEditorOps(ops) }, } } diff --git a/internal/nix/nix.go b/internal/nix/nix.go index 53f99287..64130ef1 100644 --- a/internal/nix/nix.go +++ b/internal/nix/nix.go @@ -1,16 +1,33 @@ package nix import ( + "bytes" _ "embed" "encoding/json" + "errors" + "io" "log" "os" + "os/exec" "path" "github.com/replit/upm/internal/api" "github.com/replit/upm/internal/util" ) +type NixEditorDepType string + +const ( + Regular NixEditorDepType = "regular" + Python NixEditorDepType = "python" +) + +type NixEditorOp struct { + Op string `json:"op"` + DepType NixEditorDepType `json:"dep_type"` + Dep string `json:"dep"` +} + type ReplitNixAdd struct { Deps []string `json:"deps,omitempty"` PythonLibraryDeps []string `json:"libdeps,omitempty"` @@ -50,41 +67,76 @@ func PythonNixDeps(pack string) ReplitNixAdd { return val } -func ReplitNixAddToNixEditorCmds(replitNixAdd ReplitNixAdd) [][]string { - result := [][]string{} +func ReplitNixAddToNixEditorOps(replitNixAdd ReplitNixAdd) []NixEditorOp { + result := []NixEditorOp{} + for _, dep := range replitNixAdd.Deps { + result = append(result, NixEditorOp{Op: "add", Dep: dep, DepType: Regular}) + } + for _, dep := range replitNixAdd.PythonLibraryDeps { + result = append(result, NixEditorOp{Op: "add", Dep: dep, DepType: Python}) + } + return result +} + +func RunNixEditorOps(ops []NixEditorOp) { repl_home := os.Getenv("REPL_HOME") if repl_home == "" { util.Die("REPL_HOME was not set") } path := path.Join(repl_home, "replit.nix") - for _, dep := range replitNixAdd.Deps { - result = append(result, []string{"nix-editor", "--path", path, "--add", dep}) + cmd := exec.Command("nix-editor", "--path", path) + stdin, err := cmd.StdinPipe() + if err != nil { + util.Die("couldn't make stdin pipe to nix-editor") } - - for _, dep := range replitNixAdd.PythonLibraryDeps { - result = append(result, []string{"nix-editor", "--path", path, "--dep-type", "python", "--add", dep}) + stdout, err := cmd.StdoutPipe() + if err != nil { + util.Die("couldn't make stdout pipe to nix-editor") } - return result -} - -func RunNixEditorCmds(cmds [][]string) { - for _, cmd := range cmds { - output := util.GetCmdOutput(cmd) - - var nixEditorStatus struct { - Status string - Data string - } + err = cmd.Start() + if err != nil { + util.Die("nix-editor error: %s", err) + } - if err := json.Unmarshal(output, &nixEditorStatus); err != nil { - util.Die("unexpected nix-editor output: %s", err) + in := &bytes.Buffer{} + encoder := json.NewEncoder(in) + for _, op := range ops { + err := encoder.Encode(op) + if err != nil { + util.Die("unable to turn op into json: %v error: %s", op, err) } - if nixEditorStatus.Status != "success" { - util.Die("nix-editor error: %s", nixEditorStatus.Data) + } + _, err = stdin.Write(in.Bytes()) + if err != nil { + util.Die("unable to write to nix-editor") + } + stdin.Close() + + go func() { + decoder := json.NewDecoder(stdout) + for { + var nixEditorStatus struct { + Status string + Data string + } + err := decoder.Decode(&nixEditorStatus) + if err != nil { + if errors.Is(err, io.EOF) { + break + } + util.Die("unexpected nix-editor output: %s", err) + } + if nixEditorStatus.Status != "success" { + util.Die("nix-editor error: %s", nixEditorStatus.Data) + } } - // otherwise we have success and don't need to output - // anything + stdout.Close() + }() + + err = cmd.Wait() + if err != nil { + util.Die("nix-editor error: %s", err) } } diff --git a/internal/nix/nix_test.go b/internal/nix/nix_test.go index 6d5acd0a..83794a87 100644 --- a/internal/nix/nix_test.go +++ b/internal/nix/nix_test.go @@ -1,7 +1,6 @@ package nix import ( - "os" "testing" assert "github.com/stretchr/testify/assert" @@ -11,12 +10,12 @@ func TestNixPythonMap(t *testing.T) { deps := PythonNixDeps("pycairo") assert.Equal(t, - &ReplitNixAdd{ + ReplitNixAdd{ Deps: []string{ "pkgs.pkg-config", "pkgs.cairo", }, - PythonLibraryDeps: []string{}, + PythonLibraryDeps: nil, }, deps) } @@ -32,18 +31,13 @@ func TestReplitNixAddToNixEditorCmds(t *testing.T) { }, } - err := os.Setenv("REPL_HOME", "/tmp") - assert.NoError(t, err) + cmds := ReplitNixAddToNixEditorOps(*deps) - cmds := ReplitNixAddToNixEditorCmds(*deps) - - expected := [][]string{ - []string{"nix-editor", "--path", "/tmp/replit.nix", "--add", "pkgs.pkg-config"}, - []string{"nix-editor", "--path", "/tmp/replit.nix", "--add", "pkgs.cairo"}, - []string{"nix-editor", "--path", "/tmp/replit.nix", "--dep-type", "python", "--add", "pkgs.lib"}, + expected := []NixEditorOp{ + {Op: "add", DepType: Regular, Dep: "pkgs.pkg-config"}, + {Op: "add", DepType: Regular, Dep: "pkgs.cairo"}, + {Op: "add", DepType: Python, Dep: "pkgs.lib"}, } assert.Equal(t, expected, cmds) } - -// func integration test with python add calling nix adds