trocla 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -7
- data/.travis.yml +4 -0
- data/Gemfile +4 -1
- data/README.md +164 -17
- data/bin/trocla +31 -16
- data/lib/VERSION +2 -2
- data/lib/trocla.rb +48 -28
- data/lib/trocla/default_config.yaml +33 -4
- data/lib/trocla/encryptions.rb +3 -2
- data/lib/trocla/encryptions/ssl.rb +8 -10
- data/lib/trocla/formats/x509.rb +57 -30
- data/lib/trocla/store.rb +74 -0
- data/lib/trocla/stores.rb +39 -0
- data/lib/trocla/stores/memory.rb +56 -0
- data/lib/trocla/stores/moneta.rb +54 -0
- data/lib/trocla/util.rb +22 -8
- data/spec/spec_helper.rb +235 -20
- data/spec/trocla/encryptions/none_spec.rb +22 -0
- data/spec/trocla/encryptions/ssl_spec.rb +2 -32
- data/spec/trocla/formats/x509_spec.rb +295 -0
- data/spec/trocla/store/memory_spec.rb +6 -0
- data/spec/trocla/store/moneta_spec.rb +6 -0
- data/spec/trocla/util_spec.rb +24 -12
- data/spec/trocla_spec.rb +109 -76
- data/trocla.gemspec +31 -22
- metadata +120 -84
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
---
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5013d3c6ab75dc39bbbb5f7c8a77b19f7b5bed1c
|
4
|
+
data.tar.gz: bffc23e9979133c7303c7fde6b4b7a24fe367f8b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 009e2b762c641a8f10be76d673a3860b98cd3dd91a27b77da5d0775c9312da26f454c1c54a039bc9011d25c6c77dd6813b87007e0a71dcab35839fb09d5a3457
|
7
|
+
data.tar.gz: e873f4ac50bebf1ab00eddb06b3c9cca0040783a46195391f7064cebba0b2c47648454cef391e20831c405ff6280d2354f249050eb5605452f8e1f3f5becbdc7
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -11,12 +11,14 @@ else
|
|
11
11
|
gem "highline", "~> 1.6.2"
|
12
12
|
end
|
13
13
|
|
14
|
+
if defined?(RUBY_ENGINE) && (RUBY_ENGINE == 'jruby')
|
15
|
+
gem 'jruby-openssl'
|
16
|
+
end
|
14
17
|
gem "bcrypt"
|
15
18
|
|
16
19
|
# Add dependencies to develop your gem here.
|
17
20
|
# Include everything needed to run rake, tests, features, etc.
|
18
21
|
group :development do
|
19
|
-
gem "mocha"
|
20
22
|
if RUBY_VERSION.to_f > 1.8
|
21
23
|
gem "rspec"
|
22
24
|
gem "rdoc"
|
@@ -26,4 +28,5 @@ group :development do
|
|
26
28
|
gem "rdoc", "~> 3.8"
|
27
29
|
gem "jeweler", "~> 1.6"
|
28
30
|
end
|
31
|
+
gem 'rspec-pending_for'
|
29
32
|
end
|
data/README.md
CHANGED
@@ -11,21 +11,26 @@ Furthermore it provides you a simple cli that helps you to modify the password
|
|
11
11
|
storage from the cli.
|
12
12
|
|
13
13
|
Trocla does not only create and/or store a plain password, it is also able to
|
14
|
-
generate (and store) any kind hashed passwords based on the plain password.
|
14
|
+
generate (and store) any kind of hashed passwords based on the plain password.
|
15
15
|
As long as the plain password is preset, trocla is able to generate any kind
|
16
16
|
of hashed passwords through an easy extendible plugin system.
|
17
17
|
|
18
18
|
It is not necessary to store the plain password on the server, you can also
|
19
19
|
just feed trocla with the hashed password and use that in your other tools.
|
20
20
|
A common example for that is that you let puppet retrieve (and hence create)
|
21
|
-
a salted
|
21
|
+
a salted sha512 password for a user. This will then store the salted sha512 of
|
22
22
|
a random password AND the plain text password in trocla. Later you can
|
23
23
|
retrieve (by deleting) the plain password and send it to the user. Puppet
|
24
24
|
will still simply retrieve the hashed password that is stored in trocla,
|
25
25
|
while the plain password is not anymore stored on the server.
|
26
26
|
|
27
|
-
|
28
|
-
trocla. By default it uses a
|
27
|
+
Be default trocla uses moneta to store the passwords and can use any kind of
|
28
|
+
key/value based storage supported by moneta for trocla. By default it uses a
|
29
|
+
simple yaml file.
|
30
|
+
However, since version 0.2.0 trocla also supports a pluggable storage backend
|
31
|
+
which allows you to write your custom backend. See more about stores below.
|
32
|
+
|
33
|
+
Trocla can also be integrated into [Hiera](https://docs.puppetlabs.com/hiera/) by using ZeroPointEnergy's [hiera-backend](https://github.com/ZeroPointEnergy/hiera-backend-trocla).
|
29
34
|
|
30
35
|
## Usage
|
31
36
|
|
@@ -59,8 +64,11 @@ This will create a pgsql password hash using the username user1.
|
|
59
64
|
|
60
65
|
Valid global options are:
|
61
66
|
|
62
|
-
* length: int - Define any lenght that a newly created password should have. Default:
|
67
|
+
* length: int - Define any lenght that a newly created password should have. Default: 16 - or whatever you define in your global settings.
|
63
68
|
* charset: (default|alphanumeric|shellsafe) - Which set of chars should be used for a random password? Default: default - or whatever you define in your global settings.
|
69
|
+
* profiles: a profile name or an array of profiles matching a profile_name in your configuration. Learn more about profiles below.
|
70
|
+
* random: boolean - Whether we allow creation of random passwords or we expect a password to be preset. Default: true - or whatever you define in your global settings.
|
71
|
+
* expires: An integer indicating the amount of seconds a value (e.g. password) is available. After expiration a value will not be available anymore and trying to `get` this key will return no value (nil). Meaning that calling create after expiration, would create a new password automatically. There is more about expiration in the storage backends section.
|
64
72
|
|
65
73
|
Example:
|
66
74
|
|
@@ -88,15 +96,22 @@ far.
|
|
88
96
|
trocla set user3 plain
|
89
97
|
|
90
98
|
This will ask you for a password and set it under the appropriate key/format.
|
99
|
+
We expect a plain password to be entered and will format the password with
|
100
|
+
the selected format before storing it.
|
91
101
|
|
92
102
|
trocla set --password mysupersecretpassword user4 plain
|
93
103
|
|
94
104
|
This will take the password from the cli without asking you.
|
95
105
|
|
96
|
-
trocla set user5 mysql -p
|
106
|
+
trocla set user5 mysql -p mysuperdbpassword
|
97
107
|
|
98
108
|
This will store a mysql sha1 hash for the key user5, without storing any kind
|
99
109
|
of plain text password.
|
110
|
+
If you like trocla not to format a password, as you are passing in an already
|
111
|
+
formatted password (like the sha512 hash), then you must use `--no-format` to
|
112
|
+
skip formatting. Like:
|
113
|
+
|
114
|
+
trocla set user5 sha512crypt --no-format -p '$6$1234$xxxx....'
|
100
115
|
|
101
116
|
You can also pipe in a password:
|
102
117
|
|
@@ -124,37 +139,140 @@ deleted as well, as the hashes wouldn't match anymore the plain text password.
|
|
124
139
|
|
125
140
|
This will delete the plain password of the key user1 and return it.
|
126
141
|
|
142
|
+
### formats
|
143
|
+
|
144
|
+
trocla formats
|
145
|
+
|
146
|
+
This will list all available and supported formats.
|
147
|
+
|
127
148
|
## Attention
|
128
149
|
|
129
150
|
If you don't feed trocla initially with a hash and/or delete the generated
|
130
151
|
plain text passwords trocla will likely create a lot of plain text passwords
|
131
152
|
and store them on your machine/server. This is by intend and is all about which
|
132
153
|
problems (mainly passwords in configuration management manifests) trocla tries
|
133
|
-
to address.
|
154
|
+
to address. It is possible to store all passwords encrypted in the specific
|
155
|
+
backend.
|
156
|
+
See backend encryption for more information, however be aware that the key must
|
157
|
+
always also reside on the trocla node. So it mainly makes sense if you store
|
158
|
+
them on a remote backend like a central database server.
|
159
|
+
|
160
|
+
## Formats
|
161
|
+
|
162
|
+
Most formats are straight forward to use. Some formats require some additional
|
163
|
+
options to work properly. These are documented here:
|
164
|
+
|
165
|
+
### pgsql
|
166
|
+
|
167
|
+
Password hashes for PostgreSQL servers. Requires the option `username` to be set
|
168
|
+
to the username to which the password will be assigned.
|
169
|
+
|
170
|
+
### x509
|
171
|
+
|
172
|
+
This format takes a set of additional options. Required are:
|
173
|
+
|
174
|
+
subject: A subject for the target certificate. E.g. /C=ZZ/O=Trocla Inc./CN=test/emailAddress=example@example.com
|
175
|
+
OR
|
176
|
+
CN: The CN of the the target certificate. E.g. 'This is my self-signed certificate which doubles as CA'
|
177
|
+
|
178
|
+
Additional options are:
|
179
|
+
|
180
|
+
ca The trocla key of CA (imported into or generated within trocla) that
|
181
|
+
will be used to sign that certificate.
|
182
|
+
become_ca Whether the certificate should become a CA or not. Default: false,
|
183
|
+
to enable set it to true.
|
184
|
+
hash Hash to be used. Default sha2
|
185
|
+
keysize Keysize for the new key. Default is: 4096
|
186
|
+
serial Serial to be used, default is selecting a random one.
|
187
|
+
days How many days should the certificate be valid. Default 365
|
188
|
+
C instead within the subject string
|
189
|
+
ST instead within the subject string
|
190
|
+
L instead within the subject string
|
191
|
+
O instead within the subject string
|
192
|
+
OU instead within the subject string
|
193
|
+
emailAddress instead within the subject string
|
194
|
+
altnames An array of subjectAltNames. By default for non CA certificates we
|
195
|
+
ensure that the CN ends up here as well. If you don't want that.
|
196
|
+
You need to pass an empty array.
|
197
|
+
name_constraints An array of domains that are added as permitted x509 NameConstraint.
|
198
|
+
By default, we do not add any contraint, meaning all domains are
|
199
|
+
signable by the CA, as soon as we have one item in the list, only
|
200
|
+
DNS entries matching this list are allowed. Be aware, that older
|
201
|
+
openssl versions have a bug with [leading dots](https://rt.openssl.org/Ticket/Display.html?id=3562) for name
|
202
|
+
constraints. So using them might not work everywhere as expected.
|
134
203
|
|
135
204
|
## Installation
|
136
205
|
|
137
|
-
Simply build and install the gem.
|
206
|
+
Simply build and install the gem.
|
138
207
|
|
139
208
|
## Configuration
|
140
209
|
|
141
210
|
Trocla can be configured in /etc/troclarc.yaml and in ~/.troclarc.yaml. A sample configuration file can be found in `lib/trocla/default_config.yaml`.
|
211
|
+
By default trocla configures moneta to store all data in /tmp/trocla.yaml
|
212
|
+
|
213
|
+
### Profiles
|
214
|
+
|
215
|
+
It is possible to define profiles within the configuration file. The idea behind profiles are to make it easy to group together certain options for
|
216
|
+
automatic password generation.
|
217
|
+
|
218
|
+
Trocla ships with a default set of profiles, which are part of the `lib/trocla/default_config.yaml` configuration file. It is possible to override
|
219
|
+
the existing profiles within your own configuration file, as well as adding more. Note that the profiles part of the configuration file is merged
|
220
|
+
together and your configuration file has precedence.
|
221
|
+
|
222
|
+
The profiles part in the config is a hash where each entry consist of a name (key) and a hash of options (value).
|
223
|
+
|
224
|
+
Profiles make it especially easy to define a preset of options for SSL certificates as you will only need to set the certificate specific options,
|
225
|
+
while global options such as C, O or OU can be preset within the profile.
|
226
|
+
|
227
|
+
Profiles are used by setting the profiles option to a name of the pre-configured profiles, when passing options to the password option. On the cli
|
228
|
+
this looks like:
|
229
|
+
|
230
|
+
trocla create foo plain 'profiles: rootpw'
|
231
|
+
|
232
|
+
It is possible to pass mutliple profiles as an array, while the order will also reflect the precedence of the options.
|
233
|
+
|
234
|
+
Also it is possible to set a default profiles option in the options part of the configuration file.
|
142
235
|
|
143
236
|
### Storage backends
|
144
237
|
|
145
|
-
Trocla
|
238
|
+
Trocla has a pluggable storage backend, which allows you to choose the way that values are stored (persistently).
|
239
|
+
Such a store is a simple class that implements Trocla::Store and at the moment there are the following store implementations:
|
240
|
+
|
241
|
+
* Moneta - the default store using [moneta](https://rubygems.org/gems/moneta) to delegate storing the values
|
242
|
+
* Memory - simple inmemory backend. Mainly used for testing.
|
243
|
+
|
244
|
+
The backend is chosen based on the `store` configuration option. If it is a symbol, we expect it to be a store that we ship with trocla. Otherwise, we assume it to be a fully qualified ruby class name, that inherits from Trocla::Store. If trocla should load an additional library to be able to find your custom store class, you can set `store_require` to whatever should be passed to a ruby require statement.
|
245
|
+
|
246
|
+
Store backends can be configured through the `store_options` configuration.
|
247
|
+
|
248
|
+
#### Expiration
|
249
|
+
|
250
|
+
We expect storage backends to implement support for the `expires` option, so that keys expire after the passed amount of seconds. Furthermore a storage backend needs to implement the behaviour described by the rspec shared_example 'store_validation' section 'expiration'. Mainly:
|
251
|
+
|
252
|
+
* Expiration is always for all formats per key.
|
253
|
+
* Adding, deleting or updating a format will keep the existing expiration, but reset the planned expiration.
|
254
|
+
* While setting a new plain format will not only erase all other formats, but also erase/reset any expires.
|
255
|
+
* Setting a value with an expires option of 0 or false, will remove any existent expiration.
|
256
|
+
|
257
|
+
New backends should be tested using the provided shared example.
|
258
|
+
|
259
|
+
#### Moneta backends
|
260
|
+
|
261
|
+
Trocla uses moneta as its default storage backend and hence can store your passwords in any of moneta's supported backends. By default it uses the yaml backend, which is configured as followed:
|
146
262
|
|
147
263
|
```YAML
|
148
|
-
|
149
|
-
|
264
|
+
store_options:
|
265
|
+
adapter: :YAML
|
266
|
+
adapter_options:
|
150
267
|
:file: '/tmp/trocla.yaml'
|
151
268
|
```
|
152
269
|
|
153
270
|
In environments with multiple Puppet masters using an existing DB cluster might make sense. The configured user needs to be granted at least SELECT, INSERT, UPDATE, DELETE and CREATE permissions on your database:
|
154
271
|
|
155
272
|
```YAML
|
156
|
-
|
157
|
-
|
273
|
+
store_options:
|
274
|
+
adapter: :Sequel
|
275
|
+
adapter_options:
|
158
276
|
:db: 'mysql://db.server.name'
|
159
277
|
:user: 'trocla'
|
160
278
|
:password: '***'
|
@@ -162,21 +280,50 @@ adapter_options:
|
|
162
280
|
:table: 'trocla'
|
163
281
|
```
|
164
282
|
|
165
|
-
These examples are by no way complete, moneta has much more to offer.
|
283
|
+
These examples are by no way complete, moneta has much more to offer. Please have a look at [moneta's documentation](https://github.com/minad/moneta/blob/master/README.md) for further information.
|
166
284
|
|
167
|
-
###
|
285
|
+
### Backend encryption
|
168
286
|
|
169
|
-
You might want to let Trocla encrypt all your passwords
|
287
|
+
By default trocla does not encrypt anything it stores. You might want to let Trocla encrypt all your passwords, at the moment the only supported way is SSL.
|
288
|
+
Given that often trocla's store is on the same system at it's being used, there might be little sense to encrypt everything while the encryption keys are on the same system. However, if you are for example using an existing DB cluster using backend encryption you won't store any plaintext passwords within the database system.
|
289
|
+
|
290
|
+
### Backend SSL encryption
|
291
|
+
|
292
|
+
To enable SSL encryption (e.g. by using your puppet masters SSL keys), you need to set the following configuration options:
|
170
293
|
|
171
294
|
```YAML
|
172
295
|
encryption: :ssl
|
173
|
-
|
296
|
+
encryption_options:
|
174
297
|
:private_key: '/var/lib/puppet/ssl/private_keys/trocla.pem'
|
175
298
|
:public_key: '/var/lib/puppet/ssl/public_keys/trocla.pem'
|
176
299
|
```
|
177
300
|
|
178
301
|
## Update & Changes
|
179
302
|
|
303
|
+
### to 0.2.0
|
304
|
+
|
305
|
+
1. New feature profiles: Introduce profiles to make it easy to have a default set of properties. See the profiles section for more information.
|
306
|
+
1. New feature expiration: Make it possible that keys can have an expiration. See the expiration section for more information.
|
307
|
+
1. Increase default password length to 16.
|
308
|
+
1. Add a console safe password charset. It should provide a subset of chars that are easier to type on a physical keyboard.
|
309
|
+
1. Fix a bug with encryptions while deleting all formats.
|
310
|
+
1. Introduce pluggable stores, so in the future we are able to talk to different backends and not only moneta. For testing and inspiration a simple in memory storage backend was added.
|
311
|
+
1. CHANGE: moneta's configuration for `adapter` & `adapter_options` now live under store_options in the configuration file. Till 0.3.0 old configuration entries will still be accepted.
|
312
|
+
1. CHANGE: ssl_options is now known as encryption_options. Till 0.3.0 old configuration entries will still be accepted.
|
313
|
+
1. Improve randomness when creating a serial number.
|
314
|
+
1. Add a new charset: hexadecimal
|
315
|
+
1. Add support for name constraints within the x509 format
|
316
|
+
1. Clarify documentation of the set action, as well as introduce `--no-format` for the set action.
|
317
|
+
|
318
|
+
### to 0.1.3
|
319
|
+
|
320
|
+
1. CHANGE: Self signed certificates are no longer CAs by default, actually they have never been due to a bug. If you want that a certificate is also a CA, you *must* pass `become_ca: true` to the options hash. But this makes it actually possible, that you can even have certificate chains. Thanks for initial hint to [Adrien Bréfort](https://github.com/abrefort)
|
321
|
+
1. Default keysize is now 4096
|
322
|
+
1. SECURITY: Do not increment serial, rather choose a random one.
|
323
|
+
1. Fixing setting of altnames, was not possible due to bug, till now.
|
324
|
+
1. Add extended tests for the x509 format, that describe all the internal specialities and should give an idea how it can be used.
|
325
|
+
1. Add cli option to list all formats
|
326
|
+
|
180
327
|
### to 0.1.1
|
181
328
|
|
182
329
|
1. fix storing data longer that public Keysize -11. Thanks [Timo Goebel](https://github.com/timogoebel)
|
data/bin/trocla
CHANGED
@@ -9,12 +9,12 @@ require 'yaml'
|
|
9
9
|
options = { :config_file => nil, :ask_password => true, :trace => false }
|
10
10
|
|
11
11
|
OptionParser.new do |opts|
|
12
|
-
opts.on(
|
12
|
+
opts.on('--version', '-V', 'Version information') do
|
13
13
|
puts Trocla::VERSION::STRING
|
14
14
|
exit
|
15
15
|
end
|
16
16
|
|
17
|
-
opts.on(
|
17
|
+
opts.on('--config CONFIG', '-c', 'Configuration file') do |v|
|
18
18
|
if File.exist?(v)
|
19
19
|
options[:config_file] = v
|
20
20
|
else
|
@@ -23,19 +23,23 @@ OptionParser.new do |opts|
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
opts.on(
|
26
|
+
opts.on('--trace', 'Show stack trace on failure') do
|
27
27
|
options[:trace] = true
|
28
28
|
end
|
29
29
|
|
30
|
-
opts.on(
|
30
|
+
opts.on('--no-random', 'Do not generate a random password if there is no plain text password available') do
|
31
31
|
options['random'] = false
|
32
32
|
end
|
33
33
|
|
34
|
-
opts.on(
|
34
|
+
opts.on('--no-format', 'Do not format a password when setting it using `set`') do
|
35
|
+
options['no_format'] = true
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on('--length LENGTH', 'Length for a randomly created password') do |v|
|
35
39
|
options['length'] = v.to_i
|
36
40
|
end
|
37
41
|
|
38
|
-
opts.on(
|
42
|
+
opts.on('--password [PASSWORD]', '-p', 'Provide password at command line or STDIN') do |pass|
|
39
43
|
options[:ask_password] = false
|
40
44
|
options[:password] = pass
|
41
45
|
end
|
@@ -59,23 +63,29 @@ end
|
|
59
63
|
def set(options)
|
60
64
|
if options.delete(:ask_password)
|
61
65
|
require 'highline/import'
|
62
|
-
password = ask(
|
63
|
-
pwd2 = ask(
|
66
|
+
password = ask('Enter your password: ') { |q| q.echo = 'x' }.to_s
|
67
|
+
pwd2 = ask('Repeat password: ') { |q| q.echo = 'x' }.to_s
|
64
68
|
unless password == pwd2
|
65
|
-
STDERR.puts
|
69
|
+
STDERR.puts 'Passwords did not match, exiting!'
|
66
70
|
exit 1
|
67
71
|
end
|
68
72
|
else
|
69
73
|
password = options.delete(:password) || STDIN.read.chomp
|
70
74
|
end
|
71
75
|
format = options.delete(:trocla_format)
|
76
|
+
no_format = options.delete('no_format')
|
72
77
|
trocla = Trocla.new(options.delete(:config_file))
|
78
|
+
value = if no_format
|
79
|
+
password
|
80
|
+
else
|
81
|
+
trocla.formats(format).format(password, options.delete(:other_options).shift.to_s)
|
82
|
+
end
|
73
83
|
trocla.set_password(
|
74
84
|
options.delete(:trocla_key),
|
75
85
|
format,
|
76
|
-
|
86
|
+
value
|
77
87
|
)
|
78
|
-
|
88
|
+
''
|
79
89
|
end
|
80
90
|
|
81
91
|
def reset(options)
|
@@ -93,9 +103,13 @@ def delete(options)
|
|
93
103
|
)
|
94
104
|
end
|
95
105
|
|
106
|
+
def formats(options)
|
107
|
+
"Available formats: #{Trocla::Formats.all.join(', ')}"
|
108
|
+
end
|
109
|
+
|
96
110
|
def check_format(format_name)
|
97
111
|
if format_name.nil?
|
98
|
-
STDERR.puts
|
112
|
+
STDERR.puts 'Missing format, exiting...'
|
99
113
|
exit 1
|
100
114
|
elsif !Trocla::Formats.available?(format_name)
|
101
115
|
STDERR.puts "Error: The format #{format_name} is not available"
|
@@ -103,13 +117,13 @@ def check_format(format_name)
|
|
103
117
|
end
|
104
118
|
end
|
105
119
|
|
106
|
-
actions=['create','get','set','reset','delete']
|
120
|
+
actions=['create','get','set','reset','delete', 'formats' ]
|
107
121
|
|
108
|
-
if
|
122
|
+
if (action=ARGV.shift) && actions.include?(action)
|
109
123
|
options[:trocla_key] = ARGV.shift
|
110
124
|
options[:trocla_format] = ARGV.shift
|
111
125
|
options[:other_options] = ARGV
|
112
|
-
check_format(options[:trocla_format]) unless
|
126
|
+
check_format(options[:trocla_format]) unless ['delete','formats'].include?(action)
|
113
127
|
begin
|
114
128
|
if result = send(action,options)
|
115
129
|
puts result.is_a?(String) ? result : result.inspect
|
@@ -117,13 +131,14 @@ if !(ARGV.length < 2) && (action=ARGV.shift) && actions.include?(action)
|
|
117
131
|
rescue Exception => e
|
118
132
|
unless e.message == 'exit'
|
119
133
|
STDERR.puts "Action failed with the following message: #{e.message}"
|
120
|
-
STDERR.puts
|
134
|
+
STDERR.puts '(See full trace by running task with --trace)'
|
121
135
|
end
|
122
136
|
raise e if options[:trace]
|
123
137
|
exit 1
|
124
138
|
end
|
125
139
|
else
|
126
140
|
STDERR.puts "Please supply one of the following actions: #{actions.join(', ')}"
|
141
|
+
STDERR.puts "Use #{$0} --help to get a list of options for these actions"
|
127
142
|
exit 1
|
128
143
|
end
|
129
144
|
|
data/lib/VERSION
CHANGED