mellon 1.1.1 → 1.2.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/CHANGELOG.md +9 -1
- data/README.md +5 -5
- data/Rakefile +3 -0
- data/lib/mellon.rb +0 -1
- data/lib/mellon/keychain.rb +8 -7
- data/lib/mellon/store.rb +5 -0
- data/lib/mellon/utils.rb +45 -0
- data/lib/mellon/version.rb +1 -1
- data/mellon.gemspec +1 -0
- data/spec/mellon/keychain_spec.rb +109 -37
- data/spec/mellon/store_spec.rb +31 -21
- data/spec/mellon_spec.rb +1 -1
- data/spec/spec_helper.rb +12 -8
- metadata +18 -4
- data/lib/mellon/shell_utils.rb +0 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 76d1c2268781b0099e7c924852a17bfbe0f52fa6
|
4
|
+
data.tar.gz: 108c0fa6d05f1f87307fcc52e9c15738b83b9c32
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d17a055a2d6f33bb9099bdfca03cee93e0b6ee70ab133b4ae9e749a115319af8f373d72632698c1a500f87d73d701f484d8db4acf9cb9581098ef113d3ed1c59
|
7
|
+
data.tar.gz: b3ec39f219f671ff33ffb60a14f6f73eee5cbb10551e2f3a66c119acacd043da68dd4845c41721ccd36fcd2537c0674633cecd473ba53f6fab2b42a9c58bc42e
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
[HEAD][]
|
2
2
|
--------
|
3
3
|
|
4
|
+
[v1.2.0][]
|
5
|
+
----------
|
6
|
+
|
7
|
+
- Add Mellon::Store#to_h (47523c2f)
|
8
|
+
- Add more verbosity when $DEBUG is set (29b97367)
|
9
|
+
- Match only keychain name in Keychain.find (44693409) [closes #5]
|
10
|
+
|
4
11
|
[v1.1.1][]
|
5
12
|
----------
|
6
13
|
|
@@ -21,7 +28,8 @@
|
|
21
28
|
|
22
29
|
Initial release!
|
23
30
|
|
24
|
-
[HEAD]: https://github.com/elabs/mellon/compare/v1.
|
31
|
+
[HEAD]: https://github.com/elabs/mellon/compare/v1.2.0...HEAD
|
32
|
+
[v1.2.0]: https://github.com/elabs/mellon/compare/v1.1.1...v1.2.0
|
25
33
|
[v1.1.1]: https://github.com/elabs/mellon/compare/v1.1.0...v1.1.1
|
26
34
|
[v1.1.0]: https://github.com/elabs/mellon/compare/v1.0.0...v1.1.0
|
27
35
|
[v1.0.0]: https://github.com/elabs/mellon/compare/24b83977d...v1.0.0
|
data/README.md
CHANGED
@@ -32,7 +32,7 @@ See instructions over at [econfig-keychain](https://github.com/elabs/econfig-key
|
|
32
32
|
|
33
33
|
# Documentation
|
34
34
|
|
35
|
-
An API reference can be found at [
|
35
|
+
An API reference can be found at [rubydoc.info/github/elabs/mellon](http://www.rubydoc.info/github/elabs/mellon/).
|
36
36
|
|
37
37
|
## Mellon::Keychain
|
38
38
|
|
@@ -46,7 +46,7 @@ keychain["ruby note"] # => "Hello from Ruby!"
|
|
46
46
|
keychain["ruby note"] = nil # deletes keychain note `ruby note`
|
47
47
|
```
|
48
48
|
|
49
|
-
More documentation can be found at the [API reference for Mellon::Keychain](http://
|
49
|
+
More documentation can be found at the [API reference for Mellon::Keychain](http://www.rubydoc.info/github/elabs/mellon/Mellon/Keychain).
|
50
50
|
|
51
51
|
## Mellon::Store
|
52
52
|
|
@@ -63,7 +63,7 @@ store["some key"] # => "Hello from Ruby!"
|
|
63
63
|
store.keychain[store.project_name] # => "---\nsome key: Hello from Ruby!\n"
|
64
64
|
```
|
65
65
|
|
66
|
-
More documentation can be found at the [API reference for Mellon::Store](http://
|
66
|
+
More documentation can be found at the [API reference for Mellon::Store](http://www.rubydoc.info/github/elabs/mellon/Mellon/Store).
|
67
67
|
|
68
68
|
## Command-line interface
|
69
69
|
|
@@ -75,5 +75,5 @@ When you install the Mellon gem you also get an executable called `mellon`. See
|
|
75
75
|
|
76
76
|
[Elabs]: http://www.elabs.se/
|
77
77
|
[elabs logo]: ./elabs-logo.png?raw=true
|
78
|
-
[Mellon::Keychain]: #
|
79
|
-
[Mellon::Store]: #
|
78
|
+
[Mellon::Keychain]: #mellonkeychain
|
79
|
+
[Mellon::Store]: #mellonstore
|
data/Rakefile
CHANGED
data/lib/mellon.rb
CHANGED
data/lib/mellon/keychain.rb
CHANGED
@@ -11,7 +11,7 @@ module Mellon
|
|
11
11
|
# @param [String] key
|
12
12
|
# @return [Keychain, nil]
|
13
13
|
def search(key)
|
14
|
-
output =
|
14
|
+
output = Utils.security("find-generic-password", "-l", key)
|
15
15
|
new(output[/keychain: "(.+)"/i, 1], ensure_exists: false)
|
16
16
|
rescue CommandError => e
|
17
17
|
raise unless e.message =~ ENTRY_MISSING
|
@@ -26,13 +26,14 @@ module Mellon
|
|
26
26
|
def find(name)
|
27
27
|
quoted = Regexp.quote(name)
|
28
28
|
regexp = Regexp.new(quoted, Regexp::IGNORECASE)
|
29
|
+
keychains = list
|
29
30
|
|
30
|
-
keychain =
|
31
|
-
keychain.
|
31
|
+
keychain = keychains.find do |keychain|
|
32
|
+
keychain.name =~ regexp
|
32
33
|
end
|
33
34
|
|
34
35
|
if keychain.nil?
|
35
|
-
raise KeyError, "Could not find keychain “#{name}” in #{
|
36
|
+
raise KeyError, "Could not find keychain “#{name}” in #{keychains.map(&:name).join(", ")}"
|
36
37
|
end
|
37
38
|
|
38
39
|
keychain
|
@@ -40,13 +41,13 @@ module Mellon
|
|
40
41
|
|
41
42
|
# @return [Keychain] default keychain
|
42
43
|
def default
|
43
|
-
keychain_path =
|
44
|
+
keychain_path = Utils.security("default-keychain")[KEYCHAIN_REGEXP, 1]
|
44
45
|
new(keychain_path, ensure_exists: false)
|
45
46
|
end
|
46
47
|
|
47
48
|
# @return [Array<Keychain>] all available keychains
|
48
49
|
def list
|
49
|
-
|
50
|
+
Utils.security("list-keychains").scan(KEYCHAIN_REGEXP).map do |(keychain_path)|
|
50
51
|
new(keychain_path, ensure_exists: false)
|
51
52
|
end
|
52
53
|
end
|
@@ -197,7 +198,7 @@ module Mellon
|
|
197
198
|
# @param [Array<String>] command
|
198
199
|
def command(*command, &block)
|
199
200
|
command += [path]
|
200
|
-
|
201
|
+
Utils.security *command, &block
|
201
202
|
end
|
202
203
|
end
|
203
204
|
end
|
data/lib/mellon/store.rb
CHANGED
data/lib/mellon/utils.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
+
require "open3"
|
2
|
+
require "shellwords"
|
3
|
+
|
1
4
|
module Mellon
|
5
|
+
# @api private
|
2
6
|
module Utils
|
3
7
|
module_function
|
4
8
|
|
@@ -88,5 +92,46 @@ module Mellon
|
|
88
92
|
password
|
89
93
|
end
|
90
94
|
end
|
95
|
+
|
96
|
+
# @see #sh
|
97
|
+
def security(*command, &block)
|
98
|
+
sh("security", *command, &block)
|
99
|
+
end
|
100
|
+
|
101
|
+
# @overload sh(*command, &block)
|
102
|
+
# Yields stdout and stderr to the given block.
|
103
|
+
#
|
104
|
+
# @overload sh(*command)
|
105
|
+
# Returns stdout.
|
106
|
+
#
|
107
|
+
# @raise [CommandError] if command exited with a non-zero exit status.
|
108
|
+
def sh(*command)
|
109
|
+
$stderr.puts "$ " + command.join(" ") if $VERBOSE
|
110
|
+
stdout, stderr, status = Open3.capture3(*command)
|
111
|
+
|
112
|
+
stdout.chomp!
|
113
|
+
stderr.chomp!
|
114
|
+
|
115
|
+
if $DEBUG
|
116
|
+
$stderr.puts stdout.gsub(/(?<=\n|\A)/, "--> ") unless stdout.empty?
|
117
|
+
$stderr.puts stderr.gsub(/(?<=\n|\A)/, "--! ") unless stderr.empty?
|
118
|
+
end
|
119
|
+
|
120
|
+
unless status.success?
|
121
|
+
error_string = Shellwords.join(command)
|
122
|
+
error_string << "\n"
|
123
|
+
|
124
|
+
stderr = "<no output>" if stderr.empty?
|
125
|
+
error_string << " " << stderr.chomp
|
126
|
+
|
127
|
+
raise CommandError, "[ERROR] #{error_string}"
|
128
|
+
end
|
129
|
+
|
130
|
+
if block_given?
|
131
|
+
yield [stdout, stderr]
|
132
|
+
else
|
133
|
+
stdout
|
134
|
+
end
|
135
|
+
end
|
91
136
|
end
|
92
137
|
end
|
data/lib/mellon/version.rb
CHANGED
data/mellon.gemspec
CHANGED
@@ -3,27 +3,108 @@ describe Mellon::Keychain do
|
|
3
3
|
Mellon::Keychain.new(keychain_path)
|
4
4
|
end
|
5
5
|
|
6
|
+
describe ".search" do
|
7
|
+
it "returns the first keychain that contains the requested entry" do
|
8
|
+
stub_command "security find-generic-password -l booboom", stdout: <<-STDOUT
|
9
|
+
keychain: "/Users/dev/Library/Keychains/projects.keychain"
|
10
|
+
class: "genp"
|
11
|
+
attributes:
|
12
|
+
0x00000007 <blob>="booboom"
|
13
|
+
0x00000008 <blob>=<NULL>
|
14
|
+
"acct"<blob>=<NULL>
|
15
|
+
"cdat"<timedate>=0x32303135303132333037343035395A00 "20150123074059Z\000"
|
16
|
+
"crtr"<uint32>=<NULL>
|
17
|
+
"cusi"<sint32>=<NULL>
|
18
|
+
"desc"<blob>="secure note"
|
19
|
+
"gena"<blob>=<NULL>
|
20
|
+
"icmt"<blob>=<NULL>
|
21
|
+
"invi"<sint32>=<NULL>
|
22
|
+
"mdat"<timedate>=0x32303135303132333037343035395A00 "20150123074059Z\000"
|
23
|
+
"nega"<sint32>=<NULL>
|
24
|
+
"prot"<blob>=<NULL>
|
25
|
+
"scrp"<sint32>=<NULL>
|
26
|
+
"svce"<blob>="booboom"
|
27
|
+
"type"<uint32>="note"
|
28
|
+
STDOUT
|
29
|
+
|
30
|
+
keychain = Mellon::Keychain.search "booboom"
|
31
|
+
expect(keychain.path).to eq "/Users/dev/Library/Keychains/projects.keychain"
|
32
|
+
expect(keychain.name).to eq "projects"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "returns nil if the entry is missing" do
|
36
|
+
stub_command "security find-generic-password -l booboom", error: true, stderr: <<-STDERR
|
37
|
+
security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.
|
38
|
+
STDERR
|
39
|
+
|
40
|
+
keychain = Mellon::Keychain.search "booboom"
|
41
|
+
expect(keychain).to eq nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe ".find" do
|
46
|
+
before do
|
47
|
+
stub_command "security list-keychains", stdout: <<-STDOUT
|
48
|
+
"/Users/dev/Library/Keychains/login.keychain"
|
49
|
+
"/Users/dev/Library/Keychains/projects.keychain"
|
50
|
+
"/Users/dev/Library/Keychains/developer.keychain"
|
51
|
+
"/Library/Keychains/System.keychain"
|
52
|
+
STDOUT
|
53
|
+
end
|
54
|
+
|
55
|
+
it "finds a keychain matching the given name" do
|
56
|
+
keychain = Mellon::Keychain.find("dev")
|
57
|
+
expect(keychain.name).to eq "developer"
|
58
|
+
expect(keychain.path).to eq "/Users/dev/Library/Keychains/developer.keychain"
|
59
|
+
end
|
60
|
+
|
61
|
+
it "raises an error if no keychain was found" do
|
62
|
+
expect { Mellon::Keychain.find("bar") }.to raise_error(KeyError, /Could not find keychain/)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
specify ".list" do
|
67
|
+
stub_command "security list-keychains", stdout: <<-STDOUT
|
68
|
+
"/Users/dev/Library/Keychains/login.keychain"
|
69
|
+
"/Users/dev/Library/Keychains/projects.keychain"
|
70
|
+
"/Users/dev/Library/Keychains/developer.keychain"
|
71
|
+
"/Library/Keychains/System.keychain"
|
72
|
+
STDOUT
|
73
|
+
|
74
|
+
expect(Mellon::Keychain.list.map(&:name)).to contain_exactly("login", "projects", "developer", "System")
|
75
|
+
end
|
76
|
+
|
77
|
+
specify ".default" do
|
78
|
+
stub_command "security default-keychain", stdout: <<-STDOUT
|
79
|
+
"/Users/dev/Library/Keychains/login.keychain"
|
80
|
+
STDOUT
|
81
|
+
|
82
|
+
default = Mellon::Keychain.default
|
83
|
+
expect(default.path).to eq "/Users/dev/Library/Keychains/login.keychain"
|
84
|
+
expect(default.name).to eq "login"
|
85
|
+
end
|
86
|
+
|
6
87
|
specify "#name" do
|
7
|
-
keychain.name.
|
88
|
+
expect(keychain.name).to eq "temporary_keychain"
|
8
89
|
end
|
9
90
|
|
10
91
|
specify "#path" do
|
11
|
-
keychain.path.
|
92
|
+
expect(keychain.path).to eq keychain_path
|
12
93
|
end
|
13
94
|
|
14
95
|
specify "keychain can be stored in hash" do
|
15
96
|
hash = {}
|
16
97
|
hash[keychain] = "some value"
|
17
|
-
hash[Mellon::Keychain.new(keychain.path)].
|
98
|
+
expect(hash[Mellon::Keychain.new(keychain.path)]).to eq "some value"
|
18
99
|
end
|
19
100
|
|
20
101
|
describe "#==" do
|
21
102
|
it "is equal to another keychain with same path" do
|
22
|
-
keychain.
|
103
|
+
expect(keychain).to eq Mellon::Keychain.new(keychain.path)
|
23
104
|
end
|
24
105
|
|
25
106
|
it "is not equal to any other object" do
|
26
|
-
keychain.
|
107
|
+
expect(keychain).to_not eq({})
|
27
108
|
end
|
28
109
|
end
|
29
110
|
|
@@ -35,31 +116,31 @@ describe Mellon::Keychain do
|
|
35
116
|
|
36
117
|
describe "#keys" do
|
37
118
|
it "lists all keys available in the keychain" do
|
38
|
-
keychain.keys.
|
119
|
+
expect(keychain.keys).to contain_exactly("simple", "existing", "encoded", "plist", "empty", "doomed", "json store", "yaml store")
|
39
120
|
end
|
40
121
|
end
|
41
122
|
|
42
123
|
describe "#fetch" do
|
43
124
|
it "delegates (and as such, behaves equally) to #[]" do
|
44
|
-
keychain.
|
45
|
-
keychain.fetch("simple").
|
125
|
+
expect(keychain).to receive(:[]).with("simple").and_call_original
|
126
|
+
expect(keychain.fetch("simple")).to eq "Simple note"
|
46
127
|
end
|
47
128
|
|
48
129
|
describe "behaves like Hash#fetch" do
|
49
130
|
specify "when key exists" do
|
50
|
-
keychain.fetch("simple", nil).
|
51
|
-
keychain.fetch("simple", "default value").
|
52
|
-
keychain.fetch("simple", "default value") { "block value" }.
|
53
|
-
keychain.fetch("simple") { "block value" }.
|
131
|
+
expect(keychain.fetch("simple", nil)).to eq "Simple note"
|
132
|
+
expect(keychain.fetch("simple", "default value")).to eq "Simple note"
|
133
|
+
expect(keychain.fetch("simple", "default value") { "block value" }).to eq "Simple note"
|
134
|
+
expect(keychain.fetch("simple") { "block value" }).to eq "Simple note"
|
54
135
|
|
55
|
-
keychain.fetch("simple").
|
136
|
+
expect(keychain.fetch("simple")).to eq "Simple note"
|
56
137
|
end
|
57
138
|
|
58
139
|
specify "when key does not exist" do
|
59
|
-
keychain.fetch("missing", nil).
|
60
|
-
keychain.fetch("missing", "default value").
|
61
|
-
keychain.fetch("missing", "default value") { "block value" }.
|
62
|
-
keychain.fetch("missing") { "block value" }.
|
140
|
+
expect(keychain.fetch("missing", nil)).to eq nil
|
141
|
+
expect(keychain.fetch("missing", "default value")).to eq "default value"
|
142
|
+
expect(keychain.fetch("missing", "default value") { "block value" }).to eq "block value"
|
143
|
+
expect(keychain.fetch("missing") { "block value" }).to eq "block value"
|
63
144
|
|
64
145
|
expect { keychain.fetch("missing") }.to raise_error(KeyError)
|
65
146
|
end
|
@@ -68,52 +149,43 @@ describe Mellon::Keychain do
|
|
68
149
|
|
69
150
|
describe "#[key]" do
|
70
151
|
it "reads simple entries" do
|
71
|
-
keychain["simple"].
|
152
|
+
expect(keychain["simple"]).to eq "Simple note"
|
72
153
|
end
|
73
154
|
|
74
155
|
it "reads encoded entries" do
|
75
|
-
keychain["encoded"].
|
156
|
+
expect(keychain["encoded"]).to eq "Encoded\nnote"
|
76
157
|
end
|
77
158
|
|
78
159
|
it "reads plist entries" do
|
79
|
-
keychain["plist"].
|
160
|
+
expect(keychain["plist"]).to eq "Plist note."
|
80
161
|
end
|
81
162
|
|
82
163
|
it "reads empty entries" do
|
83
|
-
keychain["empty"].
|
164
|
+
expect(keychain["empty"]).to eq ""
|
84
165
|
end
|
85
166
|
|
86
167
|
it "returns nil when there is no entry with the given name" do
|
87
|
-
keychain["nonexisting note"].
|
88
|
-
end
|
89
|
-
|
90
|
-
it "raises an error when command exits for a reason other than key missing" do
|
91
|
-
error = Mellon::CommandError.new(<<-ERROR)
|
92
|
-
security find-generic-password -g -l unreadable /Users/dev/Projects/mellon/spec/temporary_keychain.keychain
|
93
|
-
security: SecKeychainItemCopyAccess: In dark wake, no UI possible
|
94
|
-
ERROR
|
95
|
-
Mellon::ShellUtils.should_receive(:sh).and_raise(error)
|
96
|
-
expect { keychain["simple"] }.to raise_error(Mellon::CommandError, /dark wake/)
|
168
|
+
expect(keychain["nonexisting note"]).to be_nil
|
97
169
|
end
|
98
170
|
end
|
99
171
|
|
100
172
|
describe "#[]=" do
|
101
173
|
it "can create a new note" do
|
102
|
-
keychain["new note"].
|
174
|
+
expect(keychain["new note"]).to be_nil
|
103
175
|
keychain["new note"] = "This is new data"
|
104
|
-
keychain["new note"].
|
176
|
+
expect(keychain["new note"]).to eq "This is new data"
|
105
177
|
end
|
106
178
|
|
107
179
|
it "can write data to an existing note" do
|
108
|
-
keychain["existing"].
|
180
|
+
expect(keychain["existing"]).to eq "Existing note."
|
109
181
|
keychain["existing"] = "This is new"
|
110
|
-
keychain["existing"].
|
182
|
+
expect(keychain["existing"]).to eq "This is new"
|
111
183
|
end
|
112
184
|
|
113
185
|
it "can delete an existing note" do
|
114
|
-
keychain["doomed"].
|
186
|
+
expect(keychain["doomed"]).to_not be_nil
|
115
187
|
keychain["doomed"] = nil
|
116
|
-
keychain["doomed"].
|
188
|
+
expect(keychain["doomed"]).to be_nil
|
117
189
|
end
|
118
190
|
end
|
119
191
|
end
|
data/spec/mellon/store_spec.rb
CHANGED
@@ -9,75 +9,85 @@ describe Mellon::Store do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
it "finds and uses the keychain containing the project name by default" do
|
12
|
-
Mellon::Keychain.
|
13
|
-
Mellon::Store.new(project_name).keychain.
|
12
|
+
expect(Mellon::Keychain).to receive(:search).with(project_name).and_return(keychain)
|
13
|
+
expect(Mellon::Store.new(project_name).keychain).to eq keychain
|
14
14
|
end
|
15
15
|
|
16
16
|
it "uses the default keychain is no keychain contains the project name" do
|
17
17
|
keychain = double
|
18
|
-
Mellon::Keychain.
|
19
|
-
Mellon::Keychain.
|
20
|
-
Mellon::Store.new(project_name).keychain.
|
18
|
+
expect(Mellon::Keychain).to receive(:search).with(project_name).and_return(nil)
|
19
|
+
expect(Mellon::Keychain).to receive(:default).and_return(keychain)
|
20
|
+
expect(Mellon::Store.new(project_name).keychain).to eq keychain
|
21
21
|
end
|
22
22
|
|
23
23
|
it "accepts specifying the keychain by name" do
|
24
24
|
keychain = double
|
25
|
-
Mellon::Keychain.
|
25
|
+
expect(Mellon::Keychain).to receive(:find).with("projects").and_return(keychain)
|
26
26
|
store = Mellon::Store.new(project_name, keychain: "projects")
|
27
|
-
store.keychain.
|
27
|
+
expect(store.keychain).to eq keychain
|
28
28
|
end
|
29
29
|
|
30
30
|
it "accepts specifying the keychain object" do
|
31
31
|
store = Mellon::Store.new(project_name, keychain: keychain)
|
32
|
-
store.keychain.
|
32
|
+
expect(store.keychain).to eq keychain
|
33
33
|
end
|
34
34
|
|
35
35
|
it "allows setting the serializer" do
|
36
36
|
require "json"
|
37
37
|
store = Mellon::Store.new("json store", keychain: keychain, serializer: JSON)
|
38
|
-
store["some value"].
|
38
|
+
expect(store["some value"]).to eq "This is some json value"
|
39
39
|
store["some value"] = "New value"
|
40
|
-
store["some value"].
|
40
|
+
expect(store["some value"]).to eq "New value"
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
44
|
describe "#[]" do
|
45
45
|
it "returns the value for key inside the store" do
|
46
|
-
store["some value"].
|
46
|
+
expect(store["some value"]).to eq "This is some yaml value"
|
47
47
|
end
|
48
48
|
|
49
49
|
it "returns nil if store entry does not exist" do
|
50
50
|
store = Mellon::Store.new("missing project", keychain: keychain)
|
51
|
-
store["some value"].
|
51
|
+
expect(store["some value"]).to be_nil
|
52
52
|
end
|
53
53
|
|
54
54
|
it "returns nil if keychain item is empty" do
|
55
55
|
store = Mellon::Store.new("empty", keychain: keychain)
|
56
|
-
store["some value"].
|
56
|
+
expect(store["some value"]).to be_nil
|
57
57
|
store["some value"] = "New value"
|
58
|
-
store["some value"].
|
58
|
+
expect(store["some value"]).to eq "New value"
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
62
|
describe "#[]=" do
|
63
63
|
it "assigns an existing value for key inside the store" do
|
64
|
-
store["some value"].
|
64
|
+
expect(store["some value"]).to eq "This is some yaml value"
|
65
65
|
store["some value"] = "This is a new value"
|
66
|
-
store["some value"].
|
66
|
+
expect(store["some value"]).to eq "This is a new value"
|
67
67
|
end
|
68
68
|
|
69
69
|
it "creates the store entry if it does not exist" do
|
70
70
|
store = Mellon::Store.new("missing project", keychain: keychain)
|
71
|
-
store["some value"].
|
71
|
+
expect(store["some value"]).to be_nil
|
72
72
|
store["some value"] = "That value"
|
73
|
-
store["some value"].
|
73
|
+
expect(store["some value"]).to eq "That value"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#to_h" do
|
78
|
+
it "returns a hash with all keys contained" do
|
79
|
+
store["new value"] = "This is new value"
|
80
|
+
expect(store.to_h).to eq({
|
81
|
+
"some value" => "This is some yaml value",
|
82
|
+
"new value" => "This is new value"
|
83
|
+
})
|
74
84
|
end
|
75
85
|
end
|
76
86
|
|
77
87
|
specify "#fetch" do
|
78
|
-
store.fetch("some value").
|
79
|
-
store.fetch("missing value", "default").
|
80
|
-
store.fetch("missing value") { "default" }.
|
88
|
+
expect(store.fetch("some value")).to eq "This is some yaml value"
|
89
|
+
expect(store.fetch("missing value", "default")).to eq "default"
|
90
|
+
expect(store.fetch("missing value") { "default" }).to eq "default"
|
81
91
|
|
82
92
|
expect { store.fetch("missing value") }.to raise_error(KeyError)
|
83
93
|
end
|
data/spec/mellon_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -6,14 +6,6 @@ keychain_path = File.expand_path("./temporary_keychain.keychain", __dir__)
|
|
6
6
|
original_keychain_path = File.expand_path("./keychain.keychain", __dir__)
|
7
7
|
|
8
8
|
RSpec.configure do |config|
|
9
|
-
config.expect_with :rspec do |c|
|
10
|
-
c.syntax = [:expect, :should]
|
11
|
-
end
|
12
|
-
|
13
|
-
config.mock_with :rspec do |c|
|
14
|
-
c.syntax = :should
|
15
|
-
end
|
16
|
-
|
17
9
|
config.around do |example|
|
18
10
|
FileUtils.cp(original_keychain_path, keychain_path)
|
19
11
|
example.run
|
@@ -23,4 +15,16 @@ RSpec.configure do |config|
|
|
23
15
|
define_method :keychain_path do
|
24
16
|
keychain_path
|
25
17
|
end
|
18
|
+
|
19
|
+
config.include(Module.new do
|
20
|
+
def stub_command(command, stdout: "", stderr: "", error: false)
|
21
|
+
status = if error
|
22
|
+
double("success?" => false)
|
23
|
+
else
|
24
|
+
double("success?" => true)
|
25
|
+
end
|
26
|
+
|
27
|
+
expect(Open3).to receive(:capture3).with(*command.split(" ")).and_return([stdout, stderr, status])
|
28
|
+
end
|
29
|
+
end)
|
26
30
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mellon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kim Burgestrand
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-01-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: plist
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: yard
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
description:
|
98
112
|
email:
|
99
113
|
- kim@burgestrand.se
|
@@ -113,7 +127,6 @@ files:
|
|
113
127
|
- elabs-logo.png
|
114
128
|
- lib/mellon.rb
|
115
129
|
- lib/mellon/keychain.rb
|
116
|
-
- lib/mellon/shell_utils.rb
|
117
130
|
- lib/mellon/store.rb
|
118
131
|
- lib/mellon/utils.rb
|
119
132
|
- lib/mellon/version.rb
|
@@ -143,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
156
|
version: '0'
|
144
157
|
requirements: []
|
145
158
|
rubyforge_project:
|
146
|
-
rubygems_version: 2.
|
159
|
+
rubygems_version: 2.4.5
|
147
160
|
signing_key:
|
148
161
|
specification_version: 4
|
149
162
|
summary: A command-line utility for managing secret application credentials via OSX
|
@@ -154,3 +167,4 @@ test_files:
|
|
154
167
|
- spec/mellon/store_spec.rb
|
155
168
|
- spec/mellon_spec.rb
|
156
169
|
- spec/spec_helper.rb
|
170
|
+
has_rdoc:
|
data/lib/mellon/shell_utils.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
require "open3"
|
2
|
-
require "shellwords"
|
3
|
-
|
4
|
-
module Mellon
|
5
|
-
module ShellUtils
|
6
|
-
module_function
|
7
|
-
|
8
|
-
def security(*command, &block)
|
9
|
-
sh("security", *command, &block)
|
10
|
-
end
|
11
|
-
|
12
|
-
def sh(*command)
|
13
|
-
$stderr.puts command.join(" ") if $VERBOSE
|
14
|
-
stdout, stderr, status = Open3.capture3(*command)
|
15
|
-
|
16
|
-
stdout.chomp!
|
17
|
-
stderr.chomp!
|
18
|
-
|
19
|
-
unless status.success?
|
20
|
-
error_string = Shellwords.join(command)
|
21
|
-
error_string << "\n"
|
22
|
-
|
23
|
-
stderr = "<no output>" if stderr.empty?
|
24
|
-
error_string << " " << stderr.chomp
|
25
|
-
|
26
|
-
raise CommandError, "[ERROR] #{error_string}"
|
27
|
-
end
|
28
|
-
|
29
|
-
if block_given?
|
30
|
-
yield [stdout, stderr]
|
31
|
-
else
|
32
|
-
stdout
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|