htauth 2.2.0 → 3.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.
- checksums.yaml +4 -4
- data/HISTORY.md +21 -1
- data/Manifest.txt +5 -27
- data/README.md +51 -31
- data/exe/htdigest-ruby +14 -0
- data/exe/htpasswd-ruby +14 -0
- data/htauth.gemspec +33 -0
- data/lib/htauth/algorithm.rb +42 -29
- data/lib/htauth/argon2.rb +86 -0
- data/lib/htauth/bcrypt.rb +17 -11
- data/lib/htauth/cli/digest.rb +42 -49
- data/lib/htauth/cli/passwd.rb +127 -114
- data/lib/htauth/cli.rb +5 -4
- data/lib/htauth/console.rb +9 -6
- data/lib/htauth/crypt.rb +11 -9
- data/lib/htauth/descendant_tracker.rb +11 -9
- data/lib/htauth/digest_entry.rb +22 -19
- data/lib/htauth/digest_file.rb +25 -18
- data/lib/htauth/entry.rb +3 -1
- data/lib/htauth/error.rb +6 -5
- data/lib/htauth/file.rb +35 -39
- data/lib/htauth/md5.rb +35 -34
- data/lib/htauth/passwd_entry.rb +29 -38
- data/lib/htauth/passwd_file.rb +32 -27
- data/lib/htauth/plaintext.rb +7 -5
- data/lib/htauth/sha1.rb +9 -7
- data/lib/htauth/version.rb +3 -1
- data/lib/htauth.rb +29 -28
- metadata +24 -113
- data/Rakefile +0 -27
- data/bin/htdigest-ruby +0 -12
- data/bin/htpasswd-ruby +0 -12
- data/spec/algorithm_spec.rb +0 -8
- data/spec/bcrypt_spec.rb +0 -33
- data/spec/cli/digest_spec.rb +0 -149
- data/spec/cli/passwd_spec.rb +0 -330
- data/spec/crypt_spec.rb +0 -12
- data/spec/digest_entry_spec.rb +0 -60
- data/spec/digest_file_spec.rb +0 -65
- data/spec/md5_spec.rb +0 -13
- data/spec/passwd_entry_spec.rb +0 -159
- data/spec/passwd_file_spec.rb +0 -84
- data/spec/plaintext_spec.rb +0 -11
- data/spec/sha1_spec.rb +0 -11
- data/spec/spec_helper.rb +0 -28
- data/spec/test.add.digest +0 -3
- data/spec/test.add.passwd +0 -3
- data/spec/test.delete.digest +0 -1
- data/spec/test.delete.passwd +0 -1
- data/spec/test.original.digest +0 -2
- data/spec/test.original.passwd +0 -2
- data/spec/test.update.digest +0 -2
- data/spec/test.update.passwd +0 -2
- data/tasks/default.rake +0 -242
- data/tasks/this.rb +0 -208
- /data/{LICENSE → LICENSE.txt} +0 -0
data/spec/passwd_entry_spec.rb
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require 'htauth/passwd_entry'
|
|
3
|
-
|
|
4
|
-
describe HTAuth::PasswdEntry do
|
|
5
|
-
before(:each) do
|
|
6
|
-
@alice = HTAuth::PasswdEntry.new("alice", "a secret", "crypt", { :salt => "mD" })
|
|
7
|
-
@bob = HTAuth::PasswdEntry.new("bob", "b secret", "crypt", { :salt => "b8"})
|
|
8
|
-
@salt = "lo1tk/.."
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
it "initializes with a user and realm" do
|
|
12
|
-
_(@alice.user).must_equal "alice"
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
it "has the correct crypt password" do
|
|
16
|
-
@alice.password = "a secret"
|
|
17
|
-
_(@alice.digest).must_equal "mDwdZuXalQ5zk"
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
it "encrypts correctly for md5" do
|
|
21
|
-
bob = HTAuth::PasswdEntry.new("bob", "b secret", "md5", { :salt => @salt })
|
|
22
|
-
_(bob.digest).must_equal "$apr1$lo1tk/..$CarApvZPee0F6Wj1U0GxZ1"
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
it "encrypts correctly for sha1" do
|
|
26
|
-
bob = HTAuth::PasswdEntry.new("bob", "b secret", "sha1", { :salt => @salt })
|
|
27
|
-
_(bob.digest).must_equal "{SHA}b/tjGXbX80MEKVnF200S43ca4hY="
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
it "encrypts correctly for plaintext" do
|
|
31
|
-
bob = HTAuth::PasswdEntry.new("bob", "b secret", "plaintext", { :salt => @salt })
|
|
32
|
-
_(bob.digest).must_equal "b secret"
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it "encrypts with crypt as a default, when parsed from crypt()'d line" do
|
|
36
|
-
bob2 = HTAuth::PasswdEntry.from_line(@bob.to_s)
|
|
37
|
-
_(bob2.algorithm).must_be_instance_of(HTAuth::Crypt)
|
|
38
|
-
bob2.password = "another secret"
|
|
39
|
-
_(bob2.algorithm).must_be_instance_of(HTAuth::Crypt)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
it "encrypts with crypt as a default, when parsed from plaintext line" do
|
|
43
|
-
p = HTAuth::PasswdEntry.new('paul', 'p secret', 'plaintext')
|
|
44
|
-
p2 = HTAuth::PasswdEntry.from_line(p.to_s)
|
|
45
|
-
_(p2.algorithm).must_be_instance_of(HTAuth::Plaintext)
|
|
46
|
-
p2.password = "another secret"
|
|
47
|
-
_(p2.algorithm).must_be_instance_of(HTAuth::Crypt)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
it "encrypts with md5 as default, when parsed from an md5 line" do
|
|
51
|
-
m = HTAuth::PasswdEntry.new("mary", "m secret", "md5")
|
|
52
|
-
m2 = HTAuth::PasswdEntry.from_line(m.to_s)
|
|
53
|
-
_(m2.algorithm).must_be_instance_of(HTAuth::Md5)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
it "encrypts with sha1 as default, when parsed from an sha1 line" do
|
|
57
|
-
s = HTAuth::PasswdEntry.new("steve", "s secret", "sha1")
|
|
58
|
-
s2 = HTAuth::PasswdEntry.from_line(s.to_s)
|
|
59
|
-
_(s2.algorithm).must_be_instance_of(HTAuth::Sha1)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
it "encrypts with bcrypt as default when parsed from a bcrypt line" do
|
|
63
|
-
b = HTAuth::PasswdEntry.new("brenda", "b secret", "bcrypt")
|
|
64
|
-
b2 = HTAuth::PasswdEntry.from_line(b.to_s)
|
|
65
|
-
_(b2.algorithm).must_be_instance_of(HTAuth::Bcrypt)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
it "determins the algorithm to be crypt when checking a password" do
|
|
69
|
-
bob2 = HTAuth::PasswdEntry.from_line(@bob.to_s)
|
|
70
|
-
_(bob2.algorithm).must_be_instance_of(HTAuth::Crypt)
|
|
71
|
-
_(bob2.authenticated?("b secret")).must_equal true
|
|
72
|
-
_(bob2.algorithm).must_be_instance_of(HTAuth::Crypt)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
it "determins the algorithm to be plain when checking a password" do
|
|
76
|
-
bob2 = HTAuth::PasswdEntry.from_line("bob:b secret")
|
|
77
|
-
_(bob2.algorithm).must_be_instance_of(HTAuth::Plaintext)
|
|
78
|
-
_(bob2.authenticated?("b secret")).must_equal true
|
|
79
|
-
_(bob2.algorithm).must_be_instance_of(HTAuth::Plaintext)
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
it "authenticates correctly against md5" do
|
|
83
|
-
m = HTAuth::PasswdEntry.new("mary", "m secret", "md5")
|
|
84
|
-
m2 = HTAuth::PasswdEntry.from_line(m.to_s)
|
|
85
|
-
_(m2.authenticated?("m secret")).must_equal true
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
it "authenticates correctly against sha1" do
|
|
89
|
-
s = HTAuth::PasswdEntry.new("steve", "s secret", "sha1")
|
|
90
|
-
s2 = HTAuth::PasswdEntry.from_line(s.to_s)
|
|
91
|
-
_(s2.authenticated?("s secret")).must_equal true
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
it "authenticates correctly against bcrypt" do
|
|
95
|
-
s = HTAuth::PasswdEntry.new("brenda", "b secret", "bcrypt")
|
|
96
|
-
s2 = HTAuth::PasswdEntry.from_line(s.to_s)
|
|
97
|
-
_(s2.authenticated?("b secret")).must_equal true
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
it "can update the cost of an entry after initialization before encoding password" do
|
|
101
|
-
s = HTAuth::PasswdEntry.new("brenda", "b secret", "bcrypt")
|
|
102
|
-
_(s.algorithm.cost).must_equal(::HTAuth::Bcrypt::DEFAULT_APACHE_COST)
|
|
103
|
-
|
|
104
|
-
s2 = HTAuth::PasswdEntry.from_line(s.to_s)
|
|
105
|
-
s2.algorithm_args = { :cost => 12 }
|
|
106
|
-
s2.password = "b secret" # forces recalculation
|
|
107
|
-
|
|
108
|
-
_(s2.algorithm.cost).must_equal(12)
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
it "raises an error if assinging an invalid algorithm" do
|
|
112
|
-
b = HTAuth::PasswdEntry.new("brenda", "b secret", "bcrypt")
|
|
113
|
-
_ { b.algorithm = 42 }.must_raise(HTAuth::InvalidAlgorithmError)
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
it "returns username for a key" do
|
|
117
|
-
_(@alice.key).must_equal "alice"
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
it "checks the password correctly" do
|
|
121
|
-
_(@bob.authenticated?("b secret")).must_equal true
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
it "formats correctly when put to a string" do
|
|
125
|
-
_(@bob.to_s).must_equal "bob:b8Ml4Jp9I0N8E"
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
it "parses an input line" do
|
|
129
|
-
@bob_new = HTAuth::PasswdEntry.from_line("bob:b8Ml4Jp9I0N8E")
|
|
130
|
-
_(@bob.user).must_equal @bob_new.user
|
|
131
|
-
_(@bob.digest).must_equal @bob_new.digest
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
it "knows if an input line is a possible entry and raises an exception" do
|
|
135
|
-
_ { HTAuth::PasswdEntry.is_entry!("#stuff") }.must_raise(HTAuth::InvalidPasswdEntry)
|
|
136
|
-
_ { HTAuth::PasswdEntry.is_entry!("this:that:other:stuff") }.must_raise(HTAuth::InvalidPasswdEntry)
|
|
137
|
-
_ { HTAuth::PasswdEntry.is_entry!("this:that:other") }.must_raise(HTAuth::InvalidPasswdEntry)
|
|
138
|
-
_ { HTAuth::PasswdEntry.is_entry!("this:that:0a90549e8ffb2dd62f98252a95d88xyz") }.must_raise(HTAuth::InvalidPasswdEntry)
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
it "knows if an input line is a possible entry and returns false" do
|
|
142
|
-
_(HTAuth::PasswdEntry.is_entry?("#stuff")).must_equal false
|
|
143
|
-
_(HTAuth::PasswdEntry.is_entry?("this:that:other:stuff")).must_equal false
|
|
144
|
-
_(HTAuth::PasswdEntry.is_entry?("this:that:other")).must_equal false
|
|
145
|
-
_(HTAuth::PasswdEntry.is_entry?("this:that:0a90549e8ffb2dd62f98252a95d88xyz")).must_equal false
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
it "knows if an input line is a possible entry and returns true" do
|
|
149
|
-
_(HTAuth::PasswdEntry.is_entry?("bob:irRm0g.SDfCyI")).must_equal true
|
|
150
|
-
_(HTAuth::PasswdEntry.is_entry?("bob:b secreat")).must_equal true
|
|
151
|
-
_(HTAuth::PasswdEntry.is_entry?("bob:{SHA}b/tjGXbX80MEKVnF200S43ca4hY=")).must_equal true
|
|
152
|
-
_(HTAuth::PasswdEntry.is_entry?("bob:$apr1$lo1tk/..$CarApvZPee0F6Wj1U0GxZ1")).must_equal true
|
|
153
|
-
_(HTAuth::PasswdEntry.is_entry?("bob:$2y$05$ts3k1r.t0Cne6j6DLt0/SepT5X4qthDFEdfqHBBMO5MhqzyMz34j2")).must_equal true
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
it "duplicates itself" do
|
|
157
|
-
_(@alice.dup.to_s).must_equal @alice.to_s
|
|
158
|
-
end
|
|
159
|
-
end
|
data/spec/passwd_file_spec.rb
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require 'htauth/passwd_file'
|
|
3
|
-
require 'tempfile'
|
|
4
|
-
|
|
5
|
-
describe HTAuth::PasswdFile do
|
|
6
|
-
|
|
7
|
-
before(:each) do
|
|
8
|
-
@tf = Tempfile.new("rpasswrd-passwd")
|
|
9
|
-
@tf.write(IO.read(PASSWD_ORIGINAL_TEST_FILE))
|
|
10
|
-
@tf.close
|
|
11
|
-
@passwd_file = HTAuth::PasswdFile.new(@tf.path)
|
|
12
|
-
|
|
13
|
-
@tf2 = Tempfile.new("rpasswrd-passwd-empty")
|
|
14
|
-
@tf2.close
|
|
15
|
-
@empty_passwd_file = HTAuth::PasswdFile.new(@tf2.path)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
after(:each) do
|
|
19
|
-
@tf2.close(true)
|
|
20
|
-
@tf.close(true)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
it "can add a new entry to an already existing passwd file" do
|
|
24
|
-
@passwd_file.add_or_update("charlie", "c secret", "sha1")
|
|
25
|
-
_(@passwd_file.contents).must_equal IO.read(PASSWD_ADD_TEST_FILE)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
it "can tell if an entry already exists in the passwd file" do
|
|
29
|
-
_(@passwd_file.has_entry?("alice")).must_equal true
|
|
30
|
-
_(@passwd_file.has_entry?("david")).must_equal false
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
it "can update an entry in an already existing passwd file, algorithm can change" do
|
|
34
|
-
@passwd_file.add_or_update("alice", "a new secret", "sha1")
|
|
35
|
-
_(@passwd_file.contents).must_equal IO.read(PASSWD_UPDATE_TEST_FILE)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
it "can update an entry in an already existing passwd file, algorithm and arguments can change" do
|
|
39
|
-
@passwd_file.add_or_update("brenda", "b secret", "bcrypt")
|
|
40
|
-
entry = @passwd_file.fetch("brenda")
|
|
41
|
-
_(entry.algorithm.cost).must_equal(::HTAuth::Bcrypt::DEFAULT_APACHE_COST)
|
|
42
|
-
@passwd_file.add_or_update("brenda", "b secret", "bcrypt", :cost => 12)
|
|
43
|
-
entry = @passwd_file.fetch("brenda")
|
|
44
|
-
_(entry.algorithm.cost).must_equal(12)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
it "fetches a copy of an entry" do
|
|
48
|
-
_(@passwd_file.fetch("alice").to_s).must_equal "alice:$apr1$DghnA...$CsPcgerfsI/Ryy0AOAJtb0"
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
it "raises an error if an attempt is made to alter a non-existent file" do
|
|
52
|
-
_ { HTAuth::PasswdFile.new("some-file") }.must_raise(HTAuth::FileAccessError)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# this test will only work on systems that have /etc/ssh_host_rsa_key
|
|
56
|
-
it "raises an error if an attempt is made to open a file where no permissions are granted" do
|
|
57
|
-
_ { HTAuth::PasswdFile.new("/etc/ssh_host_rsa_key") }.must_raise(HTAuth::FileAccessError)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
it "deletes an entry" do
|
|
61
|
-
@passwd_file.delete("bob")
|
|
62
|
-
_(@passwd_file.contents).must_equal IO.read(PASSWD_DELETE_TEST_FILE)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
it "checks authentication of an entry - true" do
|
|
66
|
-
_(@passwd_file.authenticated?("alice", "a secret")).must_equal true
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
it "checks authentication of an entry - false" do
|
|
70
|
-
_(@passwd_file.authenticated?("alice", "the wrong secret")).must_equal false
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
it "is usable in a ruby manner and yields itself when opened" do
|
|
75
|
-
HTAuth::PasswdFile.open(@tf.path) do |pf|
|
|
76
|
-
pf.add_or_update("alice", "a new secret", "md5")
|
|
77
|
-
pf.delete('bob')
|
|
78
|
-
end
|
|
79
|
-
lines = IO.readlines(@tf.path)
|
|
80
|
-
_(lines.size).must_equal 1
|
|
81
|
-
_(lines.first.split(':').first).must_equal "alice"
|
|
82
|
-
_(lines.first.split(':').last).must_match( /\$apr1\$/ )
|
|
83
|
-
end
|
|
84
|
-
end
|
data/spec/plaintext_spec.rb
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require 'htauth/plaintext'
|
|
3
|
-
|
|
4
|
-
describe HTAuth::Plaintext do
|
|
5
|
-
it "encrypts the same way that apache does" do
|
|
6
|
-
apache_result = "a secret"
|
|
7
|
-
pt = HTAuth::Plaintext.new
|
|
8
|
-
_(pt.encode("a secret")).must_equal apache_result
|
|
9
|
-
end
|
|
10
|
-
end
|
|
11
|
-
|
data/spec/sha1_spec.rb
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require 'htauth/sha1'
|
|
3
|
-
|
|
4
|
-
describe HTAuth::Sha1 do
|
|
5
|
-
it "encrypts the same way that apache does" do
|
|
6
|
-
apache_result = "{SHA}ZrnlrvmM7ZCOV3FAvM7la89NKbk="
|
|
7
|
-
sha1 = HTAuth::Sha1.new
|
|
8
|
-
_(sha1.encode("a secret")).must_equal apache_result
|
|
9
|
-
end
|
|
10
|
-
end
|
|
11
|
-
|
data/spec/spec_helper.rb
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
if RUBY_VERSION >= '1.9.2' then
|
|
2
|
-
require 'simplecov'
|
|
3
|
-
puts "Using coverage!"
|
|
4
|
-
SimpleCov.start if ENV['COVERAGE']
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
gem 'minitest'
|
|
8
|
-
require 'minitest/autorun'
|
|
9
|
-
require 'minitest/pride'
|
|
10
|
-
|
|
11
|
-
PASSWD_ORIGINAL_TEST_FILE = File.join(File.dirname(__FILE__), "test.original.passwd")
|
|
12
|
-
PASSWD_ADD_TEST_FILE = File.join(File.dirname(__FILE__), "test.add.passwd")
|
|
13
|
-
PASSWD_UPDATE_TEST_FILE = File.join(File.dirname(__FILE__), "test.update.passwd")
|
|
14
|
-
PASSWD_DELETE_TEST_FILE = File.join(File.dirname(__FILE__), "test.delete.passwd")
|
|
15
|
-
PASSWD_COMMENTED_TEST_FILE = File.join(File.dirname(__FILE__), "test.comments.passwd")
|
|
16
|
-
|
|
17
|
-
DIGEST_ORIGINAL_TEST_FILE = File.join(File.dirname(__FILE__), "test.original.digest")
|
|
18
|
-
DIGEST_ADD_TEST_FILE = File.join(File.dirname(__FILE__), "test.add.digest")
|
|
19
|
-
DIGEST_UPDATE_TEST_FILE = File.join(File.dirname(__FILE__), "test.update.digest")
|
|
20
|
-
DIGEST_DELETE_TEST_FILE = File.join(File.dirname(__FILE__), "test.delete.digest")
|
|
21
|
-
DIGEST_COMMENTED_TEST_FILE = File.join(File.dirname(__FILE__), "test.comments.digest")
|
|
22
|
-
|
|
23
|
-
require 'stringio'
|
|
24
|
-
class ConsoleIO < StringIO
|
|
25
|
-
def noecho(&block)
|
|
26
|
-
yield self
|
|
27
|
-
end
|
|
28
|
-
end
|
data/spec/test.add.digest
DELETED
data/spec/test.add.passwd
DELETED
data/spec/test.delete.digest
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
bob:htauth:fcbeab6821d2ab3b00934c958db0fd1e
|
data/spec/test.delete.passwd
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
alice:$apr1$DghnA...$CsPcgerfsI/Ryy0AOAJtb0
|
data/spec/test.original.digest
DELETED
data/spec/test.original.passwd
DELETED
data/spec/test.update.digest
DELETED
data/spec/test.update.passwd
DELETED
data/tasks/default.rake
DELETED
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
# vim: syntax=ruby
|
|
2
|
-
require 'rake/clean'
|
|
3
|
-
require 'digest'
|
|
4
|
-
#------------------------------------------------------------------------------
|
|
5
|
-
# If you want to Develop on this project just run 'rake develop' and you'll
|
|
6
|
-
# have all you need to get going. If you want to use bundler for development,
|
|
7
|
-
# then run 'rake develop:using_bundler'
|
|
8
|
-
#------------------------------------------------------------------------------
|
|
9
|
-
namespace :develop do
|
|
10
|
-
|
|
11
|
-
# Install all the development and runtime dependencies of this gem using the
|
|
12
|
-
# gemspec.
|
|
13
|
-
task :default => 'Gemfile' do
|
|
14
|
-
require 'rubygems/dependency_installer'
|
|
15
|
-
installer = ::Gem::DependencyInstaller.new
|
|
16
|
-
puts "Installing bundler..."
|
|
17
|
-
installer.install 'bundler'
|
|
18
|
-
sh 'bundle install'
|
|
19
|
-
puts "\n\nNow run 'rake test'"
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
# Create a Gemfile that just references the gemspec
|
|
23
|
-
file 'Gemfile' => :gemspec do
|
|
24
|
-
File.open( "Gemfile", "w+" ) do |f|
|
|
25
|
-
f.puts "# DO NOT EDIT - This file is automatically generated"
|
|
26
|
-
f.puts "# Make changes to Manifest.txt and/or Rakefile and regenerate"
|
|
27
|
-
f.puts 'source "https://rubygems.org"'
|
|
28
|
-
f.puts 'gemspec'
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
desc "Bootstrap development"
|
|
33
|
-
task :develop => "develop:default"
|
|
34
|
-
|
|
35
|
-
#------------------------------------------------------------------------------
|
|
36
|
-
# Minitest - standard TestTask
|
|
37
|
-
#------------------------------------------------------------------------------
|
|
38
|
-
begin
|
|
39
|
-
require 'rake/testtask'
|
|
40
|
-
Rake::TestTask.new( :test ) do |t|
|
|
41
|
-
t.ruby_opts = %w[ -w ]
|
|
42
|
-
t.libs = %w[ lib spec test ]
|
|
43
|
-
t.pattern = "{test,spec}/**/{test_*,*_spec}.rb"
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
task :test_requirements
|
|
47
|
-
task :test => :test_requirements
|
|
48
|
-
task :default => :test
|
|
49
|
-
rescue LoadError
|
|
50
|
-
This.task_warning( 'test' )
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
#------------------------------------------------------------------------------
|
|
54
|
-
# RDoc - standard rdoc rake task, although we must make sure to use a more
|
|
55
|
-
# recent version of rdoc since it is the one that has 'tomdoc' markup
|
|
56
|
-
#------------------------------------------------------------------------------
|
|
57
|
-
begin
|
|
58
|
-
gem 'rdoc' # otherwise we get the wrong task from stdlib
|
|
59
|
-
require 'rdoc/task'
|
|
60
|
-
RDoc::Task.new do |t|
|
|
61
|
-
t.markup = 'tomdoc'
|
|
62
|
-
t.rdoc_dir = 'doc'
|
|
63
|
-
t.main = 'README.md'
|
|
64
|
-
t.title = "#{This.name} #{This.version}"
|
|
65
|
-
t.rdoc_files.include( FileList['*.{rdoc,md,txt}'], FileList['ext/**/*.c'],
|
|
66
|
-
FileList['lib/**/*.rb'] )
|
|
67
|
-
end
|
|
68
|
-
rescue StandardError, LoadError
|
|
69
|
-
This.task_warning( 'rdoc' )
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
#------------------------------------------------------------------------------
|
|
73
|
-
# Coverage - optional code coverage, rcov for 1.8 and simplecov for 1.9, so
|
|
74
|
-
# for the moment only rcov is listed.
|
|
75
|
-
#------------------------------------------------------------------------------
|
|
76
|
-
begin
|
|
77
|
-
require 'simplecov'
|
|
78
|
-
desc 'Run tests with code coverage'
|
|
79
|
-
task :coverage do
|
|
80
|
-
ENV['COVERAGE'] = 'true'
|
|
81
|
-
Rake::Task[:test].execute
|
|
82
|
-
end
|
|
83
|
-
CLOBBER << 'coverage' if File.directory?( 'coverage' )
|
|
84
|
-
rescue LoadError
|
|
85
|
-
This.task_warning( 'simplecov' )
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
#------------------------------------------------------------------------------
|
|
89
|
-
# Manifest - We want an explicit list of thos files that are to be packaged in
|
|
90
|
-
# the gem. Most of this is from Hoe.
|
|
91
|
-
#------------------------------------------------------------------------------
|
|
92
|
-
namespace 'manifest' do
|
|
93
|
-
desc "Check the manifest"
|
|
94
|
-
task :check => :clean do
|
|
95
|
-
files = FileList["**/*", ".*"].exclude( This.exclude_from_manifest ).to_a.sort
|
|
96
|
-
files = files.select{ |f| File.file?( f ) }
|
|
97
|
-
|
|
98
|
-
tmp = "Manifest.tmp"
|
|
99
|
-
File.open( tmp, 'w' ) do |f|
|
|
100
|
-
f.puts files.join("\n")
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
begin
|
|
104
|
-
sh "diff -du Manifest.txt #{tmp}"
|
|
105
|
-
ensure
|
|
106
|
-
rm tmp
|
|
107
|
-
end
|
|
108
|
-
puts "Manifest looks good"
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
desc "Generate the manifest"
|
|
112
|
-
task :generate => :clean do
|
|
113
|
-
files = %x[ git ls-files ].split("\n").sort
|
|
114
|
-
files.reject! { |f| f =~ This.exclude_from_manifest }
|
|
115
|
-
File.open( "Manifest.txt", "w" ) do |f|
|
|
116
|
-
f.puts files.join("\n")
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
#------------------------------------------------------------------------------
|
|
122
|
-
# Fixme - look for fixmes and report them
|
|
123
|
-
#------------------------------------------------------------------------------
|
|
124
|
-
namespace :fixme do
|
|
125
|
-
task :default => 'manifest:check' do
|
|
126
|
-
This.manifest.each do |file|
|
|
127
|
-
next if file == __FILE__
|
|
128
|
-
next unless file =~ %r/(txt|rb|md|rdoc|css|html|xml|css)\Z/
|
|
129
|
-
puts "FIXME: Rename #{file}" if file =~ /fixme/i
|
|
130
|
-
IO.readlines( file ).each_with_index do |line, idx|
|
|
131
|
-
prefix = "FIXME: #{file}:#{idx+1}".ljust(42)
|
|
132
|
-
puts "#{prefix} => #{line.strip}" if line =~ /fixme/i
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def fixme_project_root
|
|
138
|
-
This.project_path( '../fixme' )
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def fixme_project_path( subtree )
|
|
142
|
-
fixme_project_root.join( subtree )
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def local_fixme_files
|
|
146
|
-
This.manifest.select { |p| p =~ %r|^tasks/| }
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def outdated_fixme_files
|
|
150
|
-
local_fixme_files.select do |local|
|
|
151
|
-
upstream = fixme_project_path( local )
|
|
152
|
-
upstream.exist? &&
|
|
153
|
-
( Digest::SHA256.file( local ) != Digest::SHA256.file( upstream ) )
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def fixme_up_to_date?
|
|
158
|
-
outdated_fixme_files.empty?
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
desc "See if the fixme tools are outdated"
|
|
162
|
-
task :outdated do
|
|
163
|
-
if fixme_up_to_date? then
|
|
164
|
-
puts "Fixme files are up to date."
|
|
165
|
-
else
|
|
166
|
-
outdated_fixme_files.each do |f|
|
|
167
|
-
puts "#{f} is outdated"
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
desc "Update outdated fixme files"
|
|
173
|
-
task :update do
|
|
174
|
-
if fixme_up_to_date? then
|
|
175
|
-
puts "Fixme files are already up to date."
|
|
176
|
-
else
|
|
177
|
-
puts "Updating fixme files:"
|
|
178
|
-
outdated_fixme_files.each do |local|
|
|
179
|
-
upstream = fixme_project_path( local )
|
|
180
|
-
puts " * #{local}"
|
|
181
|
-
FileUtils.cp( upstream, local )
|
|
182
|
-
end
|
|
183
|
-
puts "Use your git commands as appropriate."
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
desc "Look for fixmes and report them"
|
|
188
|
-
task :fixme => "fixme:default"
|
|
189
|
-
|
|
190
|
-
#------------------------------------------------------------------------------
|
|
191
|
-
# Gem Specification
|
|
192
|
-
#------------------------------------------------------------------------------
|
|
193
|
-
# Really this is only here to support those who use bundler
|
|
194
|
-
desc "Build the #{This.name}.gemspec file"
|
|
195
|
-
task :gemspec do
|
|
196
|
-
File.open( This.gemspec_file, "wb+" ) do |f|
|
|
197
|
-
f.puts "# DO NOT EDIT - This file is automatically generated"
|
|
198
|
-
f.puts "# Make changes to Manifest.txt and/or Rakefile and regenerate"
|
|
199
|
-
f.write This.platform_gemspec.to_ruby
|
|
200
|
-
end
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
# .rbc files from ruby 2.0
|
|
204
|
-
CLOBBER << FileList["**/*.rbc"]
|
|
205
|
-
|
|
206
|
-
# The standard gem packaging task, everyone has it.
|
|
207
|
-
require 'rubygems/package_task'
|
|
208
|
-
::Gem::PackageTask.new( This.platform_gemspec ) do
|
|
209
|
-
# nothing
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
#------------------------------------------------------------------------------
|
|
213
|
-
# Release - the steps we go through to do a final release, this is pulled from
|
|
214
|
-
# a compbination of mojombo's rakegem, hoe and hoe-git
|
|
215
|
-
#
|
|
216
|
-
# 1) make sure we are on the main branch
|
|
217
|
-
# 2) make sure there are no uncommitted items
|
|
218
|
-
# 3) check the manifest and make sure all looks good
|
|
219
|
-
# 4) build the gem
|
|
220
|
-
# 5) do an empty commit to have the commit message of the version
|
|
221
|
-
# 6) tag that commit as the version
|
|
222
|
-
# 7) push main
|
|
223
|
-
# 8) push the tag
|
|
224
|
-
# 7) pus the gem
|
|
225
|
-
#------------------------------------------------------------------------------
|
|
226
|
-
task :release_check do
|
|
227
|
-
unless `git branch` =~ /^\* main/
|
|
228
|
-
abort "You must be on the main branch to release!"
|
|
229
|
-
end
|
|
230
|
-
unless `git status` =~ /^nothing to commit/m
|
|
231
|
-
abort "Nope, sorry, you have unfinished business"
|
|
232
|
-
end
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
desc "Create tag v#{This.version}, build and push #{This.platform_gemspec.full_name} to rubygems.org"
|
|
236
|
-
task :release => [ :release_check, 'manifest:check', :gem ] do
|
|
237
|
-
sh "git commit --allow-empty -a -m 'Release #{This.version}'"
|
|
238
|
-
sh "git tag -a -m 'v#{This.version}' v#{This.version}"
|
|
239
|
-
sh "git push origin main"
|
|
240
|
-
sh "git push origin v#{This.version}"
|
|
241
|
-
sh "gem push pkg/#{This.platform_gemspec.full_name}.gem"
|
|
242
|
-
end
|