github.com/replit/upm@v0.0.0-20240423230255-9ce4fc3ea24c/internal/nix/nix.go (about)

     1  package nix
     2  
     3  import (
     4  	"context"
     5  	_ "embed"
     6  	"encoding/json"
     7  	"errors"
     8  	"io"
     9  	"log"
    10  	"os"
    11  	"os/exec"
    12  	"path"
    13  
    14  	"github.com/replit/upm/internal/api"
    15  	"github.com/replit/upm/internal/util"
    16  )
    17  
    18  type NixEditorDepType string
    19  
    20  const (
    21  	Regular NixEditorDepType = "regular"
    22  )
    23  
    24  type NixEditorOp struct {
    25  	Op      string           `json:"op"`
    26  	DepType NixEditorDepType `json:"dep_type"`
    27  	Dep     string           `json:"dep"`
    28  }
    29  
    30  type ReplitNixAdd struct {
    31  	Deps []string `json:"deps,omitempty"`
    32  }
    33  
    34  var (
    35  	python_map_var map[string]ReplitNixAdd
    36  
    37  	python_map_var_loaded bool
    38  
    39  	//go:embed python_map.json
    40  	python_map_json []byte
    41  )
    42  
    43  func DefaultInstallReplitNixSystemDependencies(context.Context, []api.PkgName) {
    44  	// do nothing by default, if the language doesn't implement a system
    45  	// dependency mapping, there is no work to be done.
    46  }
    47  
    48  func python_map() map[string]ReplitNixAdd {
    49  	if python_map_var_loaded {
    50  		return python_map_var
    51  	}
    52  	err := json.Unmarshal(python_map_json, &python_map_var)
    53  	if err != nil {
    54  		log.Fatal("Error during Unmarshal(): ", err)
    55  	}
    56  	python_map_var_loaded = true
    57  	return python_map_var
    58  }
    59  
    60  func PythonNixDeps(pack string) ReplitNixAdd {
    61  	val, ok := python_map()[pack]
    62  	if !ok {
    63  		return ReplitNixAdd{}
    64  	}
    65  	return val
    66  }
    67  
    68  func ReplitNixAddToNixEditorOps(replitNixAdd ReplitNixAdd) []NixEditorOp {
    69  	result := []NixEditorOp{}
    70  	for _, dep := range replitNixAdd.Deps {
    71  		result = append(result, NixEditorOp{Op: "add", Dep: dep, DepType: Regular})
    72  	}
    73  	return result
    74  }
    75  
    76  func RunNixEditorOps(ops []NixEditorOp) {
    77  	repl_home := os.Getenv("REPL_HOME")
    78  	if repl_home == "" {
    79  		util.DieInitializationError("REPL_HOME was not set")
    80  	}
    81  	path := path.Join(repl_home, "replit.nix")
    82  
    83  	cmd := exec.Command("nix-editor", "--path", path)
    84  	stdin, err := cmd.StdinPipe()
    85  	if err != nil {
    86  		util.DieSubprocess("couldn't make stdin pipe to nix-editor")
    87  	}
    88  	stdout, err := cmd.StdoutPipe()
    89  	if err != nil {
    90  		util.DieSubprocess("couldn't make stdout pipe to nix-editor")
    91  	}
    92  
    93  	err = cmd.Start()
    94  	if err != nil {
    95  		util.DieSubprocess("nix-editor error: %s", err)
    96  	}
    97  
    98  	encoder := json.NewEncoder(stdin)
    99  	for _, op := range ops {
   100  		err := encoder.Encode(op)
   101  		if err != nil {
   102  			util.DieProtocol("unable to turn op into json: %v error: %s", op, err)
   103  		}
   104  	}
   105  	err = stdin.Close()
   106  	if err != nil {
   107  		util.DieSubprocess("unable to write to nix-editor")
   108  	}
   109  
   110  	decoder := json.NewDecoder(stdout)
   111  	for {
   112  		var nixEditorStatus struct {
   113  			Status string
   114  			Data   string
   115  		}
   116  		err := decoder.Decode(&nixEditorStatus)
   117  		if err != nil {
   118  			if errors.Is(err, io.EOF) {
   119  				break
   120  			}
   121  			util.DieSubprocess("unexpected nix-editor output: %s", err)
   122  		}
   123  		if nixEditorStatus.Status != "success" {
   124  			util.DieSubprocess("nix-editor error: %s", nixEditorStatus.Data)
   125  		}
   126  	}
   127  	stdout.Close()
   128  
   129  	err = cmd.Wait()
   130  	if err != nil {
   131  		util.DieSubprocess("nix-editor error: %s", err)
   132  	}
   133  }