class Origen::Application::Release

Responsible for co-ordinating application releases

Public Class Methods

new() click to toggle source
# File lib/origen/application/release.rb, line 11
def initialize
  @mailer = Utility::Mailer.new
end
valid_types() click to toggle source
# File lib/origen/application/release.rb, line 15
def self.valid_types
  if Origen.config.semantically_version
    if Origen.app.version.development?
      [:development, :production]
    else
      [:development, :tiny, :minor, :major]
    end
  else
    [:development, :production]
  end
end

Public Instance Methods

run(options) click to toggle source

Release a new application version

# File lib/origen/application/release.rb, line 28
      def run(options)
        # Don't want plugin callbacks kicking in during the release process
        Origen.app.plugins.disable_current
        @options = options
        if authorized?
          Origen.app.plugins.validate_production_status(true)
          fail 'No revision control configured for this application, cannot release a new version' if Origen.app.rc.nil?

          unless Origen.app.rc.local_modifications.empty?
            puts <<-EOT
Your workspace has local modifications that are preventing the requested action
  - run 'origen rc mods' to see them.
            EOT
            exit 1
          end
          get_release_type unless type
          validate_branch
          # For development tags always include the latest updates from everyone else,
          # for production tag exactly what is in the user's workspace
          if type == :development && !options[:no_fetch]
            puts 'Fetching the latest version of all application files...'
            Origen.app.rc.checkout(force: true)
          end
          Origen.app.listeners_for(:validate_release).each(&:validate_release)
          lint_test
          get_latest_version_files
          base_version = Origen.app.version(refresh: true) # Read in the latest version
          get_release_note unless note
          new_version = get_or_confirm_version(type) # Don't mask this like the above!
          write_version(new_version)
          # Refresh the version in the current thread
          Origen.app.version(refresh: true)
          if Origen.app.version != new_version
            fail "Sorry something has gone wrong trying to update the version counter to #{new_version}!"
          end

          Origen.app.version_tracker.add_version(Origen.app.version) if Origen.app.version.production?
          write_history
          Origen.app.listeners_for(:before_release_tag).each do |listener|
            listener.before_release_tag(tag, note, type, selector, options)
          end
          puts "Tagging workspace, this could take a few minutes, don't interrupt..."
          Origen.app.rc.checkin("#{version_file} #{history_file} #{File.join(Origen.root, 'Gemfile.lock')}", force: true, comment: 'Updated app version and history')
          workspace_dirs.each do |wdir|
            Origen.app.rc.tag tag
          end
          begin
            Origen.client.release!
          rescue
            # Don't allow any server post errors to kill the release process
          end
          release_gem
          Origen.app.listeners_for(:after_release_tag).each do |listener|
            listener.after_release_tag(tag, note, type, selector, options)
          end
          @mailer.send_release_notice(tag, note, type, selector) unless options[:silent]
          Origen.app.listeners_for(:after_release_email).each do |listener|
            listener.after_release_email(tag, note, type, selector, options)
          end
          puts "Successfully released version #{Origen.app.version}!"
        end
      end

Protected Instance Methods

authorized?() click to toggle source

Only allows admins to release

# File lib/origen/application/release.rb, line 343
def authorized?
  # Not sure yet if this concept can work in the open source world...
  true
  # if User.current.admin?
  #  true
  # else
  #  puts "Sorry but you can't run the release script, please contact a development team member"
  #  false
  # end
end
get_latest_version_files() click to toggle source

Pull the latest versions of the history and version ID files into the workspace

# File lib/origen/application/release.rb, line 177
def get_latest_version_files
  # Get the latest version of the version and history files
  if Origen.app.rc.dssc?
    # Legacy code that makes use of the fact that DesignSync can handle different branch selectors
    # for individual files, this capability has not been abstracted by the newer object oriented
    # revision controllers since most others cannot do it
    system "dssc co -get -force \"#{history_file};Trunk:Latest\" #{version_file}"
    # Force the history selector to Trunk, only 1 branch of this file should ever exist
    system "dssc setselector Trunk #{history_file}"
    # Make sure both are writable
    if Origen.running_on_windows?
      `attrib -R #{history_file.gsub('/', '\\')} #{version_file.gsub('/', '\\')}`
    else
      `chmod 666 #{history_file} #{version_file}`
    end
  else
    Origen.app.rc.checkout "#{history_file} #{version_file}", force: true
  end
