Last active
May 6, 2017 19:15
-
-
Save progrium/a902a78f5b7d224ec58c2b8e8c55d94b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
cd /tmp | |
rm hello.cmd | |
rm http.cmd | |
ssh cmd.io :delete hello | |
ssh cmd.io :delete http | |
source ~/.profile.d/demo | |
clear | |
[wait 2]# Let's build two quick commands with Cmd.io! | |
# First,[wait] a simple command that says hello. | |
vim [wait 20]hello.cmd[wait 3][begin-pty] | |
[wait 2]i[wait]#!cmd[wait] alpine bash[wait] | |
#!/bin/bash | |
[wait 20]echo "Hello, ${1:-world}!"[wait 20] | |
[wait][esc]:wq[wait][end-pty] | |
cat hello.cmd | ssh cmd.io :create[wait 10] hello | |
[wait 10]ssh cmd.io hello[wait 2] | |
ssh cmd.io hello[wait 5] everybody[wait 2] | |
[wait 10]clear | |
[wait]# Next, an HTTP API client with jq built-in.[wait 10] | |
# We'll also use stateful environment vars.[wait 5] | |
vim http.cmd[wait 5][begin-pty] | |
[wait]i[wait]#!cmd[wait] alpine bash curl jq[wait 10] | |
#!/bin/bash | |
curl -Ls ${BASE}${1}[wait 30] | jq -C ${2:-.} | |
[wait 20][esc]:wq[wait][end-pty] | |
cat http.cmd | ssh cmd.io :create http | |
[wait 5]ssh cmd.io http [wait 2]slack.com/api/api.test | |
ssh cmd.io http slack.com/api/api.test [wait 2].ok | |
[wait 10] | |
ssh cmd.io :env http set BASE=slack.com/api/[wait 10] | |
ssh cmd.io http api.test[wait] | |
[wait] | |
[wait 5]ssh cmd.io :env http set BASE=api.github.com/[wait 10] | |
ssh cmd.io http orgs/gliderlabs | |
[wait 5]ssh cmd.io http orgs/gliderlabs [wait].name | |
ssh cmd.io http orgs/gliderlabs .location | |
[wait] | |
[wait]clear | |
[wait]# Now let's see how we can run commands over HTTP. | |
# This will require using access tokens. | |
[wait] | |
TOKEN=$(ssh cmd.io :tokens new)[wait 5] | |
echo $TOKEN | |
[wait 5]ssh cmd.io :access http grant $TOKEN[wait 20] | |
[wait 10]curl -u $TOKEN:[wait 5] "https://cmd.io/run/progrium/http?args=orgs/gliderlabs" | |
[wait 5]curl -u $TOKEN: "https://cmd.io/run/progrium/http?args=orgs/gliderlabs+.name" | |
[wait 20] | |
[wait 20]ssh cmd.io :tokens rm $TOKEN | |
exit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bufio" | |
"bytes" | |
"io" | |
"log" | |
"math/rand" | |
"os" | |
"os/exec" | |
"regexp" | |
"strconv" | |
"strings" | |
"syscall" | |
"time" | |
"github.com/creack/termios/raw" | |
"github.com/kr/pty" | |
"github.com/pkg/term" // redundant but meh | |
) | |
func getch() []byte { | |
t, _ := term.Open("/dev/tty") | |
term.RawMode(t) | |
bytes := make([]byte, 3) | |
numRead, err := t.Read(bytes) | |
t.Restore() | |
t.Close() | |
if err != nil { | |
return nil | |
} | |
return bytes[0:numRead] | |
} | |
const ( | |
ETX = 3 | |
EOT = 4 | |
BS = 8 | |
LF = 10 | |
CR = 13 | |
ESC = 27 | |
DEL = 127 | |
CSI = 91 | |
CUU = 65 | |
CUD = 66 | |
CUF = 67 | |
CUB = 68 | |
) | |
const Speed = 2.0 | |
const Min = 45 | |
const Max = 145 | |
var HardChars = []byte("$%^&*()_+{}|<>:") | |
var MediumChars = []byte("~?!/") | |
var blockChildren = true | |
var ops []func() ([]byte, error) | |
var opIdx = -1 | |
var opRe = regexp.MustCompile("\\[[^\\]]+\\]") | |
var r = rand.New(rand.NewSource(99)) | |
func Delay(min, max int) { | |
ms := min + r.Intn(max-min) | |
mult := 1000.0 * (1.0 / Speed) | |
delay := ms * int(mult) | |
time.Sleep(time.Duration(delay) * time.Microsecond) | |
} | |
func WaitForChildren(parent int) bool { | |
cmd := exec.Command("pgrep", "-P", strconv.Itoa(parent)) | |
out, err := cmd.CombinedOutput() | |
if err != nil { | |
return false | |
} | |
if len(out) == 0 { | |
return false | |
} | |
procs := strings.Split(strings.Trim(string(out), " \n"), "\n") | |
for _, procid := range procs { | |
pid, err := strconv.Atoi(procid) | |
if err != nil { | |
continue | |
} | |
p, err := os.FindProcess(pid) | |
if err == nil { | |
var e error | |
for e == nil { | |
time.Sleep(20 * time.Millisecond) | |
e = p.Signal(syscall.Signal(0)) | |
} | |
} | |
} | |
return true | |
} | |
func SignalChildren(parent int, signal os.Signal) bool { | |
cmd := exec.Command("pgrep", "-P", strconv.Itoa(parent)) | |
out, err := cmd.CombinedOutput() | |
if err != nil { | |
return false | |
} | |
if len(out) == 0 { | |
return false | |
} | |
procs := strings.Split(strings.Trim(string(out), " \n"), "\n") | |
for _, procid := range procs { | |
pid, err := strconv.Atoi(procid) | |
if err != nil { | |
continue | |
} | |
p, err := os.FindProcess(pid) | |
if err == nil { | |
var e error | |
for e == nil { | |
time.Sleep(20 * time.Millisecond) | |
e = p.Signal(signal) | |
} | |
} | |
} | |
return true | |
} | |
func main() { | |
if len(os.Args) < 2 { | |
log.Fatal("Script argument required") | |
} | |
file, err := os.Open(os.Args[1]) | |
if err != nil { | |
panic(err) | |
} | |
shell := os.Getenv("SHELL") | |
if shell == "" { | |
shell = "/bin/sh" | |
} | |
c := exec.Command(shell) | |
f, err := pty.Start(c) | |
if err != nil { | |
panic(err) | |
} | |
tios, err := raw.MakeRaw(f.Fd()) | |
if err != nil { | |
panic(err) | |
} | |
defer raw.TcSetAttr(f.Fd(), tios) | |
go func() { | |
out := io.MultiWriter(os.Stdout, f) | |
lines := bufio.NewScanner(file) | |
for lines.Scan() { | |
line := opRe.ReplaceAllFunc(lines.Bytes(), func(op []byte) []byte { | |
opStr := strings.Trim(string(op), "[]") | |
args := strings.Split(opStr, " ") | |
if args[0] == "wait" { | |
x := 1 | |
if len(args) > 1 { | |
i, err := strconv.Atoi(args[1]) | |
if err != nil { | |
panic(err) | |
} | |
x = i | |
} | |
ops = append(ops, func() ([]byte, error) { | |
Delay(400*x, 500*x) | |
return []byte{}, nil | |
}) | |
opIdx++ | |
return []byte{ESC, byte(opIdx)} | |
} | |
if args[0] == "pause" { | |
ops = append(ops, func() ([]byte, error) { | |
getch() | |
return []byte{}, nil | |
}) | |
opIdx++ | |
return []byte{ESC, byte(opIdx)} | |
} | |
if args[0] == "sigint" { | |
ops = append(ops, func() ([]byte, error) { | |
SignalChildren(c.Process.Pid, os.Interrupt) | |
return []byte{}, nil | |
}) | |
opIdx++ | |
return []byte{ESC, byte(opIdx)} | |
} | |
if args[0] == "esc" { | |
ops = append(ops, func() ([]byte, error) { | |
return []byte{ESC}, nil | |
}) | |
opIdx++ | |
return []byte{ESC, byte(opIdx)} | |
} | |
if args[0] == "begin-stdin" { | |
ops = append(ops, func() ([]byte, error) { | |
blockChildren = false | |
return []byte{}, nil | |
}) | |
opIdx++ | |
return []byte{ESC, byte(opIdx)} | |
} | |
if args[0] == "end-stdin" { | |
ops = append(ops, func() ([]byte, error) { | |
blockChildren = true | |
return []byte{}, nil | |
}) | |
opIdx++ | |
return []byte{ESC, byte(opIdx)} | |
} | |
if args[0] == "begin-pty" { | |
ops = append(ops, func() ([]byte, error) { | |
blockChildren = false | |
os.Stdout.Write([]byte{LF}) | |
out = f | |
return []byte{}, nil | |
}) | |
opIdx++ | |
return []byte{ESC, byte(opIdx)} | |
} | |
if args[0] == "end-pty" { | |
ops = append(ops, func() ([]byte, error) { | |
blockChildren = true | |
out = io.MultiWriter(os.Stdout, f) | |
return []byte{}, nil | |
}) | |
opIdx++ | |
return []byte{ESC, byte(opIdx)} | |
} | |
return []byte{} | |
}) | |
if len(line) > 2 { | |
Delay(Min*3, Max*6) | |
} else { | |
Delay(Min, Max) | |
} | |
var space, comment, op bool | |
for _, b := range line { | |
bs := []byte{b} | |
if !op && b == ESC { | |
op = true | |
continue | |
} | |
if op { | |
op = false | |
nb, err := ops[b]() | |
if err != nil { | |
panic(err) | |
} | |
bs = nb | |
} | |
if len(bs) == 0 { | |
continue | |
} | |
if space { | |
Delay(Min*2, Max*3) | |
} else { | |
if bytes.Contains(HardChars, bs) { | |
Delay(Min*8, Max*8) | |
} else if bytes.Contains(MediumChars, bs) { | |
Delay(Min*4, Max*4) | |
} else { | |
Delay(Min, Max) | |
} | |
} | |
space = bs[0] == ' ' | |
out.Write(bs) | |
if bs[0] == '#' { | |
comment = true | |
} | |
} | |
if comment { | |
Delay(Min*10, Max*10) | |
comment = false | |
} else { | |
Delay(Min*5, Max*8) | |
} | |
out.Write([]byte("\n")) | |
if blockChildren && WaitForChildren(c.Process.Pid) { | |
Delay(Min*5, Max*5) | |
} | |
} | |
Delay(Min*20, Max*20) | |
f.Write([]byte{EOT}) | |
}() | |
//go io.Copy(f, os.Stdin) | |
io.Copy(os.Stdout, f) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment