class Omnibus::BuildVersion

Provides methods for generating Omnibus project build version strings automatically from Git repository information.

@see Omnibus::Project#build_version

@note Requires a Git repository

@todo Rename this class to reflect its absolute dependence on running in a

Git repository.

Constants

TIMESTAMP_FORMAT

Formatting string for the timestamp component of our SemVer build specifier.

@see Omnibus::BuildVersion#semver @see Time#strftime

Public Class Methods

build_start_time() click to toggle source
# File lib/omnibus/build_version.rb, line 50
def build_start_time
  new.build_start_time
end
git_describe() click to toggle source

@see (BuildVersion#git_describe)

# File lib/omnibus/build_version.rb, line 41
def git_describe
  new.git_describe
end
new(path = Config.project_root) click to toggle source

Create a new BuildVersion

@param [String] path

Path from which to read git version information
# File lib/omnibus/build_version.rb, line 59
def initialize(path = Config.project_root)
  @path = path
end
semver() click to toggle source

@see (BuildVersion#semver)

# File lib/omnibus/build_version.rb, line 46
def semver
  new.semver
end

Public Instance Methods

build_start_time() click to toggle source

We’ll attempt to retrieve the timestamp from the Jenkin’s set BUILD_TIMESTAMP or fall back to BUILD_ID environment variable. This will ensure platform specfic packages for the same build will share the same timestamp.

# File lib/omnibus/build_version.rb, line 141
def build_start_time
  @build_start_time ||= begin
                          if ENV["BUILD_TIMESTAMP"]
                            begin
                              Time.strptime(ENV["BUILD_TIMESTAMP"], "%Y-%m-%d_%H-%M-%S")
                            rescue ArgumentError
                              error_message =  "BUILD_TIMESTAMP environment variable "
                              error_message << "should be in YYYY-MM-DD_hh-mm-ss "
                              error_message << "format."
                              raise ArgumentError, error_message
                            end
                          elsif ENV["BUILD_ID"]
                            begin
                              Time.strptime(ENV["BUILD_ID"], "%Y-%m-%d_%H-%M-%S")
                            rescue ArgumentError
                              error_message =  "BUILD_ID environment variable "
                              error_message << "should be in YYYY-MM-DD_hh-mm-ss "
                              error_message << "format."
                              raise ArgumentError, error_message
                            end
                          else
                            Time.now.utc
                          end
                        end.strftime(TIMESTAMP_FORMAT)
end
commits_since_tag() click to toggle source

Extracts the number of commits since the most recent Git tag, as determined by {#git_describe}.

Here are some illustrative examples:

1.2.7-208-ge908a52 -> 208
11.0.0-alpha-59-gf55b180 -> 59
11.0.0-alpha2 -> 0
10.16.2.rc.1 -> 0

@return [Fixnum]

# File lib/omnibus/build_version.rb, line 262
def commits_since_tag
  commits_regexp = /^.*-(\d+)\-g[0-9a-f]+$/
  match = commits_regexp.match(git_describe)
  match ? match[1].to_i : 0
end
git_describe() click to toggle source

Generates a version string by running {www.kernel.org/pub/software/scm/git/docs/git-describe.html git describe} in the root of the Omnibus project.

Produces a version string of the format

MOST_RECENT_TAG-COMMITS_SINCE-gGIT_SHA

@example

11.0.0-alpha.1-207-g694b062

@return [String]

# File lib/omnibus/build_version.rb, line 178
def git_describe
  @git_describe ||= begin
    cmd = shellout("git describe --tags", cwd: @path)

    if cmd.exitstatus == 0
      cmd.stdout.chomp
    else
      log.warn(log_key) do
        "Could not extract version information from 'git describe'! " \
        "Setting version to 0.0.0."
      end
      "0.0.0"
    end
  end
end
git_sha_tag() click to toggle source

Extracts the 7-character truncated Git SHA1 from the output of {#git_describe}.

Here are some illustrative examples:

1.2.7-208-ge908a52 -> e908a52
11.0.0-alpha-59-gf55b180 -> f55b180
11.0.0-alpha2 -> nil
10.16.2.rc.1 -> nil

@return [String] the truncated SHA1 @return [nil] if no SHA1 is present in the output of #{git_describe}

# File lib/omnibus/build_version.rb, line 245
def git_sha_tag
  sha_regexp = /g([0-9a-f]+)$/
  match = sha_regexp.match(git_describe)
  match ? match[1] : nil
end
prerelease_tag() click to toggle source

Return a prerelease tag string (if it exists), as extracted from {#git_describe}.

Here are some illustrative examples:

1.2.7-208-ge908a52 -> nil
11.0.0-alpha-59-gf55b180 -> alpha
11.0.0-alpha2 -> alpha2
10.16.2.rc.1 -> rc.1

@return [String] if a pre-release tag was found @return [nil] if no pre-release tag was found

# File lib/omnibus/build_version.rb, line 224
def prerelease_tag
  prerelease_regex = if commits_since_tag > 0
                       /^v?\d+\.\d+\.\d+(?:-|\.)([0-9A-Za-z.-]+)-\d+-g[0-9a-f]+$/
                     else
                       /^v?\d+\.\d+\.\d+(?:-|\.)([0-9A-Za-z.-]+)$/
                     end
  match = prerelease_regex.match(git_describe)
  match ? match[1] : nil
end
prerelease_version?() click to toggle source

Indicates whether the version represents a pre-release or not, as signalled by the presence of a pre-release tag in the version string.

@return [Boolean] @see prerelease_tag

# File lib/omnibus/build_version.rb, line 274
def prerelease_version?
  if prerelease_tag
    true
  else
    false
  end
end
semver() click to toggle source

Generate a {semver.org/ SemVer 2.0.0-rc.1 compliant} version string for an Omnibus project.

This relies on the Omnibus project being a Git repository, as well as having tags named according to SemVer conventions (specifically, the ‘MAJOR.MINOR.PATCH-PRERELEASE` aspects)

The specific format of the version string is:

MAJOR.MINOR.PATCH-PRERELEASE+TIMESTAMP.git.COMMITS_SINCE.GIT_SHA

By default, a timestamp is incorporated into the build component of version string (see {Omnibus::BuildVersion::TIMESTAMP_FORMAT}). This option is configurable via the {Config}.

@example 11.0.0-alpha.1+20121218164140.git.207.694b062 @return [String] @see git_describe @todo Issue a warning or throw an exception if the tags of the

repository are not themselves SemVer-compliant?

@todo Consider making the {#build_start_time} method public, as

its function influences how build timestamps are generated,
and can be influenced by users.
# File lib/omnibus/build_version.rb, line 88
def semver
  build_tag = version_tag
  log.debug(log_key) { "#{self.class}##{__method__} - build tag: #{build_tag}" }

  # PRERELEASE VERSION
  log.debug(log_key) { "#{self.class}##{__method__} - prerelease_version?: #{prerelease_version?}" }

  if prerelease_version?
    # ensure all dashes are dots per precedence rules (#12) in Semver
    # 2.0.0-rc.1
    log.debug(log_key) { "#{self.class}##{__method__} - prerelease_tag: #{prerelease_tag}" }
    prerelease = prerelease_tag.tr("-", ".")
    build_tag << "-" << prerelease
    log.debug(log_key) { "#{self.class}##{__method__} - build_tag after prerelease: #{build_tag}" }
  end

  # BUILD VERSION
  # Follows SemVer conventions and the build version begins with a '+'.
  build_version_items = []

  # By default we will append a timestamp to every build. This behavior can
  # be overriden by setting the OMNIBUS_APPEND_TIMESTAMP environment
  # variable to a 'falsey' value (ie false, f, no, n or 0).
  #
  # format: YYYYMMDDHHMMSS example: 20130131123345
  if Config.append_timestamp
    log.debug(log_key) { "#{self.class}##{__method__} - build_start_time: #{build_start_time}" }
    build_version_items << build_start_time
  end

  # We'll append the git describe information unless we are sitting right
  # on an annotated tag.
  #
  # format: git.COMMITS_SINCE_TAG.GIT_SHA example: git.207.694b062
  unless commits_since_tag == 0
    log.debug(log_key) { "#{self.class}##{__method__} - commits_since_tag: #{commits_since_tag}" }
    log.debug(log_key) { "#{self.class}##{__method__} - git_sha_tag: #{git_sha_tag}" }
    build_version_items << ["git", commits_since_tag, git_sha_tag].join(".")
  end

  unless build_version_items.empty?
    log.debug(log_key) { "#{self.class}##{__method__} - build_version_items: #{build_version_items}" }
    build_tag << "-" << build_version_items.join(".")
  end

  log.debug(log_key) { "#{self.class}##{__method__} - final build_tag returned: #{build_tag}" }

  build_tag
end
version_tag() click to toggle source

Return a ‘MAJOR.MINOR.PATCH` version string, as extracted from {#git_describe}.

Here are some illustrative examples:

1.2.7-208-ge908a52 -> 1.2.7
11.0.0-alpha-59-gf55b180 -> 11.0.0
11.0.0-alpha2 -> 11.0.0
10.16.2.rc.1 -> 10.16.2

@return [String]

# File lib/omnibus/build_version.rb, line 209
def version_tag
  version_composition.join(".")
end

Private Instance Methods

version_composition() click to toggle source

Pulls out the major, minor, and patch components from the output of {#git_describe}.

Relies on the most recent Git tag being SemVer compliant (i.e., starting with a ‘MAJOR.MINOR.PATCH` string)

@return [Array<(String, String, String)>]

@todo Compute this once and store the result in an instance variable

# File lib/omnibus/build_version.rb, line 293
def version_composition
  version_regexp = /^v?(\d+)\.(\d+)\.(\d+)/

  if match = version_regexp.match(git_describe)
    match[1..3]
  else
    raise "Invalid semver tag `#{git_describe}'!"
  end
end