class CookbookOmnifetch::StagingArea

stage.discard!
stage.publish!(install_path)
# Copy files to stage.path
stage = CookbookOmnifetch::StagingArea.new
@example creating a staging area and publishing it manually
end
# Copy files to staging_path
CookbookOmnifetch::StagingArea.stage(install_path) do |staging_path|
@example installing files using the {.stage} helper
staging area it creates before returning.
those directories. The {.stage} method handles directory cleanup for the
is the caller’s responsibility to use {discard!} when it is done to remove
{StagingArea} allocates temporary directories on the local file system. It
cache before it is completely installed. (See {publish!} for details.)
running in parallel might retrieve an incomplete cookbook from the local
from the web, {StagingArea} allows you to minimize the risk that a process
When performing long operations such as installing or updating a cookbook
local directory.
A staging area in which the caller can stage files and publish them to a

def self.stage(target_path)

Other tags:
    Yieldparam: staging_path -

Parameters:
  • target_path (Pathname) --
def self.stage(target_path)
  sa = new
  begin
    yield(sa.path)
    sa.publish!(target_path) unless sa.empty? || sa.match?(target_path)
  ensure
    sa.discard!
  end
end

def discard!

{StagingAreaNotAvailable}.
the file system. Future attempts to use it will raise
The staging area is no longer available once {discard!} removes it from

Removes the staging area and its contents from the file system.
def discard!
  FileUtils.rm_rf(@stage_tmp) unless @stage_tmp.nil?
  @unavailable = true
end

def empty?

Returns:
  • (Boolean) - whether the staging area is empty

Raises:
  • (StagingAreaNotAvailable) -
def empty?
  !path.exist? || path.empty?
end

def files_different?(base1, base2, subpath)

compares two files
def files_different?(base1, base2, subpath)
  file1 = File.join(base1, subpath)
  file2 = File.join(base2, subpath)
  return true unless File.ftype(file1) == File.ftype(file2)
  return true if File.file?(file1) && !FileUtils.cmp(file1, file2)
  false
end

def match?(compare_path)

Returns:
  • (Boolean) - whether the staging area matches +compare_path+

Raises:
  • (StagingAreaNotAvailable) -

Parameters:
  • compare_path (String) --
def match?(compare_path)
  raise StagingAreaNotAvailable if unavailable?
  target = Pathname(compare_path)
  return false unless target.exist?
  files = Dir.glob("**/*", File::FNM_DOTMATCH, base: path)
  target_files = Dir.glob("**/*", File::FNM_DOTMATCH, base: target)
  return false unless files.sort == target_files.sort
  files.each do |subpath|
    return false if files_different?(path, target, subpath)
  end
  true
end

def path

Returns:
  • (Pathname) - path to the staging folder

Raises:
  • (StagingAreaNotAvailable) -
def path
  raise StagingAreaNotAvailable if unavailable?
  return @path unless @path.nil?
  # Dir.mktmpdir returns a directory with restrictive permissions that it
  # doesn't support modifying, so create a subdirectory under it with
  # regular permissions for staging.
  @stage_tmp = Dir.mktmpdir
  @path = Pathname.new(File.join(@stage_tmp, "staging"))
  FileUtils.mkdir(@path)
  @path
end

def publish!(install_path)

Raises:
  • (StagingAreaNotAvailable) -

Parameters:
  • install_path (String) --
def publish!(install_path)
  target = Pathname(install_path)
  cache_dir = target.parent
  cache_dir.mkpath
  Dir.mktmpdir("_STAGING_TMP_", cache_dir) do |tmpdir|
    newtmp = File.join(tmpdir, "new_cookbook")
    oldtmp = File.join(tmpdir, "old_cookbook")
    FileUtils.cp_r(path, newtmp)
    # We could achieve an atomic replace using symbolic links, if they are
    # supported on all platforms.
    File.rename(target, oldtmp) if target.exist?
    File.rename(newtmp, target)
  end
end

def unavailable?

Returns:
  • (Boolean) - whether the staging area is unavailable
def unavailable?
  !!@unavailable
end