lino 3.2.0.pre.10 → 4.0.0.pre.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +17 -15
- data/README.md +444 -106
- data/lib/lino/builders/mixins/appliables.rb +1 -1
- data/lib/lino/builders/mixins/arguments.rb +1 -1
- data/lib/lino/builders/mixins/options.rb +2 -2
- data/lib/lino/builders/mixins/validation.rb +1 -5
- data/lib/lino/errors/execution_error.rb +4 -0
- data/lib/lino/executors/open4.rb +1 -1
- data/lib/lino/version.rb +1 -1
- data/lib/lino.rb +4 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62e29445dc3209ccbd5fd21289316ee48e4f085cea6ac435377689f424bee7c1
|
4
|
+
data.tar.gz: 7eda92dd75dd89c9359e5b0d080e26a42f7ab86bc57c4a4364d2ff1dce1fb548
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d1e5afde0da35b878998e6d5861270376e4d524ea4f365cfa9d88075085d651b40a9b9326779488be888b9fcdbc52bd86b0111d1b0e17330d3c383fc5e47424
|
7
|
+
data.tar.gz: cb184d00c5aa2dd87c64760181457398a25eff11f3c3d8aa81bdbbab81239dd6a58f3fe1bef7e929d3528725cf505c335b8c280b7e5d508de24173afa37c0e36
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
lino (
|
4
|
+
lino (4.0.0.pre.2)
|
5
5
|
childprocess (~> 5.0.0)
|
6
6
|
hamster (~> 3.0)
|
7
7
|
open4 (~> 1.3)
|
@@ -32,9 +32,10 @@ GEM
|
|
32
32
|
diff-lcs (1.5.1)
|
33
33
|
docile (1.4.0)
|
34
34
|
drb (2.2.1)
|
35
|
-
excon (0.
|
36
|
-
faraday (2.
|
35
|
+
excon (0.111.0)
|
36
|
+
faraday (2.10.0)
|
37
37
|
faraday-net_http (>= 2.0, < 3.2)
|
38
|
+
logger
|
38
39
|
faraday-net_http (3.1.0)
|
39
40
|
net-http
|
40
41
|
ffi (1.17.0)
|
@@ -67,6 +68,7 @@ GEM
|
|
67
68
|
listen (3.9.0)
|
68
69
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
69
70
|
rb-inotify (~> 0.9, >= 0.9.10)
|
71
|
+
logger (1.6.0)
|
70
72
|
lumberjack (1.2.10)
|
71
73
|
method_source (1.1.0)
|
72
74
|
minitest (5.24.1)
|
@@ -83,7 +85,7 @@ GEM
|
|
83
85
|
sawyer (~> 0.9)
|
84
86
|
open4 (1.3.4)
|
85
87
|
parallel (1.25.1)
|
86
|
-
parser (3.3.
|
88
|
+
parser (3.3.4.0)
|
87
89
|
ast (~> 2.4.1)
|
88
90
|
racc
|
89
91
|
pry (0.14.2)
|
@@ -105,11 +107,11 @@ GEM
|
|
105
107
|
colored2 (~> 3.1)
|
106
108
|
git (~> 1.13, >= 1.13.2)
|
107
109
|
rake_factory (= 0.32.0.pre.2)
|
108
|
-
rake_git_crypt (0.1.0.pre.
|
110
|
+
rake_git_crypt (0.1.0.pre.30)
|
109
111
|
colored2 (~> 3.1)
|
110
112
|
rake_factory (= 0.32.0.pre.2)
|
111
|
-
ruby_git_crypt (= 0.1.0.pre.
|
112
|
-
ruby_gpg2 (
|
113
|
+
ruby_git_crypt (= 0.1.0.pre.6)
|
114
|
+
ruby_gpg2 (= 0.11.0.pre.5)
|
113
115
|
rake_github (0.13.0)
|
114
116
|
colored2 (~> 3.1)
|
115
117
|
octokit (>= 4.16, < 9.0)
|
@@ -142,13 +144,13 @@ GEM
|
|
142
144
|
diff-lcs (>= 1.2.0, < 2.0)
|
143
145
|
rspec-support (~> 3.13.0)
|
144
146
|
rspec-support (3.13.1)
|
145
|
-
rubocop (1.
|
147
|
+
rubocop (1.65.0)
|
146
148
|
json (~> 2.3)
|
147
149
|
language_server-protocol (>= 3.17.0)
|
148
150
|
parallel (~> 1.10)
|
149
151
|
parser (>= 3.3.0.2)
|
150
152
|
rainbow (>= 2.2.2, < 4.0)
|
151
|
-
regexp_parser (>=
|
153
|
+
regexp_parser (>= 2.4, < 3.0)
|
152
154
|
rexml (>= 3.2.5, < 4.0)
|
153
155
|
rubocop-ast (>= 1.31.1, < 2.0)
|
154
156
|
ruby-progressbar (~> 1.7)
|
@@ -157,14 +159,14 @@ GEM
|
|
157
159
|
parser (>= 3.3.1.0)
|
158
160
|
rubocop-rake (0.6.0)
|
159
161
|
rubocop (~> 1.0)
|
160
|
-
rubocop-rspec (3.0.
|
162
|
+
rubocop-rspec (3.0.3)
|
161
163
|
rubocop (~> 1.61)
|
162
164
|
ruby-progressbar (1.13.0)
|
163
|
-
ruby_git_crypt (0.1.0.pre.
|
165
|
+
ruby_git_crypt (0.1.0.pre.6)
|
164
166
|
immutable-struct (~> 2.4)
|
165
|
-
lino (
|
166
|
-
ruby_gpg2 (0.
|
167
|
-
lino (
|
167
|
+
lino (= 3.2.0.pre.10)
|
168
|
+
ruby_gpg2 (0.11.0.pre.5)
|
169
|
+
lino (= 3.2.0.pre.10)
|
168
170
|
sawyer (0.9.2)
|
169
171
|
addressable (>= 2.3.5)
|
170
172
|
faraday (>= 0.17.3, < 3)
|
@@ -206,4 +208,4 @@ DEPENDENCIES
|
|
206
208
|
simplecov
|
207
209
|
|
208
210
|
BUNDLED WITH
|
209
|
-
2.
|
211
|
+
2.5.15
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Lino
|
2
2
|
|
3
|
-
Command line execution utilities.
|
3
|
+
Command line building and execution utilities.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -25,12 +25,15 @@ Lino allows commands to be built and executed:
|
|
25
25
|
```ruby
|
26
26
|
require 'lino'
|
27
27
|
|
28
|
-
command_line = Lino
|
28
|
+
command_line = Lino.builder_for_command('ruby')
|
29
29
|
.with_flag('-v')
|
30
30
|
.with_option('-e', 'puts "Hello"')
|
31
31
|
.build
|
32
32
|
|
33
|
-
puts command_line.
|
33
|
+
puts command_line.array
|
34
|
+
# => ['ruby', '-v', '-e', 'puts "Hello"']
|
35
|
+
|
36
|
+
puts command_line.string
|
34
37
|
# => ruby -v -e puts "Hello"
|
35
38
|
|
36
39
|
command_line.execute
|
@@ -38,34 +41,66 @@ command_line.execute
|
|
38
41
|
# Hello
|
39
42
|
```
|
40
43
|
|
41
|
-
###
|
44
|
+
### Building command lines
|
45
|
+
|
46
|
+
`Lino` supports building command lines via instances of the
|
47
|
+
`Lino::Builder::CommandLine` class. `Lino::Builder::CommandLine` allows a
|
48
|
+
number of different styles of commands to be built. The object built by
|
49
|
+
`Lino::Builder::CommandLine` is an instance of `Lino::Model::CommandLine`, which
|
50
|
+
represents the components and context of a command line and allows the
|
51
|
+
command line to be executed.
|
52
|
+
|
53
|
+
Aside from the object model, `Lino::Model::CommandLine` instances have two
|
54
|
+
representations, accessible via the `#string` and `#array` instance methods.
|
55
|
+
|
56
|
+
The string representation is useful when the command line is intended to be
|
57
|
+
executed by a shell, where quoting is important. However, it can present a
|
58
|
+
security risk if the components (option values, arguments, environment
|
59
|
+
variables) of the command line are user provided. For this reason, the array
|
60
|
+
representation is preferable and is the representation used by default whenever
|
61
|
+
`Lino` executes commands.
|
42
62
|
|
43
|
-
|
44
|
-
|
63
|
+
#### Getting a command line builder
|
64
|
+
|
65
|
+
A `Lino::Builder::CommandLine` can be instantiated using:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
Lino.builder_for_command('ls')
|
69
|
+
```
|
70
|
+
|
71
|
+
or using the now deprecated:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
Lino::CommandLineBuilder.for_command('ls')
|
75
|
+
```
|
45
76
|
|
46
77
|
#### Flags
|
47
78
|
|
48
79
|
Flags can be added with `#with_flag`:
|
49
80
|
|
50
81
|
```ruby
|
51
|
-
Lino
|
82
|
+
command_line = Lino.builder_for_command('ls')
|
52
83
|
.with_flag('-l')
|
53
84
|
.with_flag('-a')
|
54
85
|
.build
|
55
|
-
.to_s
|
56
86
|
|
57
|
-
|
87
|
+
command_line.array
|
88
|
+
# => ["ls", "-l", "-a"]
|
89
|
+
command_line.string
|
90
|
+
# => "ls -l -a"
|
58
91
|
```
|
59
92
|
|
60
93
|
or `#with_flags`:
|
61
94
|
|
62
95
|
```ruby
|
63
|
-
Lino
|
96
|
+
command_line = Lino.builder_for_command('ls')
|
64
97
|
.with_flags(%w[-l -a])
|
65
98
|
.build
|
66
|
-
.to_s
|
67
99
|
|
68
|
-
|
100
|
+
command_line.array
|
101
|
+
# => ["ls", "-l", "-a"]
|
102
|
+
command_line.string
|
103
|
+
# => "ls -l -a"
|
69
104
|
```
|
70
105
|
|
71
106
|
#### Options
|
@@ -73,33 +108,38 @@ Lino::CommandLineBuilder.for_command('ls')
|
|
73
108
|
Options with values can be added with `#with_option`:
|
74
109
|
|
75
110
|
```ruby
|
76
|
-
Lino
|
111
|
+
command_line = Lino.builder_for_command('gpg')
|
77
112
|
.with_option('--recipient', 'tobyclemson@gmail.com')
|
78
113
|
.with_option('--sign', './doc.txt')
|
79
114
|
.build
|
80
|
-
.to_s
|
81
115
|
|
82
|
-
|
116
|
+
command_line.array
|
117
|
+
# => ["gpg", "--recipient", "tobyclemson@gmail.com", "--sign", "./doc.txt"]
|
118
|
+
command_line.string
|
119
|
+
# => "gpg --recipient tobyclemson@gmail.com --sign ./doc.txt"
|
120
|
+
|
83
121
|
```
|
84
122
|
|
85
123
|
or `#with_options`, either as a hash:
|
86
124
|
|
87
125
|
```ruby
|
88
|
-
Lino
|
126
|
+
command_line = Lino.builder_for_command('gpg')
|
89
127
|
.with_options({
|
90
128
|
'--recipient' => 'tobyclemson@gmail.com',
|
91
129
|
'--sign' => './doc.txt'
|
92
130
|
})
|
93
131
|
.build
|
94
|
-
.to_s
|
95
132
|
|
96
|
-
|
133
|
+
command_line.array
|
134
|
+
# => ["gpg", "--recipient", "tobyclemson@gmail.com", "--sign", "./doc.txt"]
|
135
|
+
command_line.string
|
136
|
+
# => "gpg --recipient tobyclemson@gmail.com --sign ./doc.txt"
|
97
137
|
```
|
98
138
|
|
99
139
|
or as an array:
|
100
140
|
|
101
141
|
```ruby
|
102
|
-
Lino
|
142
|
+
command_line = Lino.builder_for_command('gpg')
|
103
143
|
.with_options(
|
104
144
|
[
|
105
145
|
{ option: '--recipient', value: 'tobyclemson@gmail.com' },
|
@@ -107,20 +147,24 @@ Lino::CommandLineBuilder.for_command('gpg')
|
|
107
147
|
]
|
108
148
|
)
|
109
149
|
.build
|
110
|
-
.to_s
|
111
150
|
|
112
|
-
|
151
|
+
command_line.array
|
152
|
+
# => ["gpg", "--recipient", "tobyclemson@gmail.com", "--sign", "./doc.txt"]
|
153
|
+
command_line.string
|
154
|
+
# => "gpg --recipient tobyclemson@gmail.com --sign ./doc.txt"
|
113
155
|
```
|
114
156
|
|
115
157
|
Some commands allow options to be repeated:
|
116
158
|
|
117
159
|
```ruby
|
118
|
-
Lino
|
160
|
+
command_line = Lino.builder_for_command('example.sh')
|
119
161
|
.with_repeated_option('--opt', ['file1.txt', nil, '', 'file2.txt'])
|
120
162
|
.build
|
121
|
-
.to_s
|
122
163
|
|
123
|
-
|
164
|
+
command_line.array
|
165
|
+
# => ["example.sh", "--opt", "file1.txt", "--opt", "file2.txt"]
|
166
|
+
command_line.string
|
167
|
+
# => "example.sh --opt file1.txt --opt file2.txt"
|
124
168
|
```
|
125
169
|
|
126
170
|
> Note: `lino` ignores `nil` or empty option values in the resulting command
|
@@ -131,24 +175,28 @@ Lino::CommandLineBuilder.for_command('example.sh')
|
|
131
175
|
Arguments can be added using `#with_argument`:
|
132
176
|
|
133
177
|
```ruby
|
134
|
-
Lino
|
178
|
+
command_line = Lino.builder_for_command('diff')
|
135
179
|
.with_argument('./file1.txt')
|
136
180
|
.with_argument('./file2.txt')
|
137
181
|
.build
|
138
|
-
.to_s
|
139
182
|
|
140
|
-
|
183
|
+
command_line.array
|
184
|
+
# => ["diff", "./file1.txt", "./file2.txt"]
|
185
|
+
command_line.string
|
186
|
+
# => "diff ./file1.txt ./file2.txt"
|
141
187
|
```
|
142
188
|
|
143
189
|
or `#with_arguments`, as an array:
|
144
190
|
|
145
191
|
```ruby
|
146
|
-
Lino
|
192
|
+
command_line = Lino.builder_for_command('diff')
|
147
193
|
.with_arguments(['./file1.txt', nil, '', './file2.txt'])
|
148
194
|
.build
|
149
|
-
.to_s
|
150
195
|
|
151
|
-
|
196
|
+
command_line.array
|
197
|
+
# => ["diff", "./file1.txt", "./file2.txt"]
|
198
|
+
command_line.string
|
199
|
+
# => "diff ./file1.txt ./file2.txt"
|
152
200
|
```
|
153
201
|
|
154
202
|
> Note: `lino` ignores `nil` or empty argument values in the resulting command
|
@@ -156,30 +204,35 @@ Lino::CommandLineBuilder.for_command('diff')
|
|
156
204
|
|
157
205
|
#### Option Separators
|
158
206
|
|
159
|
-
By default,
|
160
|
-
can be overridden globally using
|
207
|
+
By default, when rendering command lines as a string, `lino` separates option
|
208
|
+
values from the option by a space. This can be overridden globally using
|
209
|
+
`#with_option_separator`:
|
161
210
|
|
162
211
|
```ruby
|
163
|
-
Lino
|
212
|
+
command_line = Lino.builder_for_command('java')
|
164
213
|
.with_option_separator(':')
|
165
214
|
.with_option('-splash', './images/splash.jpg')
|
166
215
|
.with_argument('./application.jar')
|
167
216
|
.build
|
168
|
-
.to_s
|
169
217
|
|
170
|
-
|
218
|
+
command_line.array
|
219
|
+
# => ["java", "-splash:./images/splash.jpg", "./application.jar"]
|
220
|
+
command_line.string
|
221
|
+
# => "java -splash:./images/splash.jpg ./application.jar"
|
171
222
|
```
|
172
223
|
|
173
|
-
The option separator can be overridden on an option by option basis:
|
224
|
+
The option separator can also be overridden on an option by option basis:
|
174
225
|
|
175
226
|
```ruby
|
176
|
-
Lino
|
227
|
+
command_line = Lino.builder_for_command('java')
|
177
228
|
.with_option('-splash', './images/splash.jpg', separator: ':')
|
178
229
|
.with_argument('./application.jar')
|
179
230
|
.build
|
180
|
-
.to_s
|
181
231
|
|
182
|
-
|
232
|
+
command_line.array
|
233
|
+
# => ["java", "-splash:./images/splash.jpg", "./application.jar"]
|
234
|
+
command_line.string
|
235
|
+
# => "java -splash:./images/splash.jpg ./application.jar"
|
183
236
|
```
|
184
237
|
|
185
238
|
> Note: `#with_options` supports separator overriding when the options are
|
@@ -193,29 +246,34 @@ Lino::CommandLineBuilder.for_command('java')
|
|
193
246
|
|
194
247
|
#### Option Quoting
|
195
248
|
|
196
|
-
By default, `lino` does not quote option
|
197
|
-
globally using `#with_option_quoting`:
|
249
|
+
By default, when rendering command line strings, `lino` does not quote option
|
250
|
+
values. This can be overridden globally using `#with_option_quoting`:
|
198
251
|
|
199
252
|
```ruby
|
200
|
-
Lino
|
253
|
+
command_line = Lino.builder_for_command('gpg')
|
201
254
|
.with_option_quoting('"')
|
202
255
|
.with_option('--sign', 'some file.txt')
|
203
256
|
.build
|
204
|
-
.to_s
|
205
257
|
|
206
|
-
|
258
|
+
command_line.string
|
259
|
+
# => "gpg --sign \"some file.txt\""
|
260
|
+
command_line.array
|
261
|
+
# => ["gpg", "--sign", "some file.txt"]
|
207
262
|
```
|
208
263
|
|
209
|
-
The option quoting can be overridden on an option by option basis:
|
264
|
+
The option quoting can also be overridden on an option by option basis:
|
210
265
|
|
211
266
|
```ruby
|
212
|
-
Lino
|
267
|
+
command_line = Lino.builder_for_command('java')
|
213
268
|
.with_option('-splash', './images/splash.jpg', quoting: '"')
|
214
269
|
.with_argument('./application.jar')
|
215
270
|
.build
|
216
|
-
.
|
271
|
+
.string
|
217
272
|
|
218
|
-
|
273
|
+
command_line.string
|
274
|
+
# => "java -splash \"./images/splash.jpg\" ./application.jar"
|
275
|
+
command_line.array
|
276
|
+
# => ["java", "-splash", "./images/splash.jpg", "./application.jar"]
|
219
277
|
```
|
220
278
|
|
221
279
|
> Note: `#with_options` supports quoting overriding when the options are
|
@@ -227,46 +285,55 @@ Lino::CommandLineBuilder.for_command('java')
|
|
227
285
|
> Note: option specific quoting take precedence over the global option
|
228
286
|
> quoting
|
229
287
|
|
288
|
+
> Note: option quoting has no impact on the array representation of a command
|
289
|
+
> line
|
290
|
+
|
230
291
|
#### Subcommands
|
231
292
|
|
232
293
|
Subcommands can be added using `#with_subcommand`:
|
233
294
|
|
234
295
|
```ruby
|
235
|
-
Lino
|
296
|
+
command_line = Lino.builder_for_command('git')
|
236
297
|
.with_flag('--no-pager')
|
237
298
|
.with_subcommand('log')
|
238
299
|
.build
|
239
|
-
.to_s
|
240
300
|
|
241
|
-
|
301
|
+
command_line.array
|
302
|
+
# => ["git", "--no-pager", "log"]
|
303
|
+
command_line.string
|
304
|
+
# => "git --no-pager log"
|
242
305
|
```
|
243
306
|
|
244
307
|
Multi-level subcommands can be added using multiple `#with_subcommand`
|
245
308
|
invocations:
|
246
309
|
|
247
310
|
```ruby
|
248
|
-
Lino
|
311
|
+
command_line = Lino.builder_for_command('gcloud')
|
249
312
|
.with_subcommand('sql')
|
250
313
|
.with_subcommand('instances')
|
251
314
|
.with_subcommand('set-root-password')
|
252
315
|
.with_subcommand('some-database')
|
253
316
|
.build
|
254
|
-
.to_s
|
255
317
|
|
256
|
-
|
318
|
+
command_line.array
|
319
|
+
# => ["gcloud", "sql", "instances", "set-root-password", "some-database"]
|
320
|
+
command_line.string
|
321
|
+
# => "gcloud sql instances set-root-password some-database"
|
257
322
|
```
|
258
323
|
|
259
324
|
or using `#with_subcommands`:
|
260
325
|
|
261
326
|
```ruby
|
262
|
-
Lino
|
327
|
+
command_line = Lino.builder_for_command('gcloud')
|
263
328
|
.with_subcommands(
|
264
329
|
%w[sql instances set-root-password some-database]
|
265
330
|
)
|
266
331
|
.build
|
267
|
-
|
268
|
-
|
269
|
-
# => gcloud sql instances set-root-password some-database
|
332
|
+
|
333
|
+
command_line.array
|
334
|
+
# => ["gcloud", "sql", "instances", "set-root-password", "some-database"]
|
335
|
+
command_line.string
|
336
|
+
# => "gcloud sql instances set-root-password some-database"
|
270
337
|
```
|
271
338
|
|
272
339
|
Subcommands also support options via `#with_flag`, `#with_flags`,
|
@@ -274,15 +341,17 @@ Subcommands also support options via `#with_flag`, `#with_flags`,
|
|
274
341
|
via a block, for example:
|
275
342
|
|
276
343
|
```ruby
|
277
|
-
Lino
|
344
|
+
command_line = Lino.builder_for_command('git')
|
278
345
|
.with_flag('--no-pager')
|
279
346
|
.with_subcommand('log') do |sub|
|
280
347
|
sub.with_option('--since', '2016-01-01')
|
281
348
|
end
|
282
349
|
.build
|
283
|
-
.to_s
|
284
350
|
|
285
|
-
|
351
|
+
command_line.array
|
352
|
+
# => ["git", "--no-pager", "log", "--since", "2016-01-01"]
|
353
|
+
command_line.string
|
354
|
+
# => "git --no-pager log --since 2016-01-01"
|
286
355
|
```
|
287
356
|
|
288
357
|
> Note: `#with_subcommands` also supports a block, which applies in the context
|
@@ -290,38 +359,46 @@ Lino::CommandLineBuilder.for_command('git')
|
|
290
359
|
|
291
360
|
#### Environment Variables
|
292
361
|
|
293
|
-
|
362
|
+
Environment variables can be added to command lines using
|
294
363
|
`#with_environment_variable`:
|
295
364
|
|
296
365
|
```ruby
|
297
|
-
Lino
|
366
|
+
command_line = Lino.builder_for_command('node')
|
298
367
|
.with_environment_variable('PORT', '3030')
|
299
368
|
.with_environment_variable('LOG_LEVEL', 'debug')
|
300
369
|
.with_argument('./server.js')
|
301
370
|
.build
|
302
|
-
|
303
|
-
|
304
|
-
# => PORT
|
371
|
+
|
372
|
+
command_line.string
|
373
|
+
# => "PORT=\"3030\" LOG_LEVEL=\"debug\" node ./server.js"
|
374
|
+
command_line.array
|
375
|
+
# => ["node", "./server.js"]
|
376
|
+
command_line.env
|
377
|
+
# => {"PORT"=>"3030", "LOG_LEVEL"=>"debug"}
|
305
378
|
```
|
306
379
|
|
307
380
|
or `#with_environment_variables`, either as a hash:
|
308
381
|
|
309
382
|
```ruby
|
310
|
-
Lino
|
383
|
+
command_line = Lino.builder_for_command('node')
|
311
384
|
.with_environment_variables({
|
312
385
|
'PORT' => '3030',
|
313
386
|
'LOG_LEVEL' => 'debug'
|
314
387
|
})
|
315
388
|
.build
|
316
|
-
|
317
|
-
|
318
|
-
# => PORT
|
389
|
+
|
390
|
+
command_line.string
|
391
|
+
# => "PORT=\"3030\" LOG_LEVEL=\"debug\" node ./server.js"
|
392
|
+
command_line.array
|
393
|
+
# => ["node", "./server.js"]
|
394
|
+
command_line.env
|
395
|
+
# => {"PORT"=>"3030", "LOG_LEVEL"=>"debug"}
|
319
396
|
```
|
320
397
|
|
321
398
|
or as an array:
|
322
399
|
|
323
400
|
```ruby
|
324
|
-
Lino
|
401
|
+
command_line = Lino.builder_for_command('node')
|
325
402
|
.with_environment_variables(
|
326
403
|
[
|
327
404
|
{ name: 'PORT', value: '3030' },
|
@@ -329,9 +406,13 @@ Lino::CommandLineBuilder.for_command('node')
|
|
329
406
|
]
|
330
407
|
)
|
331
408
|
.build
|
332
|
-
|
333
|
-
|
334
|
-
# => PORT
|
409
|
+
|
410
|
+
command_line.string
|
411
|
+
# => "PORT=\"3030\" LOG_LEVEL=\"debug\" node ./server.js"
|
412
|
+
command_line.array
|
413
|
+
# => ["node", "./server.js"]
|
414
|
+
command_line.env
|
415
|
+
# => {"PORT"=>"3030", "LOG_LEVEL"=>"debug"}
|
335
416
|
```
|
336
417
|
|
337
418
|
#### Option Placement
|
@@ -342,13 +423,21 @@ subcommands and arguments.
|
|
342
423
|
This is equivalent to calling `#with_options_after_command`:
|
343
424
|
|
344
425
|
```ruby
|
345
|
-
Lino
|
426
|
+
command_line = Lino.builder_for_command('gcloud')
|
346
427
|
.with_options_after_command
|
347
428
|
.with_option('--password', 'super-secure')
|
348
429
|
.with_subcommands(%w[sql instances set-root-password])
|
349
430
|
.build
|
350
|
-
.to_s
|
351
431
|
|
432
|
+
command_line.array
|
433
|
+
# =>
|
434
|
+
# ["gcloud",
|
435
|
+
# "--password",
|
436
|
+
# "super-secure",
|
437
|
+
# "sql",
|
438
|
+
# "instances",
|
439
|
+
# "set-root-password"]
|
440
|
+
command_line.string
|
352
441
|
# => gcloud --password super-secure sql instances set-root-password
|
353
442
|
```
|
354
443
|
|
@@ -356,42 +445,61 @@ Alternatively, top-level options can be placed after all subcommands using
|
|
356
445
|
`#with_options_after_subcommands`:
|
357
446
|
|
358
447
|
```ruby
|
359
|
-
Lino
|
448
|
+
command_line = Lino.builder_for_command('gcloud')
|
360
449
|
.with_options_after_subcommands
|
361
450
|
.with_option('--password', 'super-secure')
|
362
451
|
.with_subcommands(%w[sql instances set-root-password])
|
363
452
|
.build
|
364
|
-
.to_s
|
365
453
|
|
454
|
+
command_line.array
|
455
|
+
# =>
|
456
|
+
# ["gcloud",
|
457
|
+
# "sql",
|
458
|
+
# "instances",
|
459
|
+
# "set-root-password",
|
460
|
+
# "--password",
|
461
|
+
# "super-secure"]
|
462
|
+
command_line.string
|
366
463
|
# => gcloud sql instances set-root-password --password super-secure
|
367
464
|
```
|
368
465
|
|
369
466
|
or, after all arguments, using `#with_options_after_arguments`:
|
370
467
|
|
371
468
|
```ruby
|
372
|
-
Lino
|
469
|
+
command_line = Lino.builder_for_command('ls')
|
373
470
|
.with_options_after_arguments
|
374
471
|
.with_flag('-l')
|
375
472
|
.with_argument('/some/directory')
|
376
473
|
.build
|
377
|
-
.to_s
|
378
474
|
|
379
|
-
|
475
|
+
command_line.array
|
476
|
+
# => ["ls", "/some/directory", "-l"]
|
477
|
+
command_line.string
|
478
|
+
# => "ls /some/directory -l"
|
380
479
|
```
|
381
480
|
|
382
481
|
The option placement can be overridden on an option by option basis:
|
383
482
|
|
384
483
|
```ruby
|
385
|
-
Lino
|
484
|
+
command_line = Lino.builder_for_command('gcloud')
|
386
485
|
.with_options_after_subcommands
|
387
486
|
.with_option('--log-level', 'debug', placement: :after_command)
|
388
|
-
.with_option('--password', '
|
487
|
+
.with_option('--password', 'pass1')
|
389
488
|
.with_subcommands(%w[sql instances set-root-password])
|
390
489
|
.build
|
391
|
-
.to_s
|
392
490
|
|
393
|
-
|
394
|
-
#
|
491
|
+
command_line.array
|
492
|
+
# =>
|
493
|
+
# ["gcloud",
|
494
|
+
# "--log-level",
|
495
|
+
# "debug",
|
496
|
+
# "sql",
|
497
|
+
# "instances",
|
498
|
+
# "set-root-password",
|
499
|
+
# "--password",
|
500
|
+
# "pass1"]
|
501
|
+
command_line.string
|
502
|
+
# => "gcloud --log-level debug sql instances set-root-password --password pass1"
|
395
503
|
```
|
396
504
|
|
397
505
|
The `:placement` keyword argument accepts placement values of `:after_command`,
|
@@ -429,20 +537,22 @@ end
|
|
429
537
|
an instance of the appliable can be applied using `#with_appliable`:
|
430
538
|
|
431
539
|
```ruby
|
432
|
-
Lino
|
540
|
+
command_line = Lino.builder_for_command('gpg')
|
433
541
|
.with_appliable(AppliableOption.new('--recipient', 'tobyclemson@gmail.com'))
|
434
542
|
.with_flag('--sign')
|
435
543
|
.with_argument('/some/file.txt')
|
436
544
|
.build
|
437
|
-
.to_s
|
438
545
|
|
439
|
-
|
546
|
+
command_line.array
|
547
|
+
# => ["gpg", "--recipient", "tobyclemson@gmail.com", "--sign", "/some/file.txt"]
|
548
|
+
command_line.string
|
549
|
+
# => "gpg --recipient tobyclemson@gmail.com --sign /some/file.txt"
|
440
550
|
```
|
441
551
|
|
442
552
|
or multiple with `#with_appliables`:
|
443
553
|
|
444
554
|
```ruby
|
445
|
-
Lino
|
555
|
+
command_line = Lino.builder_for_command('gpg')
|
446
556
|
.with_appliables([
|
447
557
|
AppliableOption.new('--recipient', 'user@example.com'),
|
448
558
|
AppliableOption.new('--output', '/signed.txt')
|
@@ -450,57 +560,285 @@ Lino::CommandLineBuilder.for_command('gpg')
|
|
450
560
|
.with_flag('--sign')
|
451
561
|
.with_argument('/file.txt')
|
452
562
|
.build
|
453
|
-
.to_s
|
454
563
|
|
455
|
-
|
564
|
+
command_line.array
|
565
|
+
# =>
|
566
|
+
# ["gpg",
|
567
|
+
# "--recipient",
|
568
|
+
# "tobyclemson@gmail.com",
|
569
|
+
# "--output",
|
570
|
+
# "/signed.txt",
|
571
|
+
# "--sign",
|
572
|
+
# "/some/file.txt"]
|
573
|
+
command_line.string
|
574
|
+
# => "gpg --recipient user@example.com --output /signed.txt --sign /file.txt"
|
456
575
|
```
|
457
576
|
|
458
577
|
> Note: an 'appliable' is any object that has an `#apply` method.
|
459
578
|
|
460
579
|
> Note: `lino` ignores `nil` or empty appliables in the resulting command line.
|
461
580
|
|
462
|
-
|
581
|
+
#### Working Directory
|
582
|
+
|
583
|
+
By default, when a command line is executed, the working directory of the parent
|
584
|
+
process is used. This can be overridden with `#with_working_directory`:
|
585
|
+
|
586
|
+
```ruby
|
587
|
+
command_line = Lino.builder_for_command('ls')
|
588
|
+
.with_flag('-l')
|
589
|
+
.with_working_directory('/home/tobyclemson')
|
590
|
+
.build
|
591
|
+
|
592
|
+
command_line.working_directory
|
593
|
+
# => "/home/tobyclemson"
|
594
|
+
```
|
595
|
+
|
596
|
+
All built in executors honour the provided working directory, setting it on
|
597
|
+
spawned processes.
|
463
598
|
|
464
|
-
|
599
|
+
### Executing command lines
|
600
|
+
|
601
|
+
`Lino::Model::CommandLine` instances can be executed after construction. They
|
602
|
+
utilise an executor to achieve this, which is any object that has an
|
603
|
+
`#execute(command_line, opts)` method. `Lino` provides default executors such
|
604
|
+
that a custom executor only needs to be provided in special cases.
|
605
|
+
|
606
|
+
#### `#execute`
|
607
|
+
|
608
|
+
A `Lino::Model::CommandLine` instance can be executed using the `#execute`
|
609
|
+
method:
|
465
610
|
|
466
611
|
```ruby
|
467
|
-
command_line = Lino
|
612
|
+
command_line = Lino.builder_for_command('ls')
|
468
613
|
.with_flag('-l')
|
469
614
|
.with_flag('-a')
|
470
615
|
.with_argument('/')
|
471
616
|
.build
|
472
617
|
|
473
618
|
command_line.execute
|
474
|
-
|
475
619
|
# => <contents of / directory>
|
476
620
|
```
|
477
621
|
|
478
|
-
|
479
|
-
|
622
|
+
#### Standard Streams
|
623
|
+
|
624
|
+
By default, all streams are inherited from the parent process.
|
480
625
|
|
481
626
|
To populate standard input:
|
482
627
|
|
483
628
|
```ruby
|
484
|
-
|
629
|
+
require 'stringio'
|
630
|
+
|
631
|
+
command_line.execute(
|
632
|
+
stdin: StringIO.new('something to be passed to standard input')
|
633
|
+
)
|
485
634
|
```
|
486
635
|
|
487
|
-
The `stdin` option supports any object that responds to `
|
488
|
-
`to_s`.
|
636
|
+
The `stdin` option supports any object that responds to `read`.
|
489
637
|
|
490
638
|
To provide custom streams for standard output or standard error:
|
491
639
|
|
492
640
|
```ruby
|
493
|
-
require '
|
641
|
+
require 'tempfile'
|
494
642
|
|
495
|
-
stdout =
|
496
|
-
stderr =
|
643
|
+
stdout = Tempfile.new
|
644
|
+
stderr = Tempfile.new
|
497
645
|
|
498
646
|
command_line.execute(stdout: stdout, stderr: stderr)
|
647
|
+
|
648
|
+
stdout.rewind
|
649
|
+
stderr.rewind
|
499
650
|
|
500
|
-
puts "[output: #{stdout.
|
651
|
+
puts "[output: #{stdout.read}, error: #{stderr.read}]"
|
652
|
+
```
|
653
|
+
|
654
|
+
The `stdout` and `stderr` options support any instance of `IO` or a subclass.
|
655
|
+
|
656
|
+
#### Executors
|
657
|
+
|
658
|
+
`Lino` includes three built-in executors:
|
659
|
+
|
660
|
+
* `Lino::Executors::Childprocess` which is based on the
|
661
|
+
[`childprocess` gem](https://github.com/enkessler/childprocess)
|
662
|
+
* `Lino::Executors::Open4` which is based on the
|
663
|
+
[`open4` gem](https://github.com/ahoward/open4)
|
664
|
+
* `Lino::Executors::Mock` which does not start real processes and is useful for
|
665
|
+
use in tests.
|
666
|
+
|
667
|
+
##### Configuration
|
668
|
+
|
669
|
+
By default, an instance of `Lino::Executors::Childprocess` is used. This is
|
670
|
+
controlled by the default executor configured on `Lino`:
|
671
|
+
|
672
|
+
```ruby
|
673
|
+
Lino.configuration.executor
|
674
|
+
# => #<Lino::Executors::Childprocess:0x0000000103007108>
|
675
|
+
|
676
|
+
executor = Lino::Executors::Mock.new
|
677
|
+
|
678
|
+
Lino.configure do |config|
|
679
|
+
config.executor = executor
|
680
|
+
end
|
681
|
+
|
682
|
+
Lino.configuration.executor
|
683
|
+
# =>
|
684
|
+
# #<Lino::Executors::Mock:0x0000000106d4d3c8
|
685
|
+
# @executions=[],
|
686
|
+
# @exit_code=0,
|
687
|
+
# @stderr_contents=nil,
|
688
|
+
# @stdout_contents=nil>
|
689
|
+
|
690
|
+
Lino.reset!
|
691
|
+
|
692
|
+
Lino.configuration.executor
|
693
|
+
# => #<Lino::Executors::Childprocess:0x00000001090fcb48>
|
694
|
+
```
|
695
|
+
|
696
|
+
##### Builder overrides
|
697
|
+
|
698
|
+
Any built command will inherit the executor set as default at build time.
|
699
|
+
|
700
|
+
To override the executor on the builder, use `#with_executor`:
|
701
|
+
|
702
|
+
```ruby
|
703
|
+
executor = Lino::Executors::Mock.new
|
704
|
+
command_line = Lino.builder_for_command('ls')
|
705
|
+
.with_executor(executor)
|
706
|
+
.build
|
707
|
+
|
708
|
+
command_line.executor
|
709
|
+
# =>
|
710
|
+
# #<Lino::Executors::Mock:0x0000000108e7d890
|
711
|
+
# @executions=[],
|
712
|
+
# @exit_code=0,
|
713
|
+
# @stderr_contents=nil,
|
714
|
+
# @stdout_contents=nil>
|
715
|
+
```
|
716
|
+
|
717
|
+
##### Mock executor
|
718
|
+
|
719
|
+
The `Lino::Executors::Mock` captures executions without spawning any real
|
720
|
+
processes:
|
721
|
+
|
722
|
+
```ruby
|
723
|
+
executor = Lino::Executors::Mock.new
|
724
|
+
command_line = Lino.builder_for_command('ls')
|
725
|
+
.with_executor(executor)
|
726
|
+
.build
|
727
|
+
|
728
|
+
command_line.execute
|
729
|
+
|
730
|
+
executor.executions.length
|
731
|
+
# => 1
|
732
|
+
|
733
|
+
execution = executor.executions.first
|
734
|
+
execution.command_line == command_line
|
735
|
+
# => true
|
736
|
+
execution.exit_code
|
737
|
+
# => 0
|
738
|
+
```
|
739
|
+
|
740
|
+
The mock can be configured to write to any provided `stdout` or `stderr`:
|
741
|
+
|
742
|
+
```ruby
|
743
|
+
require 'tempfile'
|
744
|
+
|
745
|
+
executor = Lino::Executors::Mock.new
|
746
|
+
executor.write_to_stdout('hello!')
|
747
|
+
executor.write_to_stderr('error!')
|
748
|
+
|
749
|
+
command_line = Lino.builder_for_command('ls')
|
750
|
+
.with_executor(executor)
|
751
|
+
.build
|
752
|
+
|
753
|
+
stdout = Tempfile.new
|
754
|
+
stderr = Tempfile.new
|
755
|
+
|
756
|
+
command_line.execute(stdout:, stderr:)
|
757
|
+
|
758
|
+
stdout.rewind
|
759
|
+
stderr.rewind
|
760
|
+
|
761
|
+
stdout.read == 'hello!'
|
762
|
+
# => true
|
763
|
+
stderr.read == 'error!'
|
764
|
+
# => true
|
765
|
+
```
|
766
|
+
|
767
|
+
The mock also captures any provided `stdin`:
|
768
|
+
|
769
|
+
```ruby
|
770
|
+
require 'stringio'
|
771
|
+
|
772
|
+
executor = Lino::Executors::Mock.new
|
773
|
+
command_line = Lino.builder_for_command('ls')
|
774
|
+
.with_executor(executor)
|
775
|
+
.build
|
776
|
+
|
777
|
+
stdin = StringIO.new("input\n")
|
778
|
+
|
779
|
+
command_line.execute(stdin:)
|
780
|
+
|
781
|
+
execution = executor.executions.first
|
782
|
+
execution.stdin_contents
|
783
|
+
# => "input\n"
|
501
784
|
```
|
502
785
|
|
503
|
-
The
|
786
|
+
The mock can be configured to fail all executions:
|
787
|
+
|
788
|
+
```ruby
|
789
|
+
executor = Lino::Executors::Mock.new
|
790
|
+
executor.fail_all_executions
|
791
|
+
|
792
|
+
command_line = Lino.builder_for_command('ls')
|
793
|
+
.with_executor(executor)
|
794
|
+
.build
|
795
|
+
|
796
|
+
command_line.execute
|
797
|
+
# ...in `execute': Failed while executing command line.
|
798
|
+
# (Lino::Errors::ExecutionError)
|
799
|
+
|
800
|
+
command_line.execute
|
801
|
+
# ...in `execute': Failed while executing command line.
|
802
|
+
# (Lino::Errors::ExecutionError)
|
803
|
+
```
|
804
|
+
|
805
|
+
The exit code, which defaults to zero, can also be set explicitly, with anything
|
806
|
+
other than zero causing a `Lino::Errors::ExecutionError` to be raised:
|
807
|
+
|
808
|
+
```ruby
|
809
|
+
executor = Lino::Executors::Mock.new
|
810
|
+
executor.exit_code = 128
|
811
|
+
|
812
|
+
command_line = Lino.builder_for_command('ls')
|
813
|
+
.with_executor(executor)
|
814
|
+
.build
|
815
|
+
|
816
|
+
begin
|
817
|
+
command_line.execute
|
818
|
+
rescue Lino::Errors::ExecutionError => e
|
819
|
+
e.exit_code
|
820
|
+
end
|
821
|
+
# => 128
|
822
|
+
```
|
823
|
+
|
824
|
+
The mock is stateful and accumulates executions and configurations. To reset the
|
825
|
+
mock to its initial state:
|
826
|
+
|
827
|
+
```ruby
|
828
|
+
executor = Lino::Executors::Mock.new
|
829
|
+
executor.exit_code = 128
|
830
|
+
executor.write_to_stdout('hello!')
|
831
|
+
executor.write_to_stderr('error!')
|
832
|
+
|
833
|
+
executor.reset
|
834
|
+
|
835
|
+
executor.exit_code
|
836
|
+
# => 0
|
837
|
+
executor.stdout_contents
|
838
|
+
# => nil
|
839
|
+
executor.stderr_contents
|
840
|
+
# => nil
|
841
|
+
```
|
504
842
|
|
505
843
|
## Development
|
506
844
|
|
@@ -23,7 +23,7 @@ module Lino
|
|
23
23
|
quoting: nil,
|
24
24
|
placement: nil
|
25
25
|
)
|
26
|
-
return self if nil?
|
26
|
+
return self if value.nil?
|
27
27
|
|
28
28
|
with(options: @options.add(
|
29
29
|
{
|
@@ -69,7 +69,7 @@ module Lino
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def with_flag(flag)
|
72
|
-
return self if nil?
|
72
|
+
return self if flag.nil?
|
73
73
|
|
74
74
|
with(options: @options.add(
|
75
75
|
{
|
@@ -4,16 +4,12 @@ module Lino
|
|
4
4
|
module Builders
|
5
5
|
module Mixins
|
6
6
|
module Validation
|
7
|
-
def nil?(value)
|
8
|
-
value.nil?
|
9
|
-
end
|
10
|
-
|
11
7
|
def empty?(value)
|
12
8
|
value.respond_to?(:empty?) && value.empty?
|
13
9
|
end
|
14
10
|
|
15
11
|
def nil_or_empty?(value)
|
16
|
-
nil?
|
12
|
+
value.nil? || empty?(value)
|
17
13
|
end
|
18
14
|
end
|
19
15
|
end
|
data/lib/lino/executors/open4.rb
CHANGED
data/lib/lino/version.rb
CHANGED
data/lib/lino.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lino
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0.pre.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- InfraBlocks Maintainers
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-07-
|
11
|
+
date: 2024-07-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: childprocess
|