boxen 1.5.2 → 2.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.
- data/boxen.gemspec +2 -2
- data/lib/boxen/config.rb +9 -20
- data/lib/boxen/flags.rb +4 -4
- data/lib/boxen/hook/github_issue.rb +4 -1
- data/lib/boxen/keychain.rb +5 -11
- data/lib/boxen/preflight/creds.rb +63 -23
- data/script/Boxen +0 -0
- data/src/keychain-helper.c +9 -3
- data/test/boxen_config_test.rb +2 -10
- data/test/boxen_flags_test.rb +8 -8
- data/test/boxen_hook_github_issue_test.rb +7 -0
- data/test/boxen_keychain_test.rb +6 -10
- data/test/boxen_preflight_creds_test.rb +41 -0
- metadata +7 -4
data/boxen.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |gem|
|
4
4
|
gem.name = "boxen"
|
5
|
-
gem.version = "
|
5
|
+
gem.version = "2.0.0"
|
6
6
|
gem.authors = ["John Barnette", "Will Farrington"]
|
7
7
|
gem.email = ["jbarnette@github.com", "wfarr@github.com"]
|
8
8
|
gem.description = "Manage Mac development boxes with love (and Puppet)."
|
@@ -18,7 +18,7 @@ Gem::Specification.new do |gem|
|
|
18
18
|
gem.add_dependency "highline", "~> 1.6"
|
19
19
|
gem.add_dependency "json_pure", [">= 1.7.7", "< 2.0"]
|
20
20
|
gem.add_dependency "librarian-puppet", "~> 0.9.9"
|
21
|
-
gem.add_dependency "octokit", "~>
|
21
|
+
gem.add_dependency "octokit", "~> 2.0.0"
|
22
22
|
gem.add_dependency "puppet", "~> 3.0"
|
23
23
|
|
24
24
|
gem.add_development_dependency "minitest", "4.4.0" # pinned for mocha
|
data/lib/boxen/config.rb
CHANGED
@@ -26,7 +26,6 @@ module Boxen
|
|
26
26
|
end
|
27
27
|
|
28
28
|
keychain = Boxen::Keychain.new config.user
|
29
|
-
config.password = keychain.password
|
30
29
|
config.token = keychain.token
|
31
30
|
|
32
31
|
if config.enterprise?
|
@@ -69,7 +68,6 @@ module Boxen
|
|
69
68
|
end
|
70
69
|
|
71
70
|
keychain = Boxen::Keychain.new config.user
|
72
|
-
keychain.password = config.password
|
73
71
|
keychain.token = config.token
|
74
72
|
|
75
73
|
config
|
@@ -85,10 +83,10 @@ module Boxen
|
|
85
83
|
end
|
86
84
|
|
87
85
|
# Create an API instance using the current user creds. A new
|
88
|
-
# instance is created any time `
|
86
|
+
# instance is created any time `token` changes.
|
89
87
|
|
90
88
|
def api
|
91
|
-
@api ||= Octokit::Client.new :login =>
|
89
|
+
@api ||= Octokit::Client.new :login => token, :password => 'x-oauth-basic'
|
92
90
|
end
|
93
91
|
|
94
92
|
# Spew a bunch of debug logging? Default is `false`.
|
@@ -139,26 +137,12 @@ module Boxen
|
|
139
137
|
|
140
138
|
# A GitHub user login. Default is `nil`.
|
141
139
|
|
142
|
-
|
143
|
-
|
144
|
-
def login=(login)
|
145
|
-
@api = nil
|
146
|
-
@login = login
|
147
|
-
end
|
140
|
+
attr_accessor :login
|
148
141
|
|
149
142
|
# A GitHub user's profile name.
|
150
143
|
|
151
144
|
attr_accessor :name
|
152
145
|
|
153
|
-
# A GitHub user password. Default is `nil`.
|
154
|
-
|
155
|
-
attr_reader :password
|
156
|
-
|
157
|
-
def password=(password)
|
158
|
-
@api = nil
|
159
|
-
@password = password
|
160
|
-
end
|
161
|
-
|
162
146
|
# Just go through the motions? Default is `false`.
|
163
147
|
|
164
148
|
def pretend?
|
@@ -294,7 +278,12 @@ module Boxen
|
|
294
278
|
|
295
279
|
# A GitHub OAuth token. Default is `nil`.
|
296
280
|
|
297
|
-
|
281
|
+
attr_reader :token
|
282
|
+
|
283
|
+
def token=(token)
|
284
|
+
@token = token
|
285
|
+
@api = nil
|
286
|
+
end
|
298
287
|
|
299
288
|
# A local user login. Default is the `USER` environment variable.
|
300
289
|
|
data/lib/boxen/flags.rb
CHANGED
@@ -12,7 +12,7 @@ module Boxen
|
|
12
12
|
attr_reader :homedir
|
13
13
|
attr_reader :logfile
|
14
14
|
attr_reader :login
|
15
|
-
attr_reader :
|
15
|
+
attr_reader :token
|
16
16
|
attr_reader :srcdir
|
17
17
|
attr_reader :user
|
18
18
|
|
@@ -118,8 +118,8 @@ module Boxen
|
|
118
118
|
@stealth = true
|
119
119
|
end
|
120
120
|
|
121
|
-
o.on "--
|
122
|
-
@
|
121
|
+
o.on "--token TOKEN", "Your GitHub OAuth token." do |token|
|
122
|
+
@token = token
|
123
123
|
end
|
124
124
|
|
125
125
|
o.on "--profile", "Profile the Puppet run." do
|
@@ -158,7 +158,7 @@ module Boxen
|
|
158
158
|
config.homedir = homedir if homedir
|
159
159
|
config.logfile = logfile if logfile
|
160
160
|
config.login = login if login
|
161
|
-
config.
|
161
|
+
config.token = token if token
|
162
162
|
config.pretend = pretend?
|
163
163
|
config.profile = profile?
|
164
164
|
config.future_parser = future_parser?
|
@@ -4,7 +4,10 @@ module Boxen
|
|
4
4
|
class Hook
|
5
5
|
class GitHubIssue < Hook
|
6
6
|
def perform?
|
7
|
-
enabled? &&
|
7
|
+
enabled? &&
|
8
|
+
!config.stealth? && !config.pretend? &&
|
9
|
+
!config.login.to_s.empty? &&
|
10
|
+
checkout.master?
|
8
11
|
end
|
9
12
|
|
10
13
|
def call
|
data/lib/boxen/keychain.rb
CHANGED
@@ -18,14 +18,8 @@ module Boxen
|
|
18
18
|
|
19
19
|
def initialize(login)
|
20
20
|
@login = login
|
21
|
-
|
22
|
-
|
23
|
-
def password
|
24
|
-
get PASSWORD_SERVICE
|
25
|
-
end
|
26
|
-
|
27
|
-
def password=(password)
|
28
|
-
set PASSWORD_SERVICE, password
|
21
|
+
# Clear the password. We're storing tokens now.
|
22
|
+
set PASSWORD_SERVICE, ""
|
29
23
|
end
|
30
24
|
|
31
25
|
def token
|
@@ -47,14 +41,14 @@ module Boxen
|
|
47
41
|
$?.success? ? result : nil
|
48
42
|
end
|
49
43
|
|
50
|
-
def set(service,
|
51
|
-
cmd = shellescape(HELPER, service, login,
|
44
|
+
def set(service, token)
|
45
|
+
cmd = shellescape(HELPER, service, login, token)
|
52
46
|
|
53
47
|
unless system *cmd
|
54
48
|
raise Boxen::Error, "Can't save #{service} in the keychain."
|
55
49
|
end
|
56
50
|
|
57
|
-
|
51
|
+
token
|
58
52
|
end
|
59
53
|
|
60
54
|
def shellescape(*args)
|
@@ -8,21 +8,62 @@ require "octokit"
|
|
8
8
|
HighLine.track_eof = false
|
9
9
|
|
10
10
|
class Boxen::Preflight::Creds < Boxen::Preflight
|
11
|
-
|
12
|
-
|
13
|
-
end
|
11
|
+
attr :otp
|
12
|
+
attr :password
|
14
13
|
|
15
14
|
def ok?
|
16
|
-
|
15
|
+
if config.token && config.api.user
|
16
|
+
# There was a period of time when login wasn't geting set on first run.
|
17
|
+
# This should correct that.
|
18
|
+
config.login = config.api.user.login
|
19
|
+
true
|
20
|
+
end
|
21
|
+
rescue
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def tmp_api
|
26
|
+
@tmp_api ||= Octokit::Client.new :login => config.login, :password => password, :auto_paginate => true
|
17
27
|
end
|
18
28
|
|
19
|
-
def
|
20
|
-
|
29
|
+
def headers
|
30
|
+
otp.nil? ? {} : {"X-GitHub-OTP" => otp}
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_otp
|
34
|
+
console = HighLine.new
|
35
|
+
|
36
|
+
# junk API call to send OTP until we implement PUT
|
37
|
+
tmp_api.create_authorization rescue nil
|
21
38
|
|
22
|
-
|
23
|
-
|
39
|
+
@otp = console.ask "One time password (via SMS or device):" do |q|
|
40
|
+
q.echo = '*'
|
41
|
+
end
|
42
|
+
end
|
24
43
|
|
25
|
-
|
44
|
+
# Attempt to use the username+password to get a list of the user's OAuth
|
45
|
+
# authorizations from the API. If it fails because of 2FA, ask the user for
|
46
|
+
# her OTP and try again.
|
47
|
+
#
|
48
|
+
# Returns a list of authorizations
|
49
|
+
def get_tokens
|
50
|
+
begin
|
51
|
+
tmp_api.authorizations(:headers => headers)
|
52
|
+
rescue Octokit::Unauthorized => e
|
53
|
+
puts
|
54
|
+
if e.message =~ /OTP/
|
55
|
+
if otp.nil?
|
56
|
+
warn "It looks like you have two-factor auth enabled."
|
57
|
+
else
|
58
|
+
warn "That one time password didn't work. Let's try again."
|
59
|
+
end
|
60
|
+
get_otp
|
61
|
+
get_tokens
|
62
|
+
else
|
63
|
+
abort "Sorry, I can't auth you on GitHub.",
|
64
|
+
"Please check your credentials and teams and give it another try."
|
65
|
+
end
|
66
|
+
end
|
26
67
|
end
|
27
68
|
|
28
69
|
def run
|
@@ -34,27 +75,26 @@ class Boxen::Preflight::Creds < Boxen::Preflight
|
|
34
75
|
q.default = config.login || config.user
|
35
76
|
q.validate = /\A[^@]+\Z/
|
36
77
|
end
|
37
|
-
|
38
|
-
|
78
|
+
|
79
|
+
@password = console.ask "GitHub password: " do |q|
|
39
80
|
q.echo = "*"
|
40
81
|
end
|
41
82
|
|
42
|
-
|
43
|
-
puts # i <3 vertical whitespace
|
83
|
+
tokens = get_tokens
|
44
84
|
|
45
|
-
|
46
|
-
|
85
|
+
unless auth = tokens.detect { |a| a.note == "Boxen" }
|
86
|
+
auth = tmp_api.create_authorization \
|
87
|
+
:note => "Boxen",
|
88
|
+
:scopes => %w(repo user),
|
89
|
+
:headers => headers
|
47
90
|
end
|
48
91
|
|
49
|
-
|
92
|
+
config.token = auth.token
|
50
93
|
|
51
|
-
unless
|
52
|
-
|
53
|
-
|
94
|
+
unless ok?
|
95
|
+
puts
|
96
|
+
abort "Something went terribly wrong.",
|
97
|
+
"I was able to get your OAuth token, but was unable to use it."
|
54
98
|
end
|
55
|
-
|
56
|
-
# Reset the token for later.
|
57
|
-
|
58
|
-
config.token = auth.token
|
59
99
|
end
|
60
100
|
end
|
data/script/Boxen
CHANGED
Binary file
|
data/src/keychain-helper.c
CHANGED
@@ -20,7 +20,7 @@ int key_exists_p(
|
|
20
20
|
NULL, strlen(service), service, strlen(login), login, &len, &buf, item
|
21
21
|
);
|
22
22
|
|
23
|
-
if (ret ==
|
23
|
+
if (ret == errSecSuccess) {
|
24
24
|
return 0;
|
25
25
|
} else {
|
26
26
|
if (ret != errSecItemNotFound) {
|
@@ -45,7 +45,7 @@ int main(int argc, char **argv) {
|
|
45
45
|
UInt32 len;
|
46
46
|
SecKeychainItemRef item;
|
47
47
|
|
48
|
-
if (password != NULL) {
|
48
|
+
if (password != NULL && strlen(password) != 0) {
|
49
49
|
if (key_exists_p(service, login, &item) == 0) {
|
50
50
|
SecKeychainItemDelete(item);
|
51
51
|
}
|
@@ -59,7 +59,13 @@ int main(int argc, char **argv) {
|
|
59
59
|
REPORT_KEYCHAIN_ERROR(create_key);
|
60
60
|
return 1;
|
61
61
|
}
|
62
|
-
|
62
|
+
} else if (password != NULL && strlen(password) == 0) {
|
63
|
+
if (key_exists_p(service, login, &item) == 0) {
|
64
|
+
OSStatus ret = SecKeychainItemDelete(item);
|
65
|
+
if (ret != errSecSuccess) {
|
66
|
+
REPORT_KEYCHAIN_ERROR(ret);
|
67
|
+
}
|
68
|
+
}
|
63
69
|
} else {
|
64
70
|
OSStatus find_key = SecKeychainFindGenericPassword(
|
65
71
|
NULL, strlen(service), service, strlen(login), login, &len, &buf, &item);
|
data/test/boxen_config_test.rb
CHANGED
@@ -96,13 +96,6 @@ class BoxenConfigTest < Boxen::Test
|
|
96
96
|
assert_equal "foo", @config.name
|
97
97
|
end
|
98
98
|
|
99
|
-
def test_password
|
100
|
-
assert_nil @config.password
|
101
|
-
|
102
|
-
@config.password = "foo"
|
103
|
-
assert_equal "foo", @config.password
|
104
|
-
end
|
105
|
-
|
106
99
|
def test_pretend?
|
107
100
|
refute @config.pretend?
|
108
101
|
|
@@ -346,11 +339,10 @@ class BoxenConfigTest < Boxen::Test
|
|
346
339
|
end
|
347
340
|
|
348
341
|
def test_api
|
349
|
-
@config.
|
350
|
-
@config.password = pass = "s3kr!7"
|
342
|
+
@config.token = token = "s3kr!7"
|
351
343
|
|
352
344
|
api = Object.new
|
353
|
-
Octokit::Client.expects(:new).with(:login =>
|
345
|
+
Octokit::Client.expects(:new).with(:login => token, :password => 'x-oauth-basic').once.returns(api)
|
354
346
|
|
355
347
|
assert_equal api, @config.api
|
356
348
|
assert_equal api, @config.api # This extra call plus the `once` on the expectation is for the ivar cache.
|
data/test/boxen_flags_test.rb
CHANGED
@@ -12,7 +12,7 @@ class BoxenFlagsTest < Boxen::Test
|
|
12
12
|
expects(:homedir=).with "homedir"
|
13
13
|
expects(:logfile=).with "logfile"
|
14
14
|
expects(:login=).with "login"
|
15
|
-
expects(:
|
15
|
+
expects(:token=).with "token"
|
16
16
|
expects(:pretend=).with true
|
17
17
|
expects(:profile=).with true
|
18
18
|
expects(:future_parser=).with true
|
@@ -26,10 +26,10 @@ class BoxenFlagsTest < Boxen::Test
|
|
26
26
|
# Do our best to frob every switch.
|
27
27
|
|
28
28
|
flags = Boxen::Flags.new "--debug", "--help", "--login", "login",
|
29
|
-
"--no-fde", "--no-pull", "--no-issue", "--noop",
|
29
|
+
"--no-fde", "--no-pull", "--no-issue", "--noop",
|
30
30
|
"--pretend", "--profile", "--future-parser", "--report", "--projects",
|
31
31
|
"--user", "user", "--homedir", "homedir", "--srcdir", "srcdir",
|
32
|
-
"--logfile", "logfile"
|
32
|
+
"--logfile", "logfile", "--token", "token"
|
33
33
|
|
34
34
|
assert_same config, flags.apply(config)
|
35
35
|
end
|
@@ -141,14 +141,14 @@ class BoxenFlagsTest < Boxen::Test
|
|
141
141
|
assert_equal %w(foo), config.args
|
142
142
|
end
|
143
143
|
|
144
|
-
def
|
145
|
-
assert_nil flags.
|
146
|
-
assert_equal "foo", flags("--
|
144
|
+
def test_token
|
145
|
+
assert_nil flags.token
|
146
|
+
assert_equal "foo", flags("--token", "foo").token
|
147
147
|
end
|
148
148
|
|
149
|
-
def
|
149
|
+
def test_token_missing_value
|
150
150
|
ex = assert_raises Boxen::Error do
|
151
|
-
flags "--
|
151
|
+
flags "--token"
|
152
152
|
end
|
153
153
|
|
154
154
|
assert_match "missing argument", ex.message
|
@@ -31,6 +31,7 @@ class BoxenHookGitHubIssueTest < Boxen::Test
|
|
31
31
|
@config.stubs(:stealth?).returns(true)
|
32
32
|
@config.stubs(:pretend?).returns(true)
|
33
33
|
@checkout.stubs(:master?).returns(false)
|
34
|
+
@config.stubs(:login).returns(nil)
|
34
35
|
|
35
36
|
refute @hook.perform?
|
36
37
|
|
@@ -44,6 +45,12 @@ class BoxenHookGitHubIssueTest < Boxen::Test
|
|
44
45
|
refute @hook.perform?
|
45
46
|
|
46
47
|
@checkout.stubs(:master?).returns(true)
|
48
|
+
refute @hook.perform?
|
49
|
+
|
50
|
+
@config.stubs(:login).returns('')
|
51
|
+
refute @hook.perform?
|
52
|
+
|
53
|
+
@config.stubs(:login).returns('somelogin')
|
47
54
|
assert @hook.perform?
|
48
55
|
end
|
49
56
|
|
data/test/boxen_keychain_test.rb
CHANGED
@@ -3,26 +3,22 @@ require "boxen/keychain"
|
|
3
3
|
|
4
4
|
class BoxenKeychainTest < Boxen::Test
|
5
5
|
def setup
|
6
|
-
@keychain = Boxen::Keychain.new('test')
|
6
|
+
@keychain = Boxen::Keychain.new('test') if osx?
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
10
|
-
|
11
|
-
assert_equal 'foobar', @keychain.password
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_set_password
|
15
|
-
@keychain.expects(:set).with('GitHub Password', 'foobar').returns('foobar')
|
16
|
-
assert_equal 'foobar', @keychain.password=('foobar')
|
9
|
+
def osx?
|
10
|
+
RUBY_PLATFORM.downcase.include?("darwin")
|
17
11
|
end
|
18
12
|
|
19
13
|
def test_get_token
|
14
|
+
return skip("Keychain helper is OSX only") unless osx?
|
20
15
|
@keychain.expects(:get).with('GitHub API Token').returns('foobar')
|
21
16
|
assert_equal 'foobar', @keychain.token
|
22
17
|
end
|
23
18
|
|
24
19
|
def test_set_token
|
20
|
+
return skip("Keychain helper is OSX only") unless osx?
|
25
21
|
@keychain.expects(:set).with('GitHub API Token', 'foobar').returns('foobar')
|
26
22
|
assert_equal 'foobar', @keychain.token=('foobar')
|
27
23
|
end
|
28
|
-
end
|
24
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'boxen/test'
|
2
|
+
require 'boxen/preflight/creds'
|
3
|
+
|
4
|
+
class BoxenPreflightCredsTest < Boxen::Test
|
5
|
+
def setup
|
6
|
+
@config = Boxen::Config.new do |c|
|
7
|
+
c.user = 'mojombo'
|
8
|
+
c.token = 'sekr3t!'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_basic
|
13
|
+
preflight = Boxen::Preflight::Creds.new @config
|
14
|
+
|
15
|
+
error = Octokit::Unauthorized.new
|
16
|
+
@config.api.stubs(:user).raises(error)
|
17
|
+
|
18
|
+
refute preflight.ok?
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_basic_with_otp_challenge
|
22
|
+
preflight = Boxen::Preflight::Creds.new @config
|
23
|
+
|
24
|
+
blank_opt = {:headers => {}}
|
25
|
+
good_otp = {:headers => {"X-GitHub-OTP" => "123456"}}
|
26
|
+
|
27
|
+
error = Octokit::Unauthorized.new
|
28
|
+
error.stubs(:message).returns("OTP")
|
29
|
+
|
30
|
+
preflight.tmp_api.expects(:authorizations).with(blank_opt).raises(error)
|
31
|
+
preflight.tmp_api.expects(:authorizations).with(good_otp).returns([])
|
32
|
+
preflight.tmp_api.expects(:create_authorization).raises(error)
|
33
|
+
|
34
|
+
preflight.expects(:warn)
|
35
|
+
HighLine.any_instance.expects(:ask).returns("123456")
|
36
|
+
|
37
|
+
preflight.get_tokens
|
38
|
+
assert_equal "123456", preflight.otp
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: boxen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-
|
13
|
+
date: 2013-09-06 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: ansi
|
@@ -105,7 +105,7 @@ dependencies:
|
|
105
105
|
requirements:
|
106
106
|
- - ~>
|
107
107
|
- !ruby/object:Gem::Version
|
108
|
-
version:
|
108
|
+
version: 2.0.0
|
109
109
|
type: :runtime
|
110
110
|
prerelease: false
|
111
111
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -113,7 +113,7 @@ dependencies:
|
|
113
113
|
requirements:
|
114
114
|
- - ~>
|
115
115
|
- !ruby/object:Gem::Version
|
116
|
-
version:
|
116
|
+
version: 2.0.0
|
117
117
|
- !ruby/object:Gem::Dependency
|
118
118
|
name: puppet
|
119
119
|
requirement: !ruby/object:Gem::Requirement
|
@@ -223,6 +223,7 @@ files:
|
|
223
223
|
- test/boxen_keychain_test.rb
|
224
224
|
- test/boxen_postflight_active_test.rb
|
225
225
|
- test/boxen_postflight_env_test.rb
|
226
|
+
- test/boxen_preflight_creds_test.rb
|
226
227
|
- test/boxen_preflight_etc_my_cnf_test.rb
|
227
228
|
- test/boxen_preflight_homebrew_test.rb
|
228
229
|
- test/boxen_preflight_rvm_test.rb
|
@@ -271,6 +272,7 @@ test_files:
|
|
271
272
|
- test/boxen_keychain_test.rb
|
272
273
|
- test/boxen_postflight_active_test.rb
|
273
274
|
- test/boxen_postflight_env_test.rb
|
275
|
+
- test/boxen_preflight_creds_test.rb
|
274
276
|
- test/boxen_preflight_etc_my_cnf_test.rb
|
275
277
|
- test/boxen_preflight_homebrew_test.rb
|
276
278
|
- test/boxen_preflight_rvm_test.rb
|
@@ -282,3 +284,4 @@ test_files:
|
|
282
284
|
- test/fixtures/repo/modules/projects/manifests/first-project.pp
|
283
285
|
- test/fixtures/repo/modules/projects/manifests/second-project.pp
|
284
286
|
- test/system_timer.rb
|
287
|
+
has_rdoc:
|