Skip to content

Commit

Permalink
Implemented reading scripts from stdin
Browse files Browse the repository at this point in the history
Fixes #1.

This PR starts the ability to read from stdin.
The main problem though is that os.Stdin is consumed
for the script body's read and EOF encountered.
This means that we won't have interactive input.
  • Loading branch information
odeke-em committed Jan 24, 2016
1 parent c3477e2 commit 137a1d4
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 16 deletions.
34 changes: 23 additions & 11 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"bufio"
"errors"
"flag"
"fmt"
"io"
"log"
"net/http"
Expand Down Expand Up @@ -46,24 +47,35 @@ func main() {
)
flag.Parse()

var script *Script
var err error
if len(flag.Args()) < 1 {
flag.Usage()
log.Panic("No script specified")
}
script, err = FNewScript(os.Stdin)
if err != nil {
log.Panic(err)
}
} else {
_, err := os.Stat(*target)
if err != nil && os.IsNotExist(err) {
log.Panic("Script executable does not exist")
}
log.Println("Using script executable", *target)

if _, err := os.Stat(*target); os.IsNotExist(err) {
log.Panic("Script executable does not exist")
// download the script, store it someplace temporary
script, err = NewScript(flag.Arg(0))
if err != nil {
log.Panic(err)
}
}
log.Println("Using script executable", *target)

// download the script, store it someplace temporary
script, err := NewScript(flag.Arg(0))
if err != nil {
log.Panic(err)
}
defer os.Remove(script.Name())
log.Println("Script saved to", script.Name())

// sanity check
if script == nil {
log.Panic(fmt.Errorf("bug on: script is nil"))
}

// let the user look at it if they want
if cont := script.Inspect(*inspect, *editor); !cont {
log.Panic("Exiting without running", script.Name())
Expand Down
47 changes: 42 additions & 5 deletions script.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,59 @@ type Script struct {
author string
source string
filename string
// piped if set signifies that the source is from stdin
piped bool
}

// NewScript copies the shell script specified in location (which may be local
// or remote) to a temporary file and loads it into a Script.
func NewScript(location string) (*Script, error) {
script := &Script{source: location}

func NewScript(location string) (script *Script, err error) {
body, err := getFile(location)
if err != nil {
return nil, err
}
defer body.Close()

script, err = fnewScript(body)
if err != nil {
return
}

script.source = location
return
}

func FNewScript(r io.Reader) (script *Script, err error) {
script, err = fnewScript(r)
if err != nil {
return
}
script.piped = true
return
}

func fnewScript(r io.Reader) (*Script, error) {
// TODO: Detect if a reader is pipe-like
// ie like a named piped that could infinitely
// hang if content isn't read from it.
if r == nil {
return nil, fmt.Errorf("script body is nil")
}

file, err := ioutil.TempFile("", "pipethis-")
if err != nil {
return nil, err
}
defer file.Close()

io.Copy(file, body)
script.filename = file.Name()
n, err := io.Copy(file, r)
if n < 1 && err != nil {
return nil, err
}

script := &Script{
filename: file.Name(),
}

return script, nil
}
Expand Down Expand Up @@ -120,3 +152,8 @@ func (s Script) Inspect(inspect bool, editor string) bool {

return strings.ToLower(runScript) == "y"
}

// Piped tells if a script was passed in from stdin
func (s Script) Piped() bool {
return s.piped
}
20 changes: 20 additions & 0 deletions script_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ the source. If not, see http://www.gnu.org/licenses/gpl-2.0.html.
package main

import (
"io"
"io/ioutil"
"os"
"testing"
Expand Down Expand Up @@ -54,6 +55,25 @@ func TestAuthorParsesFileForPattern(t *testing.T) {
os.Remove(filename)

}

func TestPipedScripts(t *testing.T) {
pr, pw := io.Pipe()
go func() {
defer pw.Close()
io.WriteString(pw, "echo $HOME")
}()

script, err := FNewScript(pr)
assert.NoError(t, err)

if script == nil {
assert.Fail(t, "script was parsed successfully but is nil")
}
if !script.Piped() {
assert.Fail(t, "script was piped so .Piped() should return true")
}
}

func providerTestAuthorInvalid() [][]string {
return [][]string{
[]string{"", ``},
Expand Down

0 comments on commit 137a1d4

Please sign in to comment.