revision 1.5.3 → 1.6.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/.gitignore +2 -0
- data/README.org +62 -57
- data/lib/revision/cli.rb +16 -0
- data/lib/revision/md5.rb +51 -0
- data/lib/revision/releasable.rb +45 -36
- data/lib/revision/version.rb +4 -1
- data/releasables.yaml +11 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d15b43ba3833b4581b97b89a7c5ba40890373b5cc85f1186f5917e9ad5d5fb21
|
4
|
+
data.tar.gz: badf688375adaab009a16f4e03a250cb77725fcc3d140d3b47a38d7e8436a575
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33dea1ba91f478dc674d9c5b3f23f875b9ca192f9a3efa2a27f2e0e33b39a119ffb367353dd17e7b3ecb4ce17df64747b0edef415ea66eb9a1fe8604fae73378
|
7
|
+
data.tar.gz: 7c386e43dd4f3979462f0e0d4c1ca6ae29409f042d5239f5b1434ba7bb8ef4836d5ca70b0786bf86e0b83a4102191f60f4f23df883341c67a0fcd2b851f4d5d7
|
data/.gitignore
CHANGED
data/README.org
CHANGED
@@ -23,7 +23,7 @@
|
|
23
23
|
# or alternatively #+SETUPFILE: theme-readtheorg.setup
|
24
24
|
|
25
25
|
* Overview
|
26
|
-
This gem automates revision management for source projects. The tool is language agnostic (used for C, ruby and matlab projects, to date -- though you're probably better off using bundler for ruby projects). It supports per-project configuration using a yaml-format file called =releasables.yaml= located at the project root.
|
26
|
+
This gem automates revision management for source projects. The tool is language agnostic (used for C, ruby, python and matlab projects, to date -- though you're probably better off using bundler for ruby projects). It supports per-project configuration using a yaml-format file called =releasables.yaml= located at the project root.
|
27
27
|
|
28
28
|
It currently supports the following functionality
|
29
29
|
- Manage 3-component revision IDs embedded natively in source file
|
@@ -31,9 +31,10 @@ It currently supports the following functionality
|
|
31
31
|
- Automatically prompts for a changelog entry each time a revision identifier is incremented
|
32
32
|
- Optionally commits and tags changes to a git repo after an update to the revision ID
|
33
33
|
- Builds and archives projects in zip format (including release notes and arbitrary release artefacts defined
|
34
|
-
- Deploys
|
34
|
+
- Deploys build artefacts to one or more defined (local or remote) filesystem locations
|
35
|
+
- Automatically generates md5sums when archiving or deploying build artefacts
|
35
36
|
|
36
|
-
|
37
|
+
Hacked on sporadically to allow me to tag, archive and deploy projects in multiple languages in a consistent fashion.
|
37
38
|
|
38
39
|
* Installation
|
39
40
|
** Dependencies
|
@@ -101,7 +102,7 @@ Run the executable with no arguments to get usage instructions in your console w
|
|
101
102
|
|
102
103
|
#+RESULTS:
|
103
104
|
#+begin_example
|
104
|
-
Loading releasable definitions from /home/cormacc/
|
105
|
+
Loading releasable definitions from /home/cormacc/nmd/gem/revision/releasables.yaml ...
|
105
106
|
Commands:
|
106
107
|
revision --version, -v # print the version
|
107
108
|
revision archive # Archive releasable(s)
|
@@ -111,6 +112,7 @@ Commands:
|
|
111
112
|
revision help [COMMAND] # Describe available commands or one specific command
|
112
113
|
revision info # Display info for all defined releasables
|
113
114
|
revision major # Increment major revision index
|
115
|
+
revision md5 # Compute md5sums for current build artefacts
|
114
116
|
revision minor # Increment minor revision index
|
115
117
|
revision package # Build and archive releasables
|
116
118
|
revision patch # Increment patch revision index
|
@@ -166,36 +168,39 @@ The lines beginning with '#' are explanatory comments
|
|
166
168
|
#+END_NOTE
|
167
169
|
|
168
170
|
#+BEGIN_SRC yaml
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
171
|
+
:releasables
|
172
|
+
- :id: my_releasable
|
173
|
+
:revision:
|
174
|
+
# Source file containing the revision identifier
|
175
|
+
# This will also include changelog entries, embedded as comments
|
176
|
+
:src: lib/revision/version.rb
|
177
|
+
# Regex matching the source revision identifier. Must contain the following named capture groups
|
178
|
+
# - major, minor, patch :: Numeric (uint) sequences representing the three revision ID components
|
179
|
+
# - sep1, sep2 :: the characters separating the revision components
|
180
|
+
# - prefix, postfix :: sufficient syntactic context to match the revision ID uniquely
|
181
|
+
# N.B. this regex matches the version ID from the standard bundler gem skeleton,
|
182
|
+
# e.g. VERSION = "1.1.0"
|
183
|
+
:regex: (?<prefix>VERSION = ")(?<major>\d+)(?<sep1>\.)(?<minor>\d+)(?<sep2>\.)(?<patch>\d+)(?<postfix>")
|
184
|
+
# Comment char for the project language -- prefixed to each line of changelog entries
|
185
|
+
# Quotes only necessary here to prevent # being interpreted as the beginning of a YAML comment
|
186
|
+
:comment_prefix: "#"
|
187
|
+
# Sequence of build steps -- each item should be a valid shell command, prefixed with the YAML sequence item token, '- '
|
188
|
+
:build_steps:
|
189
|
+
- bundle exec rake install
|
190
|
+
# Sequence defining the files (build artefacts) to package in the release archive.
|
191
|
+
# Each artefact definition must include a :src: key/value pair.
|
192
|
+
# An optional :dest: value may be provided to rename the file during packaging, or just (as in the first entry below)
|
193
|
+
# to flatten the folder structure.
|
194
|
+
# Any <VER> (or <REV>) in the :src: or :dest: placeholders wil be replaced with the current revision ID
|
195
|
+
# The revision archive will also include the revision history extracted as a text file
|
196
|
+
:artefacts:
|
197
|
+
# A binary artefact
|
198
|
+
- :src: pkg/revision-<VER>.gem
|
199
|
+
:dest: revision-<VER>.gem
|
200
|
+
# ':dest:' defaults to the string specified for ':src:' if not specified explicitly
|
201
|
+
# md5sums are generated by default for each artefact -- the ':md5:' option allows this to be disabled per-artefact
|
202
|
+
- :src: README.org
|
203
|
+
:md5: false
|
199
204
|
#+END_SRC
|
200
205
|
|
201
206
|
**** TODO (or at least consider) add support for overridable defaults
|
@@ -213,32 +218,32 @@ managing some embedded C projects, and the default values reflect this.
|
|
213
218
|
#+END_NOTE
|
214
219
|
|
215
220
|
#+BEGIN_SRC yaml
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
221
|
+
:releasables:
|
222
|
+
- :id: mbt_cd_firmware
|
223
|
+
:revision:
|
224
|
+
:src: src/FirmwareRevision.c
|
225
|
+
:build_steps:
|
226
|
+
- make --jobs=8 -f Makefile CONF=bootloadable
|
227
|
+
:artefacts:
|
228
|
+
- :src: dist/bootloadable/production/firmware.production.hex
|
229
|
+
:dest: mbt_cd_firmware_v<REV>.bootloadable.hex
|
230
|
+
- :src: dist/default/production/firmware.production.hex
|
231
|
+
:dest: mbt_cd_firmware_v<REV>.standalone.hex
|
227
232
|
#+END_SRC
|
228
233
|
|
229
234
|
**** Ruby project
|
230
235
|
|
231
236
|
#+BEGIN_SRC yaml
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
237
|
+
:releasables:
|
238
|
+
- :id: revision
|
239
|
+
:revision:
|
240
|
+
:src: lib/revision/version.rb
|
241
|
+
:regex: (?<prefix>VERSION = ")(?<major>\d+)(?<sep1>\.)(?<minor>\d+)(?<sep2>\.)(?<patch>\d+)(?<postfix>")
|
242
|
+
:comment_prefix: "#"
|
243
|
+
:build_steps:
|
244
|
+
- bundle exec rake install
|
245
|
+
:artefacts:
|
246
|
+
- :src: pkg/revision-<VER>.gem
|
242
247
|
#+END_SRC
|
243
248
|
|
244
249
|
*** Heirarchical project
|
@@ -250,7 +255,7 @@ at that root could include them as follows...
|
|
250
255
|
#+BEGIN_SRC yaml
|
251
256
|
:releasables:
|
252
257
|
- :folder: examples/c
|
253
|
-
|
258
|
+
- :folder: examples/ruby
|
254
259
|
#+END_SRC
|
255
260
|
|
256
261
|
**** TODO consider supporting a higher-level aggregate revision ID
|
@@ -260,7 +265,7 @@ at that root could include them as follows...
|
|
260
265
|
:src: release_log.txt
|
261
266
|
:releasables:
|
262
267
|
- :folder: examples/c
|
263
|
-
|
268
|
+
- :folder: examples/ruby
|
264
269
|
#+END_SRC
|
265
270
|
|
266
271
|
* Development
|
data/lib/revision/cli.rb
CHANGED
@@ -4,6 +4,7 @@ require_relative 'releasable'
|
|
4
4
|
require_relative 'info'
|
5
5
|
require_relative 'errors'
|
6
6
|
require_relative 'version'
|
7
|
+
require_relative 'md5'
|
7
8
|
|
8
9
|
module Revision
|
9
10
|
class CLI < Thor
|
@@ -105,6 +106,21 @@ module Revision
|
|
105
106
|
select_one.tag
|
106
107
|
end
|
107
108
|
|
109
|
+
desc 'md5', 'Compute md5sums for current build artefacts'
|
110
|
+
method_option :file, :aliases => "-f", :type => :string, :default => nil ,:desc => "File to md5sum (defaults to build artefacts defined in yaml)"
|
111
|
+
def md5
|
112
|
+
r = select_one
|
113
|
+
files = options[:file].nil? ?
|
114
|
+
r.artefacts.select { |a| a[:md5]==true }.map { |a| a[:src] } :
|
115
|
+
[options[:file]]
|
116
|
+
raise "No files specified" unless files.length
|
117
|
+
puts "Calculating md5sum for files #{files}"
|
118
|
+
for f in files
|
119
|
+
md5sum = Revision::MD5.from_file(f)
|
120
|
+
puts "#{md5sum}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
108
124
|
private
|
109
125
|
|
110
126
|
def id_options
|
data/lib/revision/md5.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'digest'
|
2
|
+
|
3
|
+
class Revision::MD5
|
4
|
+
|
5
|
+
READ_CHUNK_KB = 1024
|
6
|
+
FILENAME_EXTENSION = "md5"
|
7
|
+
|
8
|
+
attr_reader :root, :filename
|
9
|
+
|
10
|
+
def self.from_file(filepath, filename: nil)
|
11
|
+
raise "File #{filepath} not found" unless File.exist?(filepath)
|
12
|
+
filename ||= File.basename(filepath)
|
13
|
+
root = File.dirname(filepath)
|
14
|
+
stream = File.open(filepath, 'rb')
|
15
|
+
new(stream, filename, root: root)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(ioreader, filename, root: nil)
|
19
|
+
root ||= Dir.getwd
|
20
|
+
@reader = ioreader
|
21
|
+
@root = root
|
22
|
+
@filename = filename
|
23
|
+
end
|
24
|
+
|
25
|
+
def calc
|
26
|
+
md5 = Digest::MD5.new
|
27
|
+
bytes_per_chunk = READ_CHUNK_KB*1024
|
28
|
+
while chunk = @reader.read(bytes_per_chunk)
|
29
|
+
md5 << chunk
|
30
|
+
end
|
31
|
+
md5.hexdigest
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
<<~EOT
|
36
|
+
#{calc} #{@filename}
|
37
|
+
EOT
|
38
|
+
end
|
39
|
+
|
40
|
+
def md5filename
|
41
|
+
"#{@filename}.#{FILENAME_EXTENSION}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def write(filepath: nil)
|
45
|
+
filepath ||= File.join(@root, md5filename)
|
46
|
+
filename = File.basename(filepath)
|
47
|
+
File.open(filepath, "w") { |f| f.write "#{calc} #{filename}" }
|
48
|
+
filepath
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
data/lib/revision/releasable.rb
CHANGED
@@ -68,7 +68,8 @@ module Revision
|
|
68
68
|
# Legacy definition syntax compatibility
|
69
69
|
@build_def = config[:build] ? config[:build] : { environment: { variables: {}}, steps: config[:build_steps]}
|
70
70
|
@artefacts = config[:artefacts] || []
|
71
|
-
@artefacts.each { |a| a[:dest] ||= a[:src] } unless @artefacts.nil? || @artefacts.empty?
|
71
|
+
@artefacts.each { |a| a[:dest] ||= File.basename(a[:src]) } unless @artefacts.nil? || @artefacts.empty?
|
72
|
+
# @artefacts.each { |a| a[:md5] = true if a[:md5].nil? } unless @artefacts.nil? || @artefacts.empty?
|
72
73
|
@config = config
|
73
74
|
end
|
74
75
|
|
@@ -187,46 +188,61 @@ module Revision
|
|
187
188
|
"#{@id}_revision_history_v#{@revision}.txt"
|
188
189
|
end
|
189
190
|
|
190
|
-
def
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
191
|
+
def interp_rev(string)
|
192
|
+
string.gsub(REVISION_PLACEHOLDER, @revision.to_s)
|
193
|
+
end
|
194
|
+
|
195
|
+
def normalise_artefact(a)
|
196
|
+
src_norm = interp_rev(a[:src])
|
197
|
+
a_norm = {
|
198
|
+
src: src_norm,
|
199
|
+
dest: a[:dest].nil? ? File.basename(src_norm) : interp_rev(a[:dest]),
|
200
|
+
md5: a[:md5].nil? ? true : a[:md5]
|
201
|
+
}
|
202
|
+
if Gem.win_platform? && !a_norm[:src].end_with?('.exe') && File.exist?(File.join(@root, a_norm[:src] + '.exe'))
|
203
|
+
puts "... windows platform -- appending '.exe' ('#{a_norm[:src]}' -> '#{a_norm[src]}.exe')"
|
204
|
+
a_norm[:src] += '.exe'
|
205
|
+
a_norm[:dest] += '.exe' unless a_norm[:dest].end_with?('.exe')
|
203
206
|
end
|
204
|
-
|
207
|
+
a_norm
|
208
|
+
end
|
209
|
+
|
210
|
+
def artefacts
|
211
|
+
@artefacts.map { |a| normalise_artefact(a)}
|
205
212
|
end
|
206
213
|
|
207
214
|
def archive
|
208
215
|
puts "Archiving #{@artefacts.length} build artefacts as #{archive_name}..."
|
209
|
-
amap = artefact_map
|
210
216
|
if File.exist?(archive_name)
|
211
217
|
puts "... deleting existing archive"
|
212
218
|
File.delete(archive_name)
|
213
219
|
end
|
214
220
|
Zip::File.open(archive_name, Zip::File::CREATE) do |zipfile|
|
215
|
-
|
216
|
-
|
217
|
-
#
|
218
|
-
|
219
|
-
|
221
|
+
zip_entries = artefacts
|
222
|
+
zip_entries.each.with_index(1) do |a, idx|
|
223
|
+
puts "... (#{idx}/#{zip_entries.length}) #{a[:dest]} :: <= #{a[:src]}"
|
224
|
+
zipfile.add(a[:dest], a[:src])
|
225
|
+
if a[:md5]
|
226
|
+
md5name = "#{a[:dest]}.md5"
|
227
|
+
puts "... (#{idx}/#{zip_entries.length}) #{a[:dest]} :: embedding md5sum (#{md5name})"
|
228
|
+
zipfile.get_output_stream(md5name) { |os| os.write("#{MD5.from_file(a[:src])}")}
|
229
|
+
else
|
230
|
+
puts "... (#{idx}/#{zip_entries.length}) #{a[:dest]} :: no md5sum required"
|
231
|
+
end
|
220
232
|
end
|
221
233
|
puts "... embedding revision history as #{changelog_name} "
|
222
234
|
zipfile.get_output_stream(changelog_name) { |os| output_changelog(os)}
|
223
235
|
end
|
236
|
+
archive_md5 = MD5.from_file(archive_name)
|
237
|
+
puts "... generating archive md5sum as #{archive_md5.md5filename} "
|
238
|
+
archive_md5.write
|
224
239
|
|
225
240
|
if @config.dig(:archive)
|
226
241
|
archive_root = File.expand_path(@config[:archive])
|
227
242
|
puts "... moving #{archive_name} to #{archive_root}"
|
228
243
|
FileUtils.mkdir_p(archive_root)
|
229
244
|
FileUtils.mv(archive_name, archive_root)
|
245
|
+
FileUtils.mv(archive_md5.md5filename, archive_root)
|
230
246
|
end
|
231
247
|
end
|
232
248
|
|
@@ -244,12 +260,6 @@ module Revision
|
|
244
260
|
|
245
261
|
raise Errors::NotSpecified.new(':deploy') if destinations.empty?
|
246
262
|
|
247
|
-
#... Eliminated global deployment pre/post functions.
|
248
|
-
#... if applicable to all dests, the logic should probably be a build step...
|
249
|
-
# if @config.dig(:deploy, :pre)
|
250
|
-
# exec_pipeline('deploy (pre)', @config[:deploy][:pre])
|
251
|
-
# end
|
252
|
-
|
253
263
|
destinations.each do |d|
|
254
264
|
destination = File.expand_path(d[:dest])
|
255
265
|
|
@@ -257,21 +267,23 @@ module Revision
|
|
257
267
|
exec_pipeline('deploy (pre / #{d[:dest]})', d[:pre])
|
258
268
|
end
|
259
269
|
|
260
|
-
puts "Deploying #{
|
270
|
+
puts "Deploying #{artefacts.length} build artefacts to #{destination}..."
|
261
271
|
if not File.exist?(destination)
|
262
272
|
puts "... folder not found -> creating ... '#{destination}'"
|
263
273
|
FileUtils.mkdir_p(destination)
|
264
274
|
end
|
265
|
-
|
266
|
-
|
267
|
-
src
|
268
|
-
|
275
|
+
artefacts.each.with_index(1) do |a, idx|
|
276
|
+
# src, dest = entry
|
277
|
+
src = File.join(@root,a[:src])
|
278
|
+
dest = destination.empty? ? a[:dest] : File.join(destination, a[:dest])
|
279
|
+
puts "... (#{idx}/#{artefacts.length}) #{src} => #{dest}"
|
269
280
|
if File.exist?(dest)
|
270
281
|
puts "... deleting existing '#{dest}' ..."
|
271
282
|
FileUtils.rm_rf(dest)
|
272
283
|
end
|
273
|
-
puts "... deploying '#{src}' -> '#{dest}"
|
284
|
+
puts "... deploying '#{src}' -> '#{dest}'"
|
274
285
|
FileUtils.cp_r(src,dest)
|
286
|
+
puts "... writing md5sum for '#{dest}' to '#{MD5.from_file(dest).write}'" if a[:md5]
|
275
287
|
end
|
276
288
|
File.open(File.join(destination,changelog_name),'w') { |f| output_changelog(f)}
|
277
289
|
|
@@ -280,9 +292,6 @@ module Revision
|
|
280
292
|
end
|
281
293
|
end
|
282
294
|
|
283
|
-
# if @config.dig(:deploy, :post)
|
284
|
-
# exec_pipeline('deploy (post)', @config[:deploy][:post])
|
285
|
-
# end
|
286
295
|
end
|
287
296
|
|
288
297
|
def package
|
data/lib/revision/version.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
# Defines the revision ID for the revision gem
|
2
2
|
module Revision
|
3
|
-
VERSION = "1.
|
3
|
+
VERSION = "1.6.0".freeze
|
4
4
|
end
|
5
5
|
|
6
6
|
# <BEGIN CHANGELOG>
|
7
7
|
#
|
8
|
+
# Version 1.6.0 (01 Dec 2021)
|
9
|
+
# - New feature: Automated MD5sum generation during 'archive' and 'deploy' tasks
|
10
|
+
#
|
8
11
|
# Version 1.5.3 (26 Oct 2021)
|
9
12
|
# - Multiple deployment destinations bugfix -- only last destination was being used.
|
10
13
|
#
|
data/releasables.yaml
CHANGED
@@ -5,6 +5,17 @@
|
|
5
5
|
:regex: (?<prefix>VERSION = ")(?<major>\d+)(?<sep1>\.)(?<minor>\d+)(?<sep2>\.)(?<patch>\d+)(?<postfix>".freeze)
|
6
6
|
:comment_prefix: "#"
|
7
7
|
:build_steps:
|
8
|
+
- mkdir -p test-output/2
|
8
9
|
- bundle exec rake install
|
10
|
+
#Define 3 build artefacts as test cases for 'deploy' and 'archive' tasks
|
11
|
+
#Expect MD5 file to be generated by default for first entry (where not specified explicitly)
|
9
12
|
:artefacts:
|
10
13
|
- :src: pkg/revision-<VER>.gem
|
14
|
+
- :src: README.org
|
15
|
+
:md5: false
|
16
|
+
- :src: Rakefile
|
17
|
+
:md5: true
|
18
|
+
#Define two deployment destinations to verify multiple deployments work as intended
|
19
|
+
:deploy:
|
20
|
+
- :dest: ./test-output
|
21
|
+
- :dest: ./test-output/2
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: revision
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cormac Cannon
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-12-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -201,6 +201,7 @@ files:
|
|
201
201
|
- lib/revision/cli.rb
|
202
202
|
- lib/revision/errors.rb
|
203
203
|
- lib/revision/info.rb
|
204
|
+
- lib/revision/md5.rb
|
204
205
|
- lib/revision/releasable.rb
|
205
206
|
- lib/revision/string_case.rb
|
206
207
|
- lib/revision/version.rb
|