Module: GitVersionBump

Defined in:
lib/git-version-bump.rb,
lib/git-version-bump/version.rb

Defined Under Namespace

Classes: VersionUnobtainable

Constant Summary collapse

VERSION =
GVB.version
MAJOR_VERSION =
GVB.major_version
MINOR_VERSION =
GVB.minor_version
PATCH_VERSION =
GVB.patch_version
INTERNAL_REVISION =
GVB.internal_revision
DATE =
GVB.date

Class Method Summary collapse

Class Method Details

.caller_fileObject



21
22
23
24
25
26
27
28
29
30
31
# File 'lib/git-version-bump.rb', line 21

def self.caller_file
  # Who called us?  Because this method gets called from other methods
  # within this file, we can't just look at Gem.location_of_caller, but
  # instead we need to parse the caller stack ourselves to find which
  # gem we're trying to version all over.
  Pathname(
    caller.
    map  { |l| l.split(':')[0] }.
    find { |l| l != __FILE__ }
  ).realpath.to_s rescue nil
end

.caller_gemspecObject



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/git-version-bump.rb', line 33

def self.caller_gemspec
  cf = caller_file or return nil

  # Grovel through all the loaded gems to try and find the gem
  # that contains the caller's file.
  Gem.loaded_specs.values.each do |spec|
    if (Dir.glob(spec.lib_dirs_glob) + Dir["#{spec.bin_dir}/*"]).
         find { |d| cf.index(Pathname(d).realpath.to_s) == 0 }
      # The caller_file is in this
      # gem!  Woohoo!
      return spec
    end
  end

  raise VersionUnobtainable,
        "Unable to find gemspec for caller file #{cf}"
end

.date(use_local_git = false) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/git-version-bump.rb', line 146

def self.date(use_local_git=false)
  if use_local_git
    unless git_available?
      raise RuntimeError,
            "GVB.date(use_local_git=true), but git is not installed"
    end

    sq_git_dir = "'#{Dir.pwd.gsub("'", "'\\''")}'"
  else
    # Shell Quoted, for your convenience
    sq_git_dir = "'" + (File.dirname(caller_file) rescue nil || Dir.pwd).gsub("'", "'\\''") + "'"
  end

  # Are we in a git tree?
  system("git -C #{sq_git_dir} status >/dev/null 2>&1")
  if $? == 0
    # Yes, we're in git.

    if dirty_tree?
      return Time.now.strftime("%F")
    else
      # Clean tree.  Date of last commit is needed.
      return `git -C #{sq_git_dir} show --format=format:%ad --date=short | head -n 1`.strip
    end
  else
    if use_local_git
      raise RuntimeError,
            "GVB.date(use_local_git=true) called from non-git location"
    end

    # Not in git; time to hit the gemspecs
    if spec = caller_gemspec
      return spec.date.strftime("%F")
    end

    raise RuntimeError,
        "GVB.date called from mysterious, non-gem location."
  end
end

.dirty_tree?Boolean

Returns:

  • (Boolean)


14
15
16
17
18
19
# File 'lib/git-version-bump.rb', line 14

def self.dirty_tree?
  # Are we in a dirty, dirty tree?
  system("! git diff --no-ext-diff --quiet --exit-code || ! git diff-index --cached --quiet HEAD")

  $? == 0
end

.git_available?Boolean

Returns:

  • (Boolean)


8
9
10
11
12
# File 'lib/git-version-bump.rb', line 8

def self.git_available?
  system("git --version >/dev/null 2>&1")

  $? == 0
end

.internal_revision(use_local_git = false) ⇒ Object



142
143
144
# File 'lib/git-version-bump.rb', line 142

def self.internal_revision(use_local_git=false)
  version(use_local_git).split('.', 4)[3].to_s
end

.major_version(use_local_git = false) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
# File 'lib/git-version-bump.rb', line 106

def self.major_version(use_local_git=false)
  ver = version(use_local_git)
  v   = ver.split('.')[0]

  unless v =~ /^[0-9]+$/
    raise ArgumentError,
            "#{v} (part of #{ver.inspect}) is not a numeric version component.  Abandon ship!"
  end

  return v.to_i
end

.minor_version(use_local_git = false) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
# File 'lib/git-version-bump.rb', line 118

