rake-deveiate 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 53275d426cc35203e19ffb6354a49628d385b4b457b312863f98de52c1c205ae
4
+ data.tar.gz: 9cc9b5659a6612aef89b69d153777ac1f2c29fb9ad8504c4e80d66dfc295243a
5
+ SHA512:
6
+ metadata.gz: 305c88f5e61673c7b898ac213daff8bb6013b0a9ea243c4eb46b175ea59f44e2913d0b632782a38f1f211a6afe9ac0dc640febfb3915564916c7dd22a99cebc2
7
+ data.tar.gz: 063f316622af921ba648aaf65ebf65efd486f71c17fedae4baab90e95c2a82eb6c407350984711c8d7b8084bc6ae6742d9d6ddd5de3e6e6b0a35903010405a85
checksums.yaml.gz.sig ADDED
Binary file
data/History.md ADDED
@@ -0,0 +1,7 @@
1
+ # Release History for rake-deveiate
2
+
3
+ ---
4
+
5
+ ## v0.1.0 [2019/10/02] Michael Granger <ged@FaerieMUD.org>
6
+
7
+ First release.
data/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # Rake Tasks for DevEiate Libraries
2
+
3
+ home
4
+ : https://hg.sr.ht/~ged/rake-deveiate
5
+
6
+ github
7
+ : https://github.com/ged/rake-deveiate
8
+
9
+ docs
10
+ : https://deveiate.org/code/rake-deveiate/
11
+
12
+
13
+ ## Description
14
+
15
+ This is a collection of Rake tasks I use for development. I distribute them as
16
+ a gem mostly so people who wish to contribute to the other Open Source
17
+ libraries I maintain can do so easily, but of course you're welcome to use them
18
+ yourself if you find them useful.
19
+
20
+
21
+ ## Prerequisites
22
+
23
+ * Ruby 2.6+
24
+
25
+
26
+ ## Installation
27
+
28
+ $ gem install rake-deveiate
29
+
30
+
31
+ ## Usage
32
+
33
+ Make a Rakefile with the following content:
34
+
35
+ require 'rake/deveiate'
36
+
37
+ Rake::DevEiate.setup( 'gemname' )
38
+
39
+ You can also pass a block to customize the project settings:
40
+
41
+ Rake::DevEiate.setup( 'gemname' ) do |project|
42
+ project.description = <<~END_DESC
43
+ This is a gem that does some stuff. It does a few things well, a lot of
44
+ things sufficiently, and nothing badly.
45
+ END_DESC
46
+ project.authors = [ 'J. Random Hacker <jrandom@example.com>' ]
47
+ end
48
+
49
+
50
+
51
+ ## Authors
52
+
53
+ - Michael Granger <ged@FaerieMUD.org>
54
+
55
+
56
+ ## License
57
+
58
+ Copyright (c) 2019, Michael Granger
59
+ All rights reserved.
60
+
61
+ Redistribution and use in source and binary forms, with or without
62
+ modification, are permitted provided that the following conditions are met:
63
+
64
+ * Redistributions of source code must retain the above copyright notice,
65
+ this list of conditions and the following disclaimer.
66
+
67
+ * Redistributions in binary form must reproduce the above copyright notice,
68
+ this list of conditions and the following disclaimer in the documentation
69
+ and/or other materials provided with the distribution.
70
+
71
+ * Neither the name of the author/s, nor the names of the project's
72
+ contributors may be used to endorse or promote products derived from this
73
+ software without specific prior written permission.
74
+
75
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
76
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
77
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
78
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
79
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
80
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
81
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
82
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
83
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
84
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,5 @@
1
+ <%= header_char %> Release History for <%= project.name %>
2
+
3
+ ---
4
+
5
+
@@ -0,0 +1,64 @@
1
+ <%= header_char %> <%= project.name.gsub(/\b([a-z])/) {|m| m.upcase } %>
2
+
3
+ home
4
+ : https://hg.sr.ht/~ged/<%= project.name %>
5
+
6
+ github
7
+ : https://github.com/ged/<%= project.name %>
8
+
9
+ docs
10
+ : https://deveiate.org/code/<%= project.name %>
11
+
12
+
13
+ <%= header_char * 2 %> Description
14
+
15
+ A gem of some sort.
16
+
17
+
18
+ <%= header_char * 2 %> Prerequisites
19
+
20
+ * Ruby <%= RUBY_VERSION.sub(/\.\d+$/, '') %>
21
+
22
+
23
+ <%= header_char * 2 %> Installation
24
+
25
+ $ gem install <%= project.name %>
26
+
27
+
28
+ <%= header_char * 2 %> Authors
29
+
30
+ <% project.authors.each do |author| -%>
31
+ - <%= author %>
32
+ <% end -%>
33
+
34
+
35
+ <%= header_char * 2 %> License
36
+
37
+ Copyright (c) <%= Date.today.year %>, <%= project.author_names.join(' and ') %>
38
+ All rights reserved.
39
+
40
+ Redistribution and use in source and binary forms, with or without
41
+ modification, are permitted provided that the following conditions are met:
42
+
43
+ * Redistributions of source code must retain the above copyright notice,
44
+ this list of conditions and the following disclaimer.
45
+
46
+ * Redistributions in binary form must reproduce the above copyright notice,
47
+ this list of conditions and the following disclaimer in the documentation
48
+ and/or other materials provided with the distribution.
49
+
50
+ * Neither the name of the author/s, nor the names of the project's
51
+ contributors may be used to endorse or promote products derived from this
52
+ software without specific prior written permission.
53
+
54
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
55
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
57
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
58
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
60
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
61
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
62
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
63
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64
+
@@ -0,0 +1,200 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'rake'
5
+
6
+ require 'rake/deveiate' unless defined?( Rake::DevEiate )
7
+
8
+
9
+ # Sanity and quality check tasks
10
+ module Rake::DevEiate::Checks
11
+ extend Rake::DSL
12
+
13
+ # Emoji for style advisories
14
+ SADFACE = "\u{1f622}"
15
+
16
+ # Tab-arrow character
17
+ TAB_ARROW = "\u{21e5}"
18
+
19
+ # Regexp to match trailing whitespace
20
+ TRAILING_WHITESPACE_RE = /\p{Blank}+$/m
21
+
22
+ # Regexp to match lines with mixed indentation
23
+ MIXED_INDENT_RE = /(?<!#)([ ]\t)/
24
+
25
+
26
+ ### Set up task defaults
27
+ def setup( _name, **options )
28
+ super if defined?( super )
29
+
30
+ @quality_check_whitelist = Rake::FileList.new
31
+ end
32
+
33
+
34
+ ##
35
+ # The Rake::FileList containing files which should not be quality-checked.
36
+ attr_reader :quality_check_whitelist
37
+
38
+
39
+ ### Define check tasks
40
+ def define_tasks
41
+ super if defined?( super )
42
+
43
+ self.define_quality_checker_tasks
44
+ self.define_sanity_checker_tasks
45
+
46
+ desc "Run several quality-checks on the code"
47
+ task :quality_checks => [ 'quality_checks:all' ]
48
+
49
+ desc "Run several sanity-checks on the code"
50
+ task :sanity_checks => [ 'sanity_checks:all' ]
51
+
52
+ task :check => [ :quality_checks, :sanity_checks ]
53
+
54
+ end
55
+
56
+
57
+ ### Set up tasks that check for poor whitespace discipline
58
+ def define_quality_checker_tasks
59
+
60
+ namespace :quality_checks do
61
+
62
+ task :all => [ :whitespace ]
63
+
64
+ desc "Check source code for inconsistent whitespace"
65
+ task :whitespace => [
66
+ :for_trailing_whitespace,
67
+ :for_mixed_indentation,
68
+ ]
69
+
70
+ desc "Check source code for trailing whitespace"
71
+ task :for_trailing_whitespace do
72
+ lines = find_matching_source_lines do |line, _|
73
+ line =~ TRAILING_WHITESPACE_RE
74
+ end
75
+
76
+ unless lines.empty?
77
+ desc = "Found some trailing whitespace"
78
+ describe_lines_that_need_fixing( desc, lines, TRAILING_WHITESPACE_RE )
79
+ fail
80
+ end
81
+ end
82
+
83
+ desc "Check source code for mixed indentation"
84
+ task :for_mixed_indentation do
85
+ lines = find_matching_source_lines do |line, _|
86
+ line =~ MIXED_INDENT_RE
87
+ end
88
+
89
+ unless lines.empty?
90
+ desc = "Found mixed indentation"
91
+ describe_lines_that_need_fixing( desc, lines, /[ ]\t/ )
92
+ fail
93
+ end
94
+ end
95
+
96
+ end
97
+
98
+ end
99
+
100
+
101
+ ### Set up some sanity-checks as dependencies of higher-level tasks
102
+ def define_sanity_checker_tasks
103
+
104
+ namespace :sanity_checks do
105
+
106
+ desc "Check source code for common problems"
107
+ task :all
108
+
109
+ end
110
+
111
+ end
112
+
113
+
114
+ # Return tuples of the form:
115
+ #
116
+ # [ <filename>, <line number>, <line> ]
117
+ #
118
+ # for every line in the Gemspec's source files for which the block
119
+ # returns true.
120
+ def find_matching_source_lines
121
+ matches = []
122
+
123
+ source_files = self.project_files.grep( /\.(h|c|rb)$/ )
124
+ source_files -= self.quality_check_whitelist
125
+
126
+ source_files.each do |filename|
127
+ previous_line = nil
128
+
129
+ IO.foreach( filename ).with_index do |line, i|
130
+ matches << [filename, i + 1, line] if yield( line, previous_line )
131
+ previous_line = line
132
+ end
133
+ end
134
+
135
+ return matches
136
+ end
137
+
138
+
139
+ ### Output a listing of the specified lines with the given +description+, highlighting
140
+ ### the characters matched by the specified +re+.
141
+ def describe_lines_that_need_fixing( description, lines, re )
142
+ self.prompt.say "\n"
143
+ self.prompt.say SADFACE + " "
144
+ self.prompt.error( "Uh-oh! " + description )
145
+
146
+ grouped_lines = group_line_matches( lines )
147
+
148
+ grouped_lines.each do |filename, linegroups|
149
+ linegroups.each do |group, lines|
150
+ if group.min == group.max
151
+ self.prompt.say( "%s:%d" % [ filename, group.min ], color: :bold )
152
+ else
153
+ self.prompt.say( "%s:%d-%d" % [ filename, group.min, group.max ], color: :bold )
154
+ end
155
+
156
+ lines.each_with_index do |line, i|
157
+ self.prompt.say "%s: %s" % [
158
+ self.pastel.dark.white( group.to_a[i].to_s ),
159
+ highlight_problems( line, re )
160
+ ]
161
+ end
162
+ self.prompt.say "\n"
163
+ end
164
+ end
165
+ end
166
+
167
+
168
+ # Return a Hash, keyed by filename, whose values are tuples of Ranges
169
+ # and lines extracted from the given [filename, linenumber, line] +tuples+.
170
+ def group_line_matches( tuples )
171
+ by_file = tuples.group_by {|tuple| tuple.first }
172
+
173
+ return by_file.each_with_object({}) do |(filename, lines), hash|
174
+ last_linenum = 0
175
+ linegroups = lines.slice_before do |filename, linenum|
176
+ gap = linenum > last_linenum + 1
177
+ last_linenum = linenum
178
+ gap
179
+ end
180
+
181
+ hash[ filename ] = linegroups.map do |group|
182
+ rng = group.first[1] .. group.last[1]
183
+ grouplines = group.transpose.last
184
+ [ rng, grouplines ]
185
+ end
186
+ end
187
+ end
188
+
189
+
190
+ ### Transform invisibles in the specified line into visible analogues.
191
+ def highlight_problems( line, re )
192
+ line.
193
+ gsub( re ) { self.pastel.on_red( $& ) }.
194
+ gsub( /\t+/ ) { self.pastel.dark.white( "#{TAB_ARROW} " * $&.length ) }
195
+ end
196
+
197
+
198
+ end # module Rake::DevEiate::Docs
199
+
200
+
@@ -0,0 +1,34 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'rake'
5
+ require 'rake/phony'
6
+ require 'rdoc/task'
7
+
8
+ require 'rake/deveiate' unless defined?( Rake::DevEiate )
9
+
10
+
11
+ # Documentation-generation tasks
12
+ module Rake::DevEiate::Docs
13
+ extend Rake::DSL
14
+
15
+
16
+ ### Define documentation tasks
17
+ def define_tasks
18
+ super if defined?( super )
19
+
20
+ task :docs => :phony
21
+
22
+ RDoc::Task.new( 'docs' ) do |rdoc|
23
+ rdoc.main = self.readme_file.to_s
24
+ rdoc.rdoc_files = self.rdoc_files
25
+ rdoc.generator = :fivefish
26
+ rdoc.title = self.title
27
+ rdoc.rdoc_dir = Rake::DevEiate::DOCS_DIR.to_s
28
+ end
29
+
30
+ end
31
+
32
+ end # module Rake::DevEiate::Docs
33
+
34
+
@@ -0,0 +1,97 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'set'
5
+ require 'rubygems'
6
+ require 'rake/deveiate' unless defined?( Rake::DevEiate )
7
+
8
+
9
+ # A dependency finder that groks the GemDependencyApi
10
+ class Rake::DevEiate::GemDepFinder
11
+
12
+ ### Create a new GemDepFinder that will find dependencies in the given
13
+ ### +depfile+.
14
+ def initialize( depfile )
15
+ @depfile = Pathname( depfile )
16
+ @dependencies = Set.new
17
+ @current_groups = Set.new
18
+ end
19
+
20
+
21
+
22
+ ######
23
+ public
24
+ ######
25
+
26
+ ##
27
+ # The Pathname of the file to find dependencies in
28
+ attr_reader :depfile
29
+
30
+ ##
31
+ # The Set of Gem::Dependency objects that describe the loaded dependencies
32
+ attr_reader :dependencies
33
+
34
+ ##
35
+ # The current set of groups to add to any declared gems
36
+ attr_reader :current_groups
37
+
38
+
39
+ ### Load the dependencies file.
40
+ def load
41
+ source = self.depfile.read
42
+ self.instance_eval( source, self.depfile.to_s, 1 )
43
+ end
44
+
45
+
46
+ #
47
+ # Gem Dependency API methods
48
+ #
49
+
50
+
51
+ ### Declare a dependency on a gem. Ignores every option except :group.
52
+ def gem( name, *requirements, **options )
53
+ if options[:group] == :development ||
54
+ options[:groups]&.include?( :development ) ||
55
+ self.current_groups.include?( :development )
56
+
57
+ requirements.push( :development )
58
+ end
59
+
60
+ dependency = Gem::Dependency.new( name, *requirements )
61
+
62
+ self.dependencies.add( dependency )
63
+ end
64
+
65
+
66
+ ### Declare a group block.
67
+ def group( *names )
68
+ options = names.pop if names.last.is_a?( Hash )
69
+ previous_groups = self.current_groups.dup
70
+ self.current_groups.replace( names )
71
+
72
+ yield
73
+ ensure
74
+ self.current_groups.replace( previous_groups ) if previous_groups
75
+ end
76
+
77
+
78
+ ### Raise, as the gemdeps file should be the authoritative source.
79
+ def gemspec( * )
80
+ raise "Circular dependency: can't depend on the gemspec to build itself"
81
+ end
82
+
83
+
84
+ ### Ignore a gem dependency API call.
85
+ def no_op_method( * ) # :nodoc:
86
+ yield if block_given?
87
+ end
88
+
89
+ alias_method :source, :no_op_method
90
+ alias_method :git, :no_op_method
91
+ alias_method :platform, :no_op_method
92
+ alias_method :platforms, :no_op_method
93
+ alias_method :ruby, :no_op_method
94
+
95
+
96
+ end # class Rake::DevEiate::GemDepFinder
97
+
@@ -0,0 +1,177 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'etc'
5
+ require 'rubygems'
6
+ require 'rake'
7
+ require 'rake/clean'
8
+ require 'rake/deveiate' unless defined?( Rake::DevEiate )
9
+
10
+
11
+ # Gemspec-generation tasks
12
+ module Rake::DevEiate::Gemspec
13
+ extend Rake::DSL
14
+
15
+ # Pattern for splitting parsed authors list items into name and email
16
+ AUTHOR_PATTERN = /^(?<name>.*)\s<(?<email>.*)>$/
17
+
18
+
19
+ ##
20
+ # The path to the file used to sign released gems
21
+ attr_accessor :signing_key
22
+
23
+
24
+ ### Set some defaults when the task lib is set up.
25
+ def setup( _name, **options )
26
+ super if defined?( super )
27
+
28
+ @signing_key = options[:signing_key] || Gem.default_key_path
29
+
30
+ @gemspec = nil
31
+ end
32
+
33
+
34
+ ### Reset any cached data when project attributes change.
35
+ def reset
36
+ super if defined?( super )
37
+ @gemspec = nil
38
+ end
39
+
40
+
41
+ ### Define gemspec tasks
42
+ def define_tasks
43
+ super if defined?( super )
44
+
45
+ gemspec_file = "#{self.name}.gemspec"
46
+
47
+ if self.has_manifest?
48
+ file( self.manifest_file )
49
+ file( gemspec_file => self.manifest_file )
50
+ else
51
+ file( gemspec_file )
52
+ end
53
+
54
+ task( gemspec_file ) do |task|
55
+ self.prompt.say "Updating gemspec"
56
+
57
+ spec = self.make_prerelease_gemspec
58
+
59
+ File.open( task.name, 'w' ) do |fh|
60
+ fh.write( spec.to_ruby )
61
+ end
62
+ end
63
+
64
+ desc "(Re)Generate the gemspec file"
65
+ task :gemspec => gemspec_file
66
+
67
+ CLEAN.include( gemspec_file.to_s )
68
+
69
+ task :checkin => :gemspec
70
+
71
+ task( :gemspec_debug, &method(:do_gemspec_debug) )
72
+ task :debug => :gemspec_debug
73
+ end
74
+
75
+
76
+ ### Task function -- output debugging for gemspec tasks.
77
+ def do_gemspec_debug( task, args )
78
+ gemspec = self.gemspec
79
+ gemspec_src = gemspec.to_yaml
80
+
81
+ self.prompt.say( "Gemspec:", color: :bright_green )
82
+ self.prompt.say( indent(gemspec_src, 4) )
83
+ self.prompt.say( "\n" )
84
+ end
85
+
86
+
87
+ ### Return the project's Gem::Specification, creating it if necessary.
88
+ def gemspec
89
+ return @gemspec ||= self.make_gemspec
90
+ end
91
+
92
+
93
+ ### Validate the gemspec, raising a Gem::InvalidSpecificationException if it's
94
+ ### not valid.
95
+ def validate_gemspec( packaging=true, strict=false )
96
+ return self.gemspec.validate( packaging, strict )
97
+ end
98
+
99
+
100
+ ### Return a Gem::Specification created from the project's metadata.
101
+ def make_gemspec
102
+ spec = Gem::Specification.new
103
+
104
+ spec.name = self.name
105
+ spec.description = self.description
106
+ spec.homepage = self.homepage
107
+ spec.summary = self.summary || self.extract_summary
108
+ spec.files = self.project_files
109
+ spec.signing_key = self.resolve_signing_key.to_s
110
+ spec.cert_chain = self.cert_files.map( &File.method(:expand_path) ).to_a
111
+ spec.version = self.version
112
+ spec.licenses = self.licenses
113
+ spec.date = Date.today
114
+
115
+ self.authors.each do |author|
116
+ if ( m = author.match(AUTHOR_PATTERN) )
117
+ spec.authors ||= []
118
+ spec.authors << m[:name]
119
+ spec.email ||= []
120
+ spec.email << m[:email] if m[:email]
121
+ else
122
+ self.prompt.warn "Couldn't extract author name + email from %p" % [ author ]
123
+ end
124
+ end
125
+
126
+ self.dependencies.each do |dep|
127
+ if dep.runtime?
128
+ spec.add_runtime_dependency( dep )
129
+ else
130
+ spec.add_development_dependency( dep )
131
+ end
132
+ end
133
+
134
+ return spec
135
+ end
136
+
137
+
138
+ ### Return a Gem::Specification with its properties modified to be suitable for
139
+ ### a pre-release gem.
140
+ def make_prerelease_gemspec
141
+ spec = self.make_gemspec
142
+
143
+ spec.version = self.prerelease_version
144
+ spec.signing_key = nil
145
+ spec.cert_chain = []
146
+
147
+ return spec
148
+ end
149
+
150
+
151
+ ### Return a version string
152
+ def prerelease_version
153
+ return "#{self.version.bump}.0.pre.#{Time.now.strftime("%Y%m%d%H%M%S")}"
154
+ end
155
+
156
+
157
+ ### Resolve the path of the signing key
158
+ def resolve_signing_key
159
+ path = Pathname( self.signing_key ).expand_path
160
+ path = path.readlink if path.symlink?
161
+ return path
162
+ end
163
+
164
+
165
+ #######
166
+ private
167
+ #######
168
+
169
+ ### Return a copy of the given text prefixed by +spaces+ number of spaces.
170
+ def indent( text, spaces=4 )
171
+ prefix = ' ' * spaces
172
+ return text.gsub( /(?<=\A|\n)/m, prefix )
173
+ end
174
+
175
+ end # module Rake::DevEiate::Gemspec
176
+
177
+