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 all 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=
136 changes: 136 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
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, 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)
}

url := "http://127.0.0.1:" + localPort
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
for {
if ctx.Err() != nil {
flog.Fatal("code-server didn't start in time %v", ctx.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
}

openBrowser(url)
sshCmd.Wait()
}

func openBrowser(url string) {
var openCmd *exec.Cmd
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.

I take it back. If neither of these exist, this program will panic.

Copy link
Contributor

Choose a reason for hiding this comment

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

openCmd = exec.Command("firefox", "--url="+url, "-safe-mode")
} else {
flog.Info("unable to find a browser to open: sshcode only supports firefox and chrome")

return
}

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

// 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")
}