Skip to content

Commit 7763728

Browse files
committed
#55 read input from stdin
scrap reading code from stdin
1 parent a40ac13 commit 7763728

3 files changed

Lines changed: 74 additions & 103 deletions

File tree

README.md

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,20 @@ By default, ```dexec``` assumes the sources are in the directory from which it i
8585
$ dexec -C /path/to/sources foo.cpp bar.cpp
8686
```
8787

88+
### Read from STDIN
89+
90+
```dexec``` will forward your terminal's STDIN to the executing code. You can redirect from a file or use pipe:
91+
92+
```sh
93+
$ dexec foo.cpp <input.txt
94+
```
95+
96+
```sh
97+
$ curl http://input | foo.cpp
98+
```
99+
100+
If using keyboard entry, ctrl-d (EOF) will terminate reading from STDIN.
101+
88102
### Include files and folders
89103

90104
Individual files can be mounted without being passed to the compiler, for example header files in C & C++, or input files for program execution. These can be included in the following way.
@@ -131,35 +145,6 @@ $ dexec foo.c -e cpp
131145

132146
This will cause ```dexec``` to attempt to lookup the image for the supplied extension in its map.
133147

134-
### Reading from STDIN
135-
136-
If no source files are specified, ```dexec``` will read from STDIN. Both manual entry and piping are supported. In both cases ```dexec``` writes the contents of STDIN to a temporary file, executes it and then removes it.
137-
138-
```dexec``` requires either the [extension](#override-image-by-file-extension) or [image](#override-image-by-nametag) to be supplied.
139-
140-
#### Manual entry
141-
142-
Text may be entered until EOF is received (Ctrl-D).
143-
144-
```sh
145-
$ dexec --extension js
146-
Enter your code. Ctrl-D to exit
147-
console.log('hello world')
148-
<Ctrl-D>
149-
150-
hello world
151-
```
152-
153-
#### Pipe
154-
155-
```sh
156-
$ echo "console.log('hello world')" | dexec --extension js
157-
```
158-
159-
```sh
160-
$ curl "http://some-url.com/foo.cpp" | dexec --image dexec/lang-cpp
161-
```
162-
163148
### Force dexec to pull latest version of image
164149

165150
Primarily for debugging purposes, the --update command triggers a ```docker pull``` of the target image before executing the code.

cli/cli.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,16 +162,13 @@ func DisplayHelp(filename string) {
162162
fmt.Println("Usage:")
163163
fmt.Printf("\t%s [options] <source files...>\n", filename)
164164
fmt.Println()
165-
fmt.Println("\tIf no source files are specified, input will be read from stdin. This requires")
166-
fmt.Println("\tthe image or extension to be set using --extension or --image.")
167-
fmt.Println()
168165
fmt.Println("Options:")
169166
fmt.Printf("\t%-36s%s\n", "-C <dir>", "Specify source directory")
170167
fmt.Printf("\t%-36s%s\n", "--arg, -a <argument>", "Pass <argument> to the executing code")
171168
fmt.Printf("\t%-36s%s\n", "--build-arg, -b <build argument>", "Pass <build argument> to compiler")
172169
fmt.Printf("\t%-36s%s\n", "--include, -i <file|path>", "Mount local <file|path> in dexec container")
173-
fmt.Printf("\t%-36s%s\n", "--extension, -e <extension>", "Override the extension used to select image")
174-
fmt.Printf("\t%-36s%s\n", "--image, -m <docker image>", "Override the image used with <docker image>")
170+
fmt.Printf("\t%-36s%s\n", "--extension, -e <extension>", "Override the image used by <extension>")
171+
fmt.Printf("\t%-36s%s\n", "--image, -m <name>", "Override the image used by <name>")
175172
fmt.Printf("\t%-36s%s\n", "--update, -u", "Force update of image")
176173
fmt.Printf("\t%-36s%s\n", "--clean", "Remove all local dexec images")
177174
fmt.Printf("\t%-36s%s\n", "--help, -h", "Show help")

main.go

Lines changed: 58 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,8 @@ import (
55
"log"
66
"os"
77
"regexp"
8-
"strings"
98
"time"
109

11-
uuid "github.com/satori/go.uuid"
12-
1310
"github.com/docker-exec/dexec/cli"
1411
"github.com/docker-exec/dexec/dexec"
1512
"github.com/docker-exec/dexec/util"
@@ -19,14 +16,11 @@ import (
1916
// RunDexecContainer runs an anonymous Docker container with a Docker Exec
2017
// image, mounting the specified sources and includes and passing the
2118
// list of sources and arguments to the entrypoint.
22-
func RunDexecContainer(cliParser cli.CLI) {
19+
func RunDexecContainer(cliParser cli.CLI) int {
2320
options := cliParser.Options
2421

25-
hasSources := len(options[cli.Source]) > 0
2622
shouldClean := len(options[cli.CleanFlag]) > 0
2723
updateImage := len(options[cli.UpdateFlag]) > 0
28-
useExtension := len(options[cli.Extension]) == 1
29-
useImage := len(options[cli.Image]) == 1
3024

3125
client, err := docker.NewClientFromEnv()
3226

@@ -51,97 +45,92 @@ func RunDexecContainer(cliParser cli.CLI) {
5145
}
5246
}
5347

54-
if hasSources || useExtension || useImage {
55-
dexecImage, err := dexec.ImageFromOptions(options)
56-
if err != nil {
57-
log.Fatal(err)
58-
}
59-
60-
dockerImage := fmt.Sprintf("%s:%s", dexecImage.Image, dexecImage.Version)
61-
62-
if !hasSources {
63-
lines := util.ReadStdin("Enter your code. Ctrl-D to exit", "<Ctrl-D>")
64-
tmpFile := fmt.Sprintf("%s.%s", uuid.NewV4(), dexecImage.Extension)
65-
66-
util.WriteFile(tmpFile, []byte(strings.Join(lines, "\n")))
67-
defer func() {
68-
util.DeleteFile(tmpFile)
69-
}()
70-
71-
options[cli.Source] = []string{tmpFile}
72-
}
48+
dexecImage, err := dexec.ImageFromOptions(options)
49+
if err != nil {
50+
log.Fatal(err)
51+
}
7352

74-
if err := dexec.FetchImage(
75-
dexecImage.Image,
76-
dexecImage.Version,
77-
updateImage,
78-
client); err != nil {
79-
log.Fatal(err)
80-
}
53+
dockerImage := fmt.Sprintf("%s:%s", dexecImage.Image, dexecImage.Version)
8154

82-
var sourceBasenames []string
83-
for _, source := range options[cli.Source] {
84-
basename, _ := dexec.ExtractBasenameAndPermission(source)
85-
sourceBasenames = append(sourceBasenames, []string{basename}...)
86-
}
55+
if err := dexec.FetchImage(
56+
dexecImage.Image,
57+
dexecImage.Version,
58+
updateImage,
59+
client); err != nil {
60+
log.Fatal(err)
61+
}
8762

88-
entrypointArgs := util.JoinStringSlices(
89-
sourceBasenames,
90-
util.AddPrefix(options[cli.BuildArg], "-b"),
91-
util.AddPrefix(options[cli.Arg], "-a"),
92-
)
93-
94-
container, err := client.CreateContainer(docker.CreateContainerOptions{
95-
Config: &docker.Config{
96-
Image: dockerImage,
97-
Cmd: entrypointArgs,
98-
},
99-
})
63+
var sourceBasenames []string
64+
for _, source := range options[cli.Source] {
65+
basename, _ := dexec.ExtractBasenameAndPermission(source)
66+
sourceBasenames = append(sourceBasenames, []string{basename}...)
67+
}
10068

101-
if err != nil {
102-
log.Fatal(err)
103-
}
69+
entrypointArgs := util.JoinStringSlices(
70+
sourceBasenames,
71+
util.AddPrefix(options[cli.BuildArg], "-b"),
72+
util.AddPrefix(options[cli.Arg], "-a"),
73+
)
74+
75+
container, err := client.CreateContainer(docker.CreateContainerOptions{
76+
Config: &docker.Config{
77+
Image: dockerImage,
78+
Cmd: entrypointArgs,
79+
StdinOnce: true,
80+
OpenStdin: true,
81+
},
82+
})
10483

105-
defer func() {
106-
if err = client.RemoveContainer(docker.RemoveContainerOptions{
107-
ID: container.ID,
108-
}); err != nil {
109-
log.Fatal(err)
110-
}
111-
}()
84+
if err != nil {
85+
log.Fatal(err)
86+
}
11287

113-
if err = client.StartContainer(container.ID, &docker.HostConfig{
114-
Binds: dexec.BuildVolumeArgs(
115-
util.RetrievePath(options[cli.TargetDir]),
116-
append(options[cli.Source], options[cli.Include]...)),
88+
defer func() {
89+
if err = client.RemoveContainer(docker.RemoveContainerOptions{
90+
ID: container.ID,
11791
}); err != nil {
11892
log.Fatal(err)
11993
}
94+
}()
12095

96+
if err = client.StartContainer(container.ID, &docker.HostConfig{
97+
Binds: dexec.BuildVolumeArgs(
98+
util.RetrievePath(options[cli.TargetDir]),
99+
append(options[cli.Source], options[cli.Include]...)),
100+
}); err != nil {
101+
log.Fatal(err)
102+
}
103+
104+
go func() {
121105
if err = client.AttachToContainer(docker.AttachToContainerOptions{
122106
Container: container.ID,
123107
OutputStream: os.Stdout,
124108
ErrorStream: os.Stderr,
109+
InputStream: os.Stdin,
125110
Stream: true,
126111
Stdout: true,
127112
Stderr: true,
128-
Logs: true,
113+
Stdin: true,
129114
}); err != nil {
130115
log.Fatal(err)
131116
}
117+
}()
118+
119+
code, err := client.WaitContainer(container.ID)
120+
if err != nil {
121+
log.Fatal(err)
132122
}
123+
return code
133124
}
134125

135126
func validate(cliParser cli.CLI) bool {
136127
options := cliParser.Options
137128

138129
hasVersionFlag := len(options[cli.VersionFlag]) == 1
139-
hasExtension := len(options[cli.Extension]) == 1
140-
hasImage := len(options[cli.Image]) == 1
141130
hasSources := len(options[cli.Source]) > 0
142131
shouldClean := len(options[cli.CleanFlag]) > 0
143132

144-
if hasSources || hasImage || hasExtension || shouldClean {
133+
if hasSources || shouldClean {
145134
return true
146135
}
147136

@@ -180,7 +169,7 @@ func main() {
180169
if err := validateDocker(); err != nil {
181170
log.Fatal(err)
182171
} else {
183-
RunDexecContainer(cliParser)
172+
os.Exit(RunDexecContainer(cliParser))
184173
}
185174
}
186175
}

0 commit comments

Comments
 (0)