sekrets 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +134 -0
- data/Rakefile +390 -0
- data/bin/sekrets +264 -0
- data/lib/sekrets/capistrano.rb +19 -0
- data/lib/sekrets.rb +343 -0
- data/sekrets.gemspec +50 -0
- data/test/lib/testing.rb +75 -0
- data/test/sekrets_test.rb +167 -0
- metadata +133 -0
data/bin/sekrets
ADDED
@@ -0,0 +1,264 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
Main {
|
4
|
+
#
|
5
|
+
synopsis <<-__
|
6
|
+
~> echo 'plaintext' | sekrets write sekrets.txt --key 42
|
7
|
+
|
8
|
+
~> sekrets read sekrets.txt --key 42
|
9
|
+
|
10
|
+
~> sekrets edit sekrets.txt
|
11
|
+
__
|
12
|
+
|
13
|
+
description <<-__
|
14
|
+
TL;DR
|
15
|
+
# create an encrypted config file
|
16
|
+
|
17
|
+
ruby -r yaml -e'puts {:api_key => 1234}.to_yaml' | sekrets write config/settings.yml.enc --key 42
|
18
|
+
|
19
|
+
# display it
|
20
|
+
|
21
|
+
sekrets read config/settings.yml.enc --key 42
|
22
|
+
|
23
|
+
# edit it
|
24
|
+
|
25
|
+
sekrets edit config/settings.yml.enc --key 42
|
26
|
+
|
27
|
+
# see that it's encrypted
|
28
|
+
|
29
|
+
cat config/settings.yml.enc
|
30
|
+
|
31
|
+
# commit it
|
32
|
+
|
33
|
+
git add config/settings.yml.enc
|
34
|
+
|
35
|
+
# put the decryption key in a file
|
36
|
+
|
37
|
+
echo 42 > sekrets.key
|
38
|
+
|
39
|
+
# ignore this file in git
|
40
|
+
|
41
|
+
echo sekrets.key >> .gitgnore
|
42
|
+
|
43
|
+
# make sure this file gets deployed on your server
|
44
|
+
|
45
|
+
echo " require 'sekrets/capistrano' " >> Capfile
|
46
|
+
|
47
|
+
# commit and deploy
|
48
|
+
|
49
|
+
git add config/settings.yml.enc
|
50
|
+
git commit -am'encrypted settings yo'
|
51
|
+
git pull && git push && cap staging deploy
|
52
|
+
|
53
|
+
# access these settings in your application code
|
54
|
+
|
55
|
+
settings = Sekrets.settings_for('./config/settings.yml.enc')
|
56
|
+
|
57
|
+
|
58
|
+
GENERAL
|
59
|
+
sekrets provides commandline tools and a library to manage and access
|
60
|
+
encrypted files in your code base.
|
61
|
+
|
62
|
+
it allows one to check encrypted infomation into a repository and to manage
|
63
|
+
it alongside the rest of the code base. it elimnates the need to check in
|
64
|
+
unencrypted information, keys, or other sensitive infomation.
|
65
|
+
|
66
|
+
sekrets provides both a general mechanism for managing arbitrary encrypted
|
67
|
+
files and a specific mechanism for managing encrypted config files.
|
68
|
+
|
69
|
+
|
70
|
+
KEY LOOKUP
|
71
|
+
for *all* operations, from the command line or otherwise, sekrets uses the
|
72
|
+
following algorithm to search for a decryption key:
|
73
|
+
|
74
|
+
- any key passed directly as a parameter to a library call will be preferred
|
75
|
+
|
76
|
+
- otherwise the code looks for a companion key file. for example, given the
|
77
|
+
file 'config/sekrets.yml.enc' sekrets will look for a key at
|
78
|
+
|
79
|
+
config/sekrets.yml.enc.key
|
80
|
+
|
81
|
+
and
|
82
|
+
|
83
|
+
config/sekrets.yml.enc.k
|
84
|
+
|
85
|
+
if either of these is found to be non-empty the contents of the file will
|
86
|
+
be used as the decryption key for that file. you should *never* commit
|
87
|
+
these key files and also add them to your .gitignore - or similar.
|
88
|
+
|
89
|
+
- next a project key file is looked for. the path of this file is
|
90
|
+
|
91
|
+
./sekrets.key
|
92
|
+
|
93
|
+
normally and, in a rails' application
|
94
|
+
|
95
|
+
RAILS_ROOT/sekrets.key
|
96
|
+
|
97
|
+
- if that is not found sekrets looks for the key in the environment under
|
98
|
+
the env var
|
99
|
+
|
100
|
+
SEKRETS_KEY
|
101
|
+
|
102
|
+
the env var used is configurable in the library
|
103
|
+
|
104
|
+
- next the global key file is search for, the path of this file is
|
105
|
+
|
106
|
+
~/.sekrets.key
|
107
|
+
|
108
|
+
- finally, if no key has yet been specified or found, the user is prompted
|
109
|
+
to input the key. prompt only occurs if the user us attached to a tty.
|
110
|
+
so, for example, no prompt will hang and application being started in the
|
111
|
+
background such as a rails' application being managed by passenger.
|
112
|
+
|
113
|
+
|
114
|
+
see Sekrets.key_for for more details
|
115
|
+
|
116
|
+
KEY DISTRIBUTION
|
117
|
+
sekrets does *not* attempt to solve the key distribution problem for you,
|
118
|
+
with one exception:
|
119
|
+
|
120
|
+
if you are using capistrano to do a 'vanilla' ssh based deploy a simple
|
121
|
+
recipe is provided which will detect a local keyfile and scp it onto the
|
122
|
+
remote server(s) on deploy.
|
123
|
+
|
124
|
+
sekrets assumes that the local keyfile, if it exists, is correct.
|
125
|
+
|
126
|
+
in plain english the capistrano recipe does:
|
127
|
+
|
128
|
+
scp ./sekrets.key deploy@remote.host.com:/rails_root/current/sekrets.key
|
129
|
+
|
130
|
+
it goes without saying that the local keyfile should *never* be checked in
|
131
|
+
and also should be in .gitignore
|
132
|
+
|
133
|
+
distribution of this key among developers is outside the scope of the
|
134
|
+
library. likely unencrypted email is the best mechanism for distribution
|
135
|
+
;-/
|
136
|
+
__
|
137
|
+
|
138
|
+
#
|
139
|
+
def run
|
140
|
+
help!
|
141
|
+
end
|
142
|
+
|
143
|
+
#
|
144
|
+
mode(:write){
|
145
|
+
argument('output', 'o'){
|
146
|
+
argument :required
|
147
|
+
default STDOUT
|
148
|
+
}
|
149
|
+
|
150
|
+
argument('input', 'i'){
|
151
|
+
argument :required
|
152
|
+
default STDIN
|
153
|
+
}
|
154
|
+
|
155
|
+
option('key', 'k'){
|
156
|
+
argument :required
|
157
|
+
}
|
158
|
+
|
159
|
+
def run
|
160
|
+
Sekrets.openw(params[:output].value) do |output|
|
161
|
+
key = key_for(output)
|
162
|
+
|
163
|
+
Sekrets.openr(params[:input].value) do |input|
|
164
|
+
decrypted = input.read
|
165
|
+
encrypted = Sekrets.encrypt(key, decrypted)
|
166
|
+
output.write(encrypted)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
}
|
171
|
+
|
172
|
+
#
|
173
|
+
mode(:read){
|
174
|
+
argument('input', 'i'){
|
175
|
+
argument :required
|
176
|
+
default STDIN
|
177
|
+
}
|
178
|
+
|
179
|
+
argument('output', 'o'){
|
180
|
+
argument :required
|
181
|
+
default STDOUT
|
182
|
+
}
|
183
|
+
|
184
|
+
option('key', 'k'){
|
185
|
+
argument :required
|
186
|
+
}
|
187
|
+
|
188
|
+
def run
|
189
|
+
Sekrets.openr(params[:input].value) do |input|
|
190
|
+
key = key_for(input)
|
191
|
+
|
192
|
+
Sekrets.openw(params[:output].value) do |output|
|
193
|
+
encrypted = input.read
|
194
|
+
decrypted = Sekrets.decrypt(key, encrypted)
|
195
|
+
output.write(decrypted)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
}
|
200
|
+
|
201
|
+
#
|
202
|
+
mode(:edit){
|
203
|
+
argument(:path)
|
204
|
+
|
205
|
+
option('key', 'k'){
|
206
|
+
argument :required
|
207
|
+
}
|
208
|
+
|
209
|
+
def run
|
210
|
+
path = params[:path].value
|
211
|
+
path = File.expand_path(path)
|
212
|
+
|
213
|
+
key = key_for(path)
|
214
|
+
|
215
|
+
decrypted =
|
216
|
+
if test(?s, path)
|
217
|
+
Sekrets.read(path, :key => key)
|
218
|
+
else
|
219
|
+
''
|
220
|
+
end
|
221
|
+
|
222
|
+
basename = File.basename(path)
|
223
|
+
encrypted = nil
|
224
|
+
|
225
|
+
Sekrets.tmpdir do
|
226
|
+
IO.binwrite(basename, decrypted)
|
227
|
+
|
228
|
+
command = "#{ Sekrets.editor } #{ basename }"
|
229
|
+
|
230
|
+
system(command)
|
231
|
+
|
232
|
+
if $?.exitstatus == 0
|
233
|
+
content = IO.binread(basename)
|
234
|
+
Sekrets.write(path, content, :key => key)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
}
|
239
|
+
|
240
|
+
#
|
241
|
+
def key_for(arg)
|
242
|
+
options = {}
|
243
|
+
|
244
|
+
if params[:key].given?
|
245
|
+
options[:key] = params[:key].value
|
246
|
+
end
|
247
|
+
|
248
|
+
key = Sekrets.key_for!(arg, options)
|
249
|
+
end
|
250
|
+
}
|
251
|
+
|
252
|
+
|
253
|
+
BEGIN {
|
254
|
+
require 'pathname'
|
255
|
+
bindir = Pathname.new(__FILE__).dirname.to_s
|
256
|
+
root = File.dirname(bindir)
|
257
|
+
libdir = File.join(root, 'lib')
|
258
|
+
require "#{ libdir }/sekrets.rb"
|
259
|
+
begin
|
260
|
+
require 'pry'
|
261
|
+
rescue Object
|
262
|
+
nil
|
263
|
+
end
|
264
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
2
|
+
namespace :sekrets do
|
3
|
+
task :upload_key do
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
rails_root = File.expand_path(File.dirname(__FILE__))
|
7
|
+
|
8
|
+
src = File.join(rails_root, 'sekrets.key')
|
9
|
+
dst = File.join(deploy_to, 'sekrets.key')
|
10
|
+
|
11
|
+
if test(?s, src)
|
12
|
+
upload(src, dst, :recursive => true)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
before "deploy:finalize_update", "sekrets:upload_key"
|
19
|
+
end
|
data/lib/sekrets.rb
ADDED
@@ -0,0 +1,343 @@
|
|
1
|
+
class Sekrets
|
2
|
+
#
|
3
|
+
Fattr(:env){ 'SEKRETS_KEY' }
|
4
|
+
Fattr(:editor){ ENV['SEKRETS_EDITOR'] || ENV['EDITOR'] || 'vim' }
|
5
|
+
Fattr(:root){ defined?(Rails.root) ? Rails.root : '.' }
|
6
|
+
Fattr(:project_key){ File.join(root, 'sekrets.key') }
|
7
|
+
Fattr(:global_key){ File.join(File.expand_path('~'), '.sekrets.key') }
|
8
|
+
|
9
|
+
#
|
10
|
+
def Sekrets.key_for(*args)
|
11
|
+
options = Map.options_for!(args)
|
12
|
+
path = args.shift || options[:path]
|
13
|
+
|
14
|
+
if options.has_key?(:key)
|
15
|
+
key = options[:key]
|
16
|
+
return(key)
|
17
|
+
end
|
18
|
+
|
19
|
+
path = path_for(path)
|
20
|
+
|
21
|
+
if path
|
22
|
+
keyfiles =
|
23
|
+
Coerce.list_of_strings(
|
24
|
+
[:keyfile, :keyfiles].map{|k| options[k]},
|
25
|
+
%W[ #{ path }.key #{ path }.k ]
|
26
|
+
)
|
27
|
+
|
28
|
+
keyfiles.each do |file|
|
29
|
+
if test(?s, file)
|
30
|
+
key = IO.binread(file).strip
|
31
|
+
return(key)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
if Sekrets.project_key and test(?s, Sekrets.project_key)
|
37
|
+
return IO.binread(Sekrets.project_key).strip
|
38
|
+
end
|
39
|
+
|
40
|
+
env_key = (options[:env] || Sekrets.env).to_s
|
41
|
+
if ENV.has_key?(env_key)
|
42
|
+
key = ENV[env_key]
|
43
|
+
return(key)
|
44
|
+
end
|
45
|
+
|
46
|
+
if Sekrets.global_key and test(?s, Sekrets.global_key)
|
47
|
+
return IO.binread(Sekrets.global_key).strip
|
48
|
+
end
|
49
|
+
|
50
|
+
unless options[:prompt] == false
|
51
|
+
if console?
|
52
|
+
key = Sekrets.ask(path)
|
53
|
+
return(key)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
return nil
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
def Sekrets.key_for!(*args, &block)
|
62
|
+
key = Sekrets.key_for(*args, &block)
|
63
|
+
raise(ArgumentError, 'no key!') unless key
|
64
|
+
key
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
def Sekrets.read(*args, &block)
|
69
|
+
options = Map.options_for!(args)
|
70
|
+
path = args.shift || options[:path]
|
71
|
+
key = args.shift || Sekrets.key_for!(path, options)
|
72
|
+
|
73
|
+
return nil unless test(?s, path)
|
74
|
+
|
75
|
+
encrypted = IO.binread(path)
|
76
|
+
decrypted = Sekrets.decrypt(key, encrypted)
|
77
|
+
new(decrypted)
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
def Sekrets.write(*args, &block)
|
82
|
+
options = Map.options_for!(args)
|
83
|
+
path = args.shift || options[:path]
|
84
|
+
content = args.shift || options[:content]
|
85
|
+
key = args.shift || Sekrets.key_for!(path, options)
|
86
|
+
|
87
|
+
dirname, basename = File.split(File.expand_path(path))
|
88
|
+
FileUtils.mkdir_p(dirname)
|
89
|
+
|
90
|
+
encrypted = Sekrets.encrypt(key, content)
|
91
|
+
|
92
|
+
tmp = path + '.tmp'
|
93
|
+
IO.binwrite(tmp, encrypted)
|
94
|
+
FileUtils.mv(tmp, path)
|
95
|
+
|
96
|
+
encrypted
|
97
|
+
new(encrypted)
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
def Sekrets.settings_for(*args, &block)
|
102
|
+
decrypted = read(*args, &block)
|
103
|
+
|
104
|
+
if decrypted
|
105
|
+
expanded = ERB.new(decrypted).result(TOPLEVEL_BINDING)
|
106
|
+
object = YAML.load(expanded)
|
107
|
+
object.is_a?(Hash) ? Map.for(object) : object
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
def Sekrets.prompt_for(*words)
|
113
|
+
["sekrets:", words, "> "].flatten.compact.join(' ')
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
def Sekrets.ask(question)
|
118
|
+
@highline ||= HighLine.new
|
119
|
+
@highline.ask(prompt_for(question))
|
120
|
+
end
|
121
|
+
|
122
|
+
#
|
123
|
+
def Sekrets.console?
|
124
|
+
STDIN.tty?
|
125
|
+
end
|
126
|
+
|
127
|
+
#
|
128
|
+
def Sekrets.tmpdir(&block)
|
129
|
+
dirname = File.join(Dir.tmpdir, 'sekrets', Process.ppid.to_s, Process.pid.to_s, rand.to_s)
|
130
|
+
|
131
|
+
FileUtils.mkdir_p(dirname)
|
132
|
+
|
133
|
+
cleanup = proc do
|
134
|
+
if dirname and test(?d, dirname)
|
135
|
+
FileUtils.rm_rf(dirname)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
if block
|
140
|
+
begin
|
141
|
+
Dir.chdir(dirname) do
|
142
|
+
block.call(dirname)
|
143
|
+
end
|
144
|
+
ensure
|
145
|
+
cleanup.call
|
146
|
+
end
|
147
|
+
else
|
148
|
+
at_exit{ cleanup.call }
|
149
|
+
dirname
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
#
|
154
|
+
def Sekrets.openw(arg, &block)
|
155
|
+
opened = false
|
156
|
+
atomic_move = proc{}
|
157
|
+
|
158
|
+
io =
|
159
|
+
case
|
160
|
+
when arg.respond_to?(:read)
|
161
|
+
arg
|
162
|
+
when arg.to_s.strip == '-'
|
163
|
+
STDOUT
|
164
|
+
else
|
165
|
+
opened = true
|
166
|
+
path = File.expand_path(arg.to_s)
|
167
|
+
dirname, basename = File.split(path)
|
168
|
+
FileUtils.mkdir_p(dirname)
|
169
|
+
tmp = path + ".sekrets.tmp.#{ Process.ppid }.#{ Process.pid }"
|
170
|
+
at_exit{ FileUtils.rm_f(tmp) }
|
171
|
+
atomic_move = proc{ FileUtils.mv(tmp, path) }
|
172
|
+
open(tmp, 'wb+')
|
173
|
+
end
|
174
|
+
|
175
|
+
close =
|
176
|
+
proc do
|
177
|
+
io.close if opened
|
178
|
+
atomic_move.call
|
179
|
+
end
|
180
|
+
|
181
|
+
if block
|
182
|
+
begin
|
183
|
+
block.call(io)
|
184
|
+
ensure
|
185
|
+
close.call
|
186
|
+
end
|
187
|
+
else
|
188
|
+
at_exit{ close.call }
|
189
|
+
io
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
#
|
194
|
+
def Sekrets.openr(arg, &block)
|
195
|
+
opened = false
|
196
|
+
|
197
|
+
io =
|
198
|
+
case
|
199
|
+
when arg.respond_to?(:read)
|
200
|
+
arg
|
201
|
+
when arg.to_s.strip == '-'
|
202
|
+
STDIN
|
203
|
+
else
|
204
|
+
opened = true
|
205
|
+
open(arg, 'rb+')
|
206
|
+
end
|
207
|
+
|
208
|
+
close =
|
209
|
+
proc do
|
210
|
+
io.close if opened
|
211
|
+
end
|
212
|
+
|
213
|
+
if block
|
214
|
+
begin
|
215
|
+
block.call(io)
|
216
|
+
ensure
|
217
|
+
close.call
|
218
|
+
end
|
219
|
+
else
|
220
|
+
at_exit{ close.call }
|
221
|
+
io
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
#
|
226
|
+
def Sekrets.path_for(object)
|
227
|
+
path = nil
|
228
|
+
|
229
|
+
if object.is_a?(String) or object.is_a?(Pathname)
|
230
|
+
return(path = object.to_s)
|
231
|
+
end
|
232
|
+
|
233
|
+
[:original_path, :original_filename, :path, :filename, :pathname].each do |msg|
|
234
|
+
if object.respond_to?(msg)
|
235
|
+
path = object.send(msg)
|
236
|
+
break
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
path
|
241
|
+
end
|
242
|
+
|
243
|
+
#
|
244
|
+
module Blowfish
|
245
|
+
def cipher(mode, key, data)
|
246
|
+
cipher = OpenSSL::Cipher::Cipher.new('bf-cbc').send(mode)
|
247
|
+
cipher.key = Digest::SHA256.digest(key.to_s)
|
248
|
+
cipher.update(data) << cipher.final
|
249
|
+
end
|
250
|
+
|
251
|
+
def encrypt(key, data)
|
252
|
+
cipher(:encrypt, key, data)
|
253
|
+
end
|
254
|
+
|
255
|
+
def decrypt(key, text)
|
256
|
+
cipher(:decrypt, key, text)
|
257
|
+
end
|
258
|
+
|
259
|
+
def cycle(key, data)
|
260
|
+
decrypt(key, encrypt(key, data))
|
261
|
+
end
|
262
|
+
|
263
|
+
def recrypt(old_key, new_key, data)
|
264
|
+
encrypt(new_key, decrypt(old_key, data))
|
265
|
+
end
|
266
|
+
|
267
|
+
extend(self)
|
268
|
+
end
|
269
|
+
|
270
|
+
extend(Blowfish)
|
271
|
+
end
|
272
|
+
|
273
|
+
|
274
|
+
Sekret = Sekrets
|
275
|
+
|
276
|
+
|
277
|
+
|
278
|
+
BEGIN {
|
279
|
+
|
280
|
+
require 'openssl'
|
281
|
+
require 'fileutils'
|
282
|
+
require 'erb'
|
283
|
+
require 'yaml'
|
284
|
+
require 'tmpdir'
|
285
|
+
|
286
|
+
class Sekrets < ::String
|
287
|
+
Version = '0.4.2' unless defined?(Version)
|
288
|
+
|
289
|
+
class << Sekrets
|
290
|
+
def version
|
291
|
+
Sekrets::Version
|
292
|
+
end
|
293
|
+
|
294
|
+
def dependencies
|
295
|
+
{
|
296
|
+
'highline' => [ 'highline' , ' >= 1.6.15' ] ,
|
297
|
+
'map' => [ 'map' , ' >= 6.3.0' ] ,
|
298
|
+
'fattr' => [ 'fattr' , ' >= 2.2.1' ] ,
|
299
|
+
'coerce' => [ 'coerce' , ' >= 0.0.3' ] ,
|
300
|
+
'main' => [ 'main' , ' >= 5.1.1' ] ,
|
301
|
+
}
|
302
|
+
end
|
303
|
+
|
304
|
+
def libdir(*args, &block)
|
305
|
+
@libdir ||= File.expand_path(__FILE__).sub(/\.rb$/,'')
|
306
|
+
args.empty? ? @libdir : File.join(@libdir, *args)
|
307
|
+
ensure
|
308
|
+
if block
|
309
|
+
begin
|
310
|
+
$LOAD_PATH.unshift(@libdir)
|
311
|
+
block.call()
|
312
|
+
ensure
|
313
|
+
$LOAD_PATH.shift()
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
def load(*libs)
|
319
|
+
libs = libs.join(' ').scan(/[^\s+]+/)
|
320
|
+
Sekrets.libdir{ libs.each{|lib| Kernel.load(lib) } }
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
begin
|
326
|
+
require 'rubygems'
|
327
|
+
rescue LoadError
|
328
|
+
nil
|
329
|
+
end
|
330
|
+
|
331
|
+
Sekrets.dependencies.each do |lib, dependency|
|
332
|
+
gem(*dependency) if defined?(gem)
|
333
|
+
require(lib)
|
334
|
+
end
|
335
|
+
|
336
|
+
Sekrets.fattr(:description){
|
337
|
+
<<-__
|
338
|
+
|
339
|
+
foobar
|
340
|
+
|
341
|
+
__
|
342
|
+
}
|
343
|
+
}
|
data/sekrets.gemspec
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
## sekrets.gemspec
|
2
|
+
#
|
3
|
+
|
4
|
+
Gem::Specification::new do |spec|
|
5
|
+
spec.name = "sekrets"
|
6
|
+
spec.version = "0.4.2"
|
7
|
+
spec.platform = Gem::Platform::RUBY
|
8
|
+
spec.summary = "sekrets"
|
9
|
+
spec.description = "description: sekrets kicks the ass"
|
10
|
+
|
11
|
+
spec.files =
|
12
|
+
["README",
|
13
|
+
"Rakefile",
|
14
|
+
"bin",
|
15
|
+
"bin/sekrets",
|
16
|
+
"lib",
|
17
|
+
"lib/sekrets",
|
18
|
+
"lib/sekrets.rb",
|
19
|
+
"lib/sekrets/capistrano.rb",
|
20
|
+
"sekrets.gemspec",
|
21
|
+
"test",
|
22
|
+
"test/lib",
|
23
|
+
"test/lib/testing.rb",
|
24
|
+
"test/sekrets_test.rb"]
|
25
|
+
|
26
|
+
spec.executables = ["sekrets"]
|
27
|
+
|
28
|
+
spec.require_path = "lib"
|
29
|
+
|
30
|
+
spec.test_files = nil
|
31
|
+
|
32
|
+
|
33
|
+
spec.add_dependency(*["highline", " >= 1.6.15"])
|
34
|
+
|
35
|
+
spec.add_dependency(*["map", " >= 6.3.0"])
|
36
|
+
|
37
|
+
spec.add_dependency(*["fattr", " >= 2.2.1"])
|
38
|
+
|
39
|
+
spec.add_dependency(*["coerce", " >= 0.0.3"])
|
40
|
+
|
41
|
+
spec.add_dependency(*["main", " >= 5.1.1"])
|
42
|
+
|
43
|
+
|
44
|
+
spec.extensions.push(*[])
|
45
|
+
|
46
|
+
spec.rubyforge_project = "codeforpeople"
|
47
|
+
spec.author = "Ara T. Howard"
|
48
|
+
spec.email = "ara.t.howard@gmail.com"
|
49
|
+
spec.homepage = "https://github.com/ahoward/sekrets"
|
50
|
+
end
|