end
get_or_confirm_version(type) click to toggle source

Prompts the user to confirm the proposed version or enter a different one

# File lib/origen/application/release.rb, line 236
def get_or_confirm_version(type)
  proposed = nil
  until version_unique?(proposed)
    proposed ||= Origen.app.version
    if type == :development
      proposed = proposed.next_dev
    else
      proposed = proposed.next_prod(type)
    end
  end
  if Origen.config.semantically_version
    puts
    puts 'HAPPY TO RELEASE THIS VERSION?:'
    unless get_text(default: proposed, confirm: :return_boolean)
      valid = false
      puts
      until valid
        puts
        puts 'ENTER THE VERSION NUMBER YOU WOULD LIKE TO USE INSTEAD:'
        proposed = Origen::VersionString.new(get_text(single: true).strip)
        valid = proposed.semantic?
        unless valid
          puts 'SORRY BUT THAT IS NOT A VALID VERSION NUMBER'
        end
      end
    end
  else
    valid = false
    until valid
      puts
      puts 'RELEASE TAG:'
      proposed = Origen::VersionString.new(get_text(default: proposed, single: true).strip)
      valid = proposed.timestamp?
      unless valid
        puts 'SORRY BUT THAT IS NOT A VALID VERSION NUMBER'
      end
    end
  end
  proposed
end
get_release_note() click to toggle source

Prompts the user to enter a release note

# File lib/origen/application/release.rb, line 198
def get_release_note
  puts ''
  if @options[:note_file]
    f = Origen.file_handler.clean_path_to(@options[:note_file])
  else
    f = "#{Origen.root}/release_note.txt"
  end
  if File.exist?(f)
    lines = File.readlines(f)
    puts 'HAPPY TO USE THIS RELEASE NOTE?:'
    puts '------------------------------------------------------------------------------------------'
    lines.each { |l| puts l }
    puts '------------------------------------------------------------------------------------------'
    if get_text(confirm: :return_boolean)
      self.note = lines.join('')
      return if note
    end
  end
  puts 'RELEASE NOTE:'
  self.note = get_text
  unless note
    puts 'Sorry but you must supply at least a minor description for this release!'
    get_release_note
  end
end
get_release_type() click to toggle source

Prompts the user to enter a release type

# File lib/origen/application/release.rb, line 225
def get_release_type
  puts ''
  puts "RELEASE TYPE (#{self.class.valid_types.join(', ')}):"
  self.type = get_text(default: self.class.valid_types.first, single: true)
  unless type
    puts 'Sorry but that release type is not valid!'
    get_release_type
  end
end
history_file() click to toggle source
# File lib/origen/application/release.rb, line 164
def history_file
  Origen.config.history_file
end
lint_test() click to toggle source

Run the lint test and require that it passes if enabled

# File lib/origen/application/release.rb, line 122
def lint_test
  if Origen.config.lint_test[:run_on_tag]
    unless system('origen lint --no-correct')
      puts ''
      puts "Can't release due to lint/style errors, fix any errors from running 'origen lint' before trying again"
      exit 1
    end
  end
end
note() click to toggle source
# File lib/origen/application/release.rb, line 151
def note
  @note ||= @options[:note]
end
note=(note) click to toggle source

Sets the release note, returns nil if invalid

# File lib/origen/application/release.rb, line 143
def note=(note)
  if note.to_s =~ /[a-f]|[A-F]/
    @note = note
  else
    @note = nil
  end
end
release_gem() click to toggle source
# File lib/origen/application/release.rb, line 107
def release_gem
  if File.exist?(File.join(Origen.root, "#{Origen.app.gem_name}.gemspec"))
    Origen.app.listeners_for(:before_release_gem).each(&:before_release_gem)
    unless system 'rake gem:release'
      puts '***************************************'.red
      puts '***************************************'.red
      puts 'SOMETHING WENT WRONG RELEASING THE GEM!'.red
      puts '***************************************'.red
      puts '***************************************'.red
    end
    Origen.app.listeners_for(:after_release_gem).each(&:after_release_gem)
  end
