lino 3.2.0.pre.10 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2345fde1348f94927b9f8790c0a3d77a28b1bd0829eaa408249ef1b9ad06a9b1
|
4
|
+
data.tar.gz: 2ed278cbe9a30e2df451ccfa78bf592a2ceb6654bb70d1d37f4a3625aae37371
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 256de30d81d5bd07e9047878c3ed4691d2052997425c95d7a33bdba5cbf402cf3fcd5356945882fb5e8cbd85346e322a36c44c995ff32392c52435163314aac8
|
7
|
+
data.tar.gz: 4fdee42f644db2994a90d0de0465f8d04df02eac2be62f08d755b272dc75278f4894a7772f169935f8a7fc7e9c429466e86980491700e858cc014fe8f5ac9dce
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
lino (
|
4
|
+
lino (4.0.0)
|
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
|
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
|
@@ -336,9 +336,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
336
336
|
version: '3.1'
|
337
337
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
338
338
|
requirements:
|
339
|
-
- - "
|
339
|
+
- - ">="
|
340
340
|
- !ruby/object:Gem::Version
|
341
|
-
version:
|
341
|
+
version: '0'
|
342
342
|
requirements: []
|
343
343
|
rubygems_version: 3.3.7
|
344
344
|
signing_key:
|