mirror of
https://github.com/asciinema/asciinema.git
synced 2025-12-16 11:48:13 +01:00
Add rec command
This commit is contained in:
34
api/api.go
Normal file
34
api/api.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Api interface {
|
||||
CreateAsciicast(*Asciicast) (string, error)
|
||||
}
|
||||
|
||||
type Asciicast struct {
|
||||
Command string
|
||||
Title string
|
||||
Rows int
|
||||
Cols int
|
||||
Shell string
|
||||
Username string
|
||||
Term string
|
||||
Stdout io.Reader
|
||||
}
|
||||
|
||||
func NewAsciicast(command, title string, rows, cols int, stdout io.Reader) *Asciicast {
|
||||
return &Asciicast{
|
||||
Command: command,
|
||||
Title: title,
|
||||
Rows: rows,
|
||||
Cols: cols,
|
||||
Shell: os.Getenv("SHELL"),
|
||||
Username: os.Getenv("USER"),
|
||||
Term: os.Getenv("TERM"),
|
||||
Stdout: stdout,
|
||||
}
|
||||
}
|
||||
111
commands/rec.go
Normal file
111
commands/rec.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/asciinema/asciinema-cli/api"
|
||||
"github.com/asciinema/asciinema-cli/cli"
|
||||
"github.com/asciinema/asciinema-cli/terminal"
|
||||
"github.com/asciinema/asciinema-cli/util"
|
||||
)
|
||||
|
||||
func Record(flags *flag.FlagSet, cfg *util.Config) cli.Command {
|
||||
command := RecordCommand{}
|
||||
|
||||
flags.StringVar(
|
||||
&command.Command,
|
||||
"c",
|
||||
defaultRecCommand(cfg.Record.Command),
|
||||
"command to record, defaults to $SHELL",
|
||||
)
|
||||
|
||||
flags.StringVar(
|
||||
&command.Title,
|
||||
"t",
|
||||
"",
|
||||
"set title of the asciicast",
|
||||
)
|
||||
|
||||
flags.BoolVar(
|
||||
&command.NoConfirm,
|
||||
"y",
|
||||
false,
|
||||
"don't ask for upload confirmation",
|
||||
)
|
||||
|
||||
return &command
|
||||
}
|
||||
|
||||
type RecordCommand struct {
|
||||
Command string
|
||||
Title string
|
||||
NoConfirm bool
|
||||
Terminal terminal.Terminal
|
||||
Api api.Api
|
||||
}
|
||||
|
||||
func (c *RecordCommand) Execute(args []string) error {
|
||||
rows, cols, _ := c.Terminal.Size()
|
||||
if rows > 30 || cols > 120 {
|
||||
util.Warningf("Current terminal size is %vx%v.", cols, rows)
|
||||
util.Warningf("It may be too big to be properly replayed on smaller screens.")
|
||||
util.Warningf("You can now resize it. Press <Enter> to start recording.")
|
||||
bufio.NewReader(os.Stdin).ReadString('\n')
|
||||
}
|
||||
|
||||
util.Printf("Asciicast recording started.")
|
||||
util.Printf("Hit ctrl-d or type \"exit\" to finish.")
|
||||
|
||||
stdout := &StdoutStream{}
|
||||
|
||||
err := c.Terminal.Record(c.Command, stdout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
util.Printf("Asciicast recording finished.")
|
||||
|
||||
// TODO: ask for upload confirmation
|
||||
|
||||
rows, cols, _ = c.Terminal.Size()
|
||||
asciicast := api.NewAsciicast(c.Command, c.Title, rows, cols, stdout.Reader())
|
||||
|
||||
url, err := c.Api.CreateAsciicast(asciicast)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(url)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func defaultRecCommand(recCommand string) string {
|
||||
if recCommand == "" {
|
||||
recCommand = os.Getenv("SHELL")
|
||||
|
||||
if recCommand == "" {
|
||||
recCommand = "/bin/sh"
|
||||
}
|
||||
}
|
||||
|
||||
return recCommand
|
||||
}
|
||||
|
||||
type StdoutStream struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (s *StdoutStream) Write(p []byte) (int, error) {
|
||||
s.data = append(s.data, p...)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (s *StdoutStream) Reader() io.Reader {
|
||||
return bytes.NewReader(s.data)
|
||||
}
|
||||
96
commands/rec_test.go
Normal file
96
commands/rec_test.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/asciinema/asciinema-cli/api"
|
||||
"github.com/asciinema/asciinema-cli/commands"
|
||||
)
|
||||
|
||||
type testTerminal struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (t *testTerminal) Size() (int, int, error) {
|
||||
return 15, 40, nil
|
||||
}
|
||||
|
||||
func (t *testTerminal) Record(command string, stdoutCopy io.Writer) error {
|
||||
if t.err != nil {
|
||||
return t.err
|
||||
}
|
||||
|
||||
stdoutCopy.Write([]byte("hello"))
|
||||
return nil
|
||||
}
|
||||
|
||||
type testApi struct {
|
||||
err error
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (a *testApi) CreateAsciicast(asciicast *api.Asciicast) (string, error) {
|
||||
if asciicast.Command != "ls" {
|
||||
a.t.Errorf("expected command to be set on asciicast")
|
||||
}
|
||||
|
||||
if asciicast.Title != "listing" {
|
||||
a.t.Errorf("expected title to be set on asciicast")
|
||||
}
|
||||
|
||||
if asciicast.Rows != 15 {
|
||||
a.t.Errorf("expected rows to be set on asciicast")
|
||||
}
|
||||
|
||||
if asciicast.Cols != 40 {
|
||||
a.t.Errorf("expected cols to be set on asciicast")
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
buf.ReadFrom(asciicast.Stdout)
|
||||
stdout := buf.String()
|
||||
if stdout != "hello" {
|
||||
a.t.Errorf("expected recorded stdout to be set on asciicast")
|
||||
}
|
||||
|
||||
if a.err != nil {
|
||||
return "", a.err
|
||||
}
|
||||
|
||||
return "http://the/url", nil
|
||||
}
|
||||
|
||||
func TestRecordCommand_Execute(t *testing.T) {
|
||||
recErr := errors.New("can't record")
|
||||
apiErr := errors.New("can't upload")
|
||||
|
||||
var tests = []struct {
|
||||
recordError error
|
||||
apiError error
|
||||
expectedError error
|
||||
}{
|
||||
{nil, nil, nil},
|
||||
{recErr, nil, recErr},
|
||||
{nil, apiErr, apiErr},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
terminal := &testTerminal{err: test.recordError}
|
||||
api := &testApi{err: test.apiError, t: t}
|
||||
|
||||
command := &commands.RecordCommand{
|
||||
Command: "ls",
|
||||
Title: "listing",
|
||||
Terminal: terminal,
|
||||
Api: api,
|
||||
}
|
||||
|
||||
err := command.Execute(nil)
|
||||
if err != test.expectedError {
|
||||
t.Errorf("expected error %v, got %v", test.expectedError, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
8
terminal/terminal.go
Normal file
8
terminal/terminal.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package terminal
|
||||
|
||||
import "io"
|
||||
|
||||
type Terminal interface {
|
||||
Size() (int, int, error)
|
||||
Record(string, io.Writer) error
|
||||
}
|
||||
Reference in New Issue
Block a user