end
selector() click to toggle source

Returns the selector, formatted as a string

# File lib/origen/application/release.rb, line 156
def selector
  "Branch: '#{Origen.app.rc.current_branch}'"
end
tag() click to toggle source

For now apply a leading ā€˜v’ to keep design sync happy, will make this CM type specific in future

# File lib/origen/application/release.rb, line 295
def tag
  tag = Origen.app.version
  if tag =~ /^\d/
    Origen.app.config.rc_tag_prepend_v ? "v#{tag}" : tag
  else
    tag
  end
end
type() click to toggle source
# File lib/origen/application/release.rb, line 138
def type
  @type ||= @options[:type]
end
type=(type) click to toggle source

Sets the current release type

# File lib/origen/application/release.rb, line 133
def type=(type)
  type = type.to_s.chomp.downcase.to_sym
  @type = self.class.valid_types.include?(type) ? type : nil
end
validate_branch() click to toggle source
# File lib/origen/application/release.rb, line 93
def validate_branch
  if Origen.app.rc.git?
    if Origen.app.config.rc_workflow == :gitflow
      if type == :development && Origen.app.rc.current_branch == 'master'
        puts "Development releases can only be made on the develop branch, your current branch is: #{Origen.app.rc.current_branch}"
        exit 1
      elsif type == :production && Origen.app.rc.current_branch != 'master'
        puts "Production releases can only be made on the master branch, your current branch is: #{Origen.app.rc.current_branch}"
        exit 1
      end
    end
  end
end
version_file() click to toggle source
# File lib/origen/application/release.rb, line 160
def version_file
  File.join(Origen.root, 'config', 'version.rb')
end
version_unique?(version) click to toggle source
# File lib/origen/application/release.rb, line 277
def version_unique?(version)
  if version
    if version.semantic?
      if Origen.app.rc.git?
        !Origen.app.rc.tag_exists?(version.prefixed)
      else
        # Don't worry about dssc, since a single branch is used for the version it will
        # always be unique
        true
      end
    else
      true
    end
  end
end
workspace_dirs() click to toggle source
# File lib/origen/application/release.rb, line 168
def workspace_dirs
  dirs = ["#{Origen.root}"]
  Origen.app.config.external_app_dirs.each do |edir|
    dirs << edir
  end
  dirs
end
write_history() click to toggle source

Write out details of the given version to the history file

# File lib/origen/application/release.rb, line 305
def write_history
  text = File.read(history_file)           # Read the existing contents
  File.open(history_file, 'w') do |file|   # Write them back, substituting the version number as required
    if Origen.app.version.production?
      file.puts "<a class=\"anchor release_tag\" name=\"#{tag.gsub('.', '_')}\"></a>"
      file.puts "<h1><a href=\"##{tag.gsub('.', '_')}\">Tag: #{tag}</a></h1>"
      file.puts ''
      file.puts "##### #{selector}".escape_underscores
      file.puts ''
      file.puts "##### by #{User.current.name} on #{time_now}"
      file.puts ''
    else
      file.puts "<a class=\"anchor release_tag\" name=\"#{tag.gsub('.', '_')}\"></a>"
      file.puts "<h2><a href=\"##{tag.gsub('.', '_')}\">Tag: #{tag}</a></h2>"
      file.puts ''
      file.puts "##### #{selector}".escape_underscores
      file.puts ''
      file.puts "##### by #{User.current.name} on #{time_now}"
      file.puts ''
    end
    file.puts ''
    file.puts note.escape_underscores(smartly = true)
    file.puts ''
    file.puts text
  end
end
write_version(version) click to toggle source

Sets the version number in the file store

# File lib/origen/application/release.rb, line 333
def write_version(version)
  if version.semantic?
    Origen::CodeGenerators.invoke_internal 'semver', [], config: { change: version }
  else
    Origen::CodeGenerators.invoke_internal 'timever', [], config: { change: version }
  end
  system 'origen -v' # Invoke Origen under the new version, this updates Gemfile.lock
end