class Shaf::Upgrade::Package

Constants

IGNORE_FILE_PATTERNS
MANIFEST_FILENAME
UPGRADE_FILES_PATH

Attributes

manifest[R]
version[R]

Public Class Methods

all() click to toggle source
# File lib/shaf/upgrade/package.rb, line 30
def all
  target_versions.map(&method(:new))
end
load(version) click to toggle source
# File lib/shaf/upgrade/package.rb, line 34
def load(version)
  v = strip_suffix(version)
  raise VersionNotFoundError unless target_versions.include? v
  new(v).tap(&:load)
end
new(version, manifest = nil, files = {}) click to toggle source
# File lib/shaf/upgrade/package.rb, line 59
def initialize(version, manifest = nil, files = {})
  @version = Version.new(version)
  @manifest = manifest
  @files = files
end

Private Class Methods

strip_suffix(file) click to toggle source
# File lib/shaf/upgrade/package.rb, line 52
def strip_suffix(file)
  file.sub('.tar.gz', '')
end
target_versions() click to toggle source
# File lib/shaf/upgrade/package.rb, line 42
def target_versions
  return @target_versions if defined? @target_versions

  files = Dir[File.join(UPGRADE_FILES_PATH, '*.tar.gz')]
  @target_versions = files.map do |file|
    str = File.basename(file, '.tar.gz')
    Version.new(str)
  end.sort
end

Public Instance Methods

<=>(other) click to toggle source
# File lib/shaf/upgrade/package.rb, line 77
def <=>(other)
  version = other.is_a?(String) ? other : other.version
  @version <=> version
end
apply(dir = nil) click to toggle source
# File lib/shaf/upgrade/package.rb, line 82
def apply(dir = nil)
  puts "\n ## Applying changes for Shaf version: #{version} ##"

  FT.transaction do
    result = [
      apply_patches(dir),
      apply_drops(dir),
      apply_additions,
      apply_substitutes(dir),
      print_messages,
    ]
    raise UpgradeError, 'Upgrade failed' unless result.all?
  end

  true
end
load() click to toggle source
# File lib/shaf/upgrade/package.rb, line 65
def load
  File.open(tarball, 'rb') do |file|
    Zlib::GzipReader.wrap(file) do |gz|
      Gem::Package::TarReader.new(gz) do |tar|
        tar.each(&method(:add_tar_entry))
      end
    end
  end
  validate
  self
end
to_s() click to toggle source
# File lib/shaf/upgrade/package.rb, line 99
def to_s
  str = "Shaf::Upgrade::Package for version #{version}"
  return str if manifest.nil?
  "#{str} (#{manifest.stats_str})"
end

Private Instance Methods

add_tar_entry(entry) click to toggle source
# File lib/shaf/upgrade/package.rb, line 113
def add_tar_entry(entry)
  filename = entry.full_name
  content = entry.read

  if filename == MANIFEST_FILENAME
    parse_manifest content
  else
    @files[filename] = content
  end
end
apply_additions() click to toggle source
# File lib/shaf/upgrade/package.rb, line 195
def apply_additions
  puts '' unless manifest.additions.empty?
  manifest.additions.all? do |chksum, filename|
    content = @files[chksum]
    puts "adding file: #{filename}"
    FT::CreateFileCommand.execute(filename) { content }
    true
  rescue StandardError => e
    STDERR.puts "Failed to add '#{filename}': #{e.message}\n"
    false
  end
end
apply_drops(dir = nil) click to toggle source
# File lib/shaf/upgrade/package.rb, line 208
def apply_drops(dir = nil)
  puts '' unless manifest.removals.empty?
  files_in(dir).all? do |file|
    next true unless manifest.drop?(file)
    puts "removing file: #{file}"
    FT::DeleteFileCommand.execute file
    true
  rescue StandardError => e
    STDERR.puts "Failed to unlink '#{file}': #{e.message}\n"
    false
  end
end
apply_patch(file, patch) click to toggle source
# File lib/shaf/upgrade/package.rb, line 177
def apply_patch(file, patch)
  result = nil

  FT::ChangeFileCommand.execute(file) do
    Open3.popen3('patch', file) do |i, o, e, t|
      i.write patch
      i.close
      puts o.read
      err = e.read
      puts err unless err.empty?
      result = t.value.success?
    end
  end

  STDERR.puts "Failed to apply patch for: #{file}\n" unless result
  result
end
apply_patches(dir = nil) click to toggle source
# File lib/shaf/upgrade/package.rb, line 168
def apply_patches(dir = nil)
  files_in(dir).map do |file|
    manifest.patches_for(file).map do |name|
      patch = @files[name]
      apply_patch(file, patch)
    end.all?
  end.all?
end
apply_substitute(file, params) click to toggle source
# File lib/shaf/upgrade/package.rb, line 232
def apply_substitute(file, params)
  return unless params[:pattern] && params[:replace]

  pattern = Regexp.new(params[:pattern])
  replacement = params[:replace]

  changed = false
  tmp = Tempfile.open do |new_file|
    File.readlines(file).each do |line|
      changed = line.gsub!(pattern, replacement) || changed
      new_file << line
    end
    new_file
  end

  return true unless changed

  puts "modifying file: #{file}"
  FT::MoveFileCommand.new(from: tmp.path, to: file).execute
  true
end
apply_substitutes(dir = nil) click to toggle source
# File lib/shaf/upgrade/package.rb, line 221
def apply_substitutes(dir = nil)
  puts '' unless manifest.regexps.empty?
  files_in(dir).map do |file|
    manifest.regexps_for(file).map do |name|
      params = YAML.safe_load(@files[name])
      params.transform_keys!(&:to_sym)
      apply_substitute(file, params)
    end.all?
  end.all?
end
files_in(dir) click to toggle source
# File lib/shaf/upgrade/package.rb, line 155
def files_in(dir)
  dir += '/' if !dir.nil? && dir[-1] != '/'
  Dir["#{dir}**/*"].reject do |file|
    ignore? file
  end
end
ignore?(file) click to toggle source
# File lib/shaf/upgrade/package.rb, line 162
def ignore?(file)
  IGNORE_FILE_PATTERNS.any? do |pattern|
    file.match? pattern
  end
end
parse_manifest(content) click to toggle source
# File lib/shaf/upgrade/package.rb, line 124
def parse_manifest(content)
  hash = YAML.safe_load(content)
  @manifest = Manifest.new(
    target_version: hash['target_version'],
    patches: hash['patches'],
    add: hash['add'],
    drop: hash['drop'],
    substitutes: hash['substitutes'],
    messages: hash['messages']
  )
end
print_messages() click to toggle source
tarball() click to toggle source
# File lib/shaf/upgrade/package.rb, line 107
def tarball
  file = File.join(UPGRADE_FILES_PATH, "#{version}.tar.gz")
  return file if File.exist? file
  raise VersionNotFoundError
end
validate() click to toggle source
# File lib/shaf/upgrade/package.rb, line 136
def validate
  raise ManifestNotFoundError unless manifest
  raise VersionConflictError unless version == manifest.target_version

  from_file = @files.keys.to_set

  manifest_patches = manifest.patches.keys.to_set
  raise MissingFileError if from_file < manifest_patches

  to_add = manifest.additions.keys.to_set
  raise MissingFileError if from_file < to_add

  @files.each do |md5, content|
    raise BadChecksumError unless Digest::MD5.hexdigest(content) == md5
  end

  true
end