package main import ( "flag" "fmt" "os" "os/exec" "strconv" "syscall" ) const usage = `workspace - Use a containerized workspace COMMANDS ` func EntryPoint(command []string) error { if len(command) == 0 { command = []string{"/bin/bash", "-i"} } uid, err := strconv.ParseInt(os.Getenv("WORKSPACE_USER"), 10, 64) if err != nil { return fmt.Errorf( "Failed to parse UID from WORKSPACE_USER env var with contents \"%s\". Error: %w", os.Getenv("WORKSPACE_USER"), err, ) } err = syscall.Setuid(int(uid)) if err != nil { return err } path, err := exec.LookPath(command[0]) if err != nil { return err } SetEnvVars() return syscall.Exec(path, command, os.Environ()) } func Run(detach bool, command []string) error { workDir, err := os.Getwd() if err != nil { return err } uid := strconv.Itoa(syscall.Getuid()) home := os.Getenv("HOME") dockerCommand := []string{ "/bin/docker", "run", "--network=host", "--workdir=" + workDir, "--user=0:" + strconv.Itoa(syscall.Getgid()), "-e", "DISPLAY=" + os.Getenv("DISPLAY"), "-e", "WORKSPACE_USER=" + uid, "-e", "HOME=" + home, "-h", os.Getenv("HOSTNAME"), "-v", "/var/run/docker.sock:/var/run/docker.sock", "-v", "/etc/subuid:/etc/subuid:ro", "-v", "/etc/subgid:/etc/subgid:ro", "-v", "/etc/hosts:/etc/hosts:ro", "-v", "/etc/passwd:/etc/passwd:ro", "-v", "/etc/resolv.conf:/etc/resolv.conf:ro", "-v", "/etc/sudoers:/etc/sudoers:ro", "-v", "/etc/group:/etc/group:ro", "-v", home + ":" + home, "-e", "SSH_AGENT_LAUNCHER=" + os.Getenv("SSH_AGENT_LAUNCHER"), "-e", "SSH_AUTH_SOCK=" + os.Getenv("SSH_AUTH_SOCK"), "-e", "PULSE_SERVER=unix:/run/user/" + uid + "/pulse/native", "-e", "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/" + uid + "/bus", "-e", "TERM=" + os.Getenv("TERM"), "-v", "/tmp/.X11-unix:/tmp/.X11-unix", "-v", "/run/user/" + uid + ":/run/user/" + uid, "-v", "/dev/snd", } if detach { dockerCommand = append(dockerCommand, "--detach") } if fileInfo, _ := os.Stdout.Stat(); (fileInfo.Mode() & os.ModeCharDevice) != 0 { dockerCommand = append(dockerCommand, "-ti") } fileInfo, err := os.Lstat(home) if err != nil { return fmt.Errorf( "Failed to determine whether home directory is a symbolic link: %w", err, ) } if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink { destination, err := os.Readlink(home) if err != nil { return fmt.Errorf( "Failed to determine whether home directory is a symbolic link: %w", err, ) } dockerCommand = append(dockerCommand, "-v", destination+":"+destination) } dockerCommand = append(dockerCommand, "hugotty/workspace:latest") dockerCommand = append(dockerCommand, command...) return syscall.Exec(dockerCommand[0], dockerCommand, os.Environ()) } func SetEnvVars() { home := os.Getenv("HOME") os.Setenv("PATH", home+"/bin:"+os.Getenv("PATH")) os.Setenv("PATH", home+"/dotfiles/bin:"+os.Getenv("PATH")) // PHP os.Setenv("PATH", home+"/.config/composer/vendor/bin:"+os.Getenv("PATH")) // Node os.Setenv("PATH", home+"/.npm_packages/bin:"+os.Getenv("PATH")) // Perl os.Setenv("PATH", home+"/perl5/perlbrew/bin:"+os.Getenv("PATH")) // Rust os.Setenv("PATH", home+"/.cargo/bin:"+os.Getenv("PATH")) // Locally installed python packages os.Setenv("PATH", home+"/.local/bin:"+os.Getenv("PATH")) // Home for virtualenvs os.Setenv("WORKON_HOME", home+"/.local/share/virtualenvs") // GO Stuff os.Setenv("GOPATH", home+"/go") os.Setenv("PATH", home+"/go/bin:"+os.Getenv("PATH")) os.Setenv("GOPRIVATE", "git.snorba.art") } func main() { var cmd string if len(os.Args) > 1 { cmd = os.Args[1] } var detach bool run := flag.NewFlagSet("run", flag.ExitOnError) run.BoolVar( &detach, "detach", false, "Whether or not to detach from the container afnter running the command", ) switch cmd { case "entrypoint": err := EntryPoint(os.Args[2:]) if err != nil { fmt.Println("error executing entrypoint command", err) os.Exit(1) } case "run": err := run.Parse(os.Args[2:]) if err != nil { fmt.Println(err) os.Exit(1) } err = Run(detach, run.Args()) if err != nil { fmt.Println("error running command in container", err) os.Exit(1) } default: fmt.Println(usage) fmt.Println("run COMMAND: Start workspace container and run COMMAND within it") run.Usage() } }