def self.minor_version(use_local_git=false)
  ver = version(use_local_git)
  v   = ver.split('.')[1]

  unless v =~ /^[0-9]+$/
    raise ArgumentError,
            "#{v} (part of #{ver.inspect}) is not a numeric version component.  Abandon ship!"
  end

  return v.to_i
end

.patch_version(use_local_git = false) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
# File 'lib/git-version-bump.rb', line 130

def self.patch_version(use_local_git=false)
  ver = version(use_local_git)
  v   = ver.split('.')[2]

  unless v =~ /^[0-9]+$/
    raise ArgumentError,
            "#{v} (part of #{ver.inspect}) is not a numeric version component.  Abandon ship!"
  end

  return v.to_i
end

.tag_version(v, release_notes = false) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/git-version-bump.rb', line 186

def self.tag_version(v, release_notes = false)
  if dirty_tree?
    puts "You have uncommitted files.  Refusing to tag a dirty tree."
  else
    if release_notes
      # We need to find the tag before this one, so we can list all the commits
      # between the two.  This is not a trivial operation.
      prev_tag = `git describe --always`.strip.gsub(/-\d+-g[0-9a-f]+$/, '')

      log_file = Tempfile.new('gvb')

      log_file.puts "\n\n\n        # Write your release notes above.  The first line should be the release name.\n        # To help you remember what's in here, the commits since your last release\n        # are listed below. This will become v\#{v}\n        #\n      EOF\n\n      log_file.close\n      system(\"git log --format='# %h  %s' \#{prev_tag}..HEAD >>\#{log_file.path}\")\n\n      pre_hash = Digest::SHA1.hexdigest(File.read(log_file.path))\n      system(\"git config -e -f \#{log_file.path}\")\n      if Digest::SHA1.hexdigest(File.read(log_file.path)) == pre_hash\n        puts \"Release notes not edited; aborting\"\n        log_file.unlink\n        return\n      end\n\n      puts \"Tagging version \#{v}...\"\n      system(\"git tag -a -F \#{log_file.path} v\#{v}\")\n      log_file.unlink\n    else\n      # Crikey this is a lot simpler\n      system(\"git tag -a -m 'Version v\#{v}' v\#{v}\")\n    end\n\n    system(\"git push >/dev/null 2>&1\")\n    system(\"git push --tags >/dev/null 2>&1\")\n  end\nend\n".gsub(/^\t\t\t\t\t/, '')

.version(use_local_git = false) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/git-version-bump.rb', line 51

def self.version(use_local_git=false)
  if use_local_git
    unless git_available?
      raise RuntimeError,
            "GVB.version(use_local_git=true) called, but git isn't installed"
    end

    sq_git_dir = "'#{Dir.pwd.gsub("'", "'\\''")}'"
  else
    # Shell Quoted, for your convenience
    sq_git_dir = "'" + (File.dirname(caller_file) rescue nil || Dir.pwd).gsub("'", "'\\''") + "'"
  end

  git_ver = `git -C #{sq_git_dir} describe --dirty='.1.dirty.#{Time.now.strftime("%Y%m%d.%H%M%S")}' --match='v[0-9]*.[0-9]*.*[0-9]' 2>/dev/null`.
              strip.
              gsub(/^v/, '').
              gsub('-', '.')

  # If git returned success, then it gave us a described version.
  # Success!
  return git_ver if $? == 0

  # git failed us; we're either not in a git repo or else we've never
  # tagged anything before.

  # Are we in a git repo with no tags?  If so, dump out our
  # super-special version and be done with it.
  system("git -C #{sq_git_dir} status >/dev/null 2>&1")
  return "0.0.0.1.ENOTAG" if $? == 0

  # We're not in a git repo.  This means that we need to get version
  # information out of rubygems, given only the filename of who called
  # us.  This takes a little bit of effort.

  if use_local_git
    raise VersionUnobtainable,
          "Unable to determine version from local git repo.  This should never happen."
  end

  if spec = caller_gemspec
    return spec.version.to_s
  else
    # If we got here, something went *badly* wrong -- presumably, we
    # weren't called from within a loaded gem, and so we've got *no*
    # idea what's going on.  Time to bail!
    if git_available?
      raise VersionUnobtainable,
            "GVB.version(#{use_local_git.inspect}) failed, and I really don't know why."
    else
      raise VersionUnobtainable,
            "GVB.version(#{use_local_git.inspect}) failed; perhaps you need to install git?"
    end
  end
end