sekrets 0.4.2
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.
- 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
|