Skip to content
This repository was archived by the owner on Jan 17, 2021. It is now read-only.

Create base #1

Merged
merged 4 commits into from
Apr 19, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
vendor
3 changes: 3 additions & 0 deletions .sail/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM codercom/ubuntu-dev-go

LABEL project_root "~/go/src/go.coder.com"
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# sshcode

`sshcode` is a CLI to automatically install and run [code-server](https://github.com/codercom/code-server) over SSH.

![Demo](/demo.gif)

## Install

Chrome is recommended.

```bash
go get go.coder.com/sshcode
```

## Usage

```bash
sshcode [email protected]
# Starts code-server on dev.kwc.io and opens in a new browser window.
```
Binary file added demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module go.coder.com/sshcode

go 1.12

require (
go.coder.com/flog v0.0.0-20190129195112-eaed154a0db8
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be // indirect
)
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
go.coder.com/flog v0.0.0-20190129195112-eaed154a0db8 h1:PtQ3moPi4EAz3cyQhkUs1IGIXa2QgJpP60yMjOdu0kk=
go.coder.com/flog v0.0.0-20190129195112-eaed154a0db8/go.mod h1:83JsYgXYv0EOaXjIMnaZ1Fl6ddNB3fJnDZ/8845mUJ8=
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be h1:mI+jhqkn68ybP0ORJqunXn+fq+Eeb4hHKqLQcFICjAc=
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
129 changes: 129 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package main

import (
"context"
"errors"
"flag"
"fmt"
"net"
"net/http"
"os"
"os/exec"
"strconv"
"time"

"go.coder.com/flog"
)

func main() {
flag.Usage = func() {
fmt.Printf(`Usage: %v HOST [SSH ARGS...]

Start code-server over SSH.
More info: https://github.com/codercom/sshcode
`, os.Args[0])
}

flag.Parse()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker but in the future, you don't need the flag library, you have no flags. You can directly use os.Args.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nvm, flag automatically gives you a -h flag which is nice. So this is the way to go.

host := flag.Arg(0)

if host == "" {
// If no host is specified output the usage.
flag.Usage()
os.Exit(1)
}

flog.Info("ensuring code-server is updated...")

// Downloads the latest code-server and allows it to be executed.
sshCmd := exec.Command("ssh",
"-tt",
host,
`/bin/bash -c 'set -euxo pipefail || exit 1
mkdir -p ~/bin
wget -q https://codesrv-ci.cdr.sh/latest-linux -O ~/bin/code-server
chmod +x ~/bin/code-server
'`,
)
output, err := sshCmd.CombinedOutput()
if err != nil {
flog.Fatal("failed to update code-server: %v: %s", err, string(output))
}

flog.Info("starting code-server...")
localPort, err := scanAvailablePort()
if err != nil {
flog.Fatal("failed to scan available port: %v", err)
}

// Starts code-server and forwards the remote port.
sshCmd = exec.Command("ssh",
"-tt",
"-q",
"-L",
localPort+":localhost:"+localPort,
host,
"~/bin/code-server --host 127.0.0.1 --allow-http --no-auth --port="+localPort,
)
sshCmd.Stdout = os.Stdout
sshCmd.Stderr = os.Stderr
err = sshCmd.Start()
if err != nil {
flog.Fatal("failed to start code-server: %v", err)
}

var openCmd *exec.Cmd
url := "http://127.0.0.1:" + localPort
if commandExists("google-chrome") {
openCmd = exec.Command("google-chrome", "--app="+url, "--disable-extensions", "--disable-plugins")
} else if commandExists("firefox") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

command for macOS is open.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this opens a chrome window with some special flags so its more of an app window

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can open chrome with open and pass args. I can't remember the details but its something like open -a Chrome my_args.

openCmd = exec.Command("firefox", "--url="+url, "-safe-mode")
}

ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
for {
err := ctx.Err()
if err != nil {
flog.Fatal("code-server didn't start in time %v", err)
}
// Waits for code-server to be available before opening the browser.
r, _ := http.NewRequest("GET", url, nil)
r = r.WithContext(ctx)
resp, err := http.DefaultClient.Do(r)
if err != nil {
continue
}
resp.Body.Close()
break
}

err = openCmd.Start()
if err != nil {
flog.Fatal("failed to open browser: %v", err)
}
sshCmd.Wait()
}

// Checks if a command exists locally.
func commandExists(name string) bool {
_, err := exec.LookPath(name)
return err == nil
}

// scanAvailablePort scans 1024-4096 until an available port is found.
func scanAvailablePort() (string, error) {
for port := 1024; port < 4096; port++ {
l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
// If we have an error the port is taken.
port++
continue
}
_ = l.Close()

return strconv.Itoa(port), nil
}

return "", errors.New("no ports available")
}