class Spriggan::BuildJob

Public Class Methods

new(source_dir, dest_dir) click to toggle source
# File lib/spriggan/buildjob.rb, line 11
def initialize(source_dir, dest_dir)
  @bare_repo = Pathname.new(source_dir).expand_path
  @apt_root = Pathname.new(dest_dir).expand_path

  unless @bare_repo.directory? and (@bare_repo + 'config').file?
    raise ArgumentError, 'invalid source'
  end

  unless @apt_root.directory? and (@apt_root + 'conf' + 'distributions').file?
    raise ArgumentError, 'invalid destination'
  end

  @project = {}
  @project[:root] = @bare_repo + 'spriggan'
  @project[:name] = @bare_repo.basename.to_s.sub(/\.git$/, '')
  @project[:name] = @bare_repo.parent.basename.to_s if @project[:name] == ''
  @project[:cache] = @project[:root] + 'cache'

  @build = {}
  @build[:timestamp] = Time.now.strftime("%Y%m%d%H%M%S")
  @build[:id] = @build[:timestamp] + '-' + rand(2**40).to_s(32).rjust(8, '0')
  @build[:root] = @project[:root] + 'jobs' + @build[:id]

  @build[:files] = @build[:root] + 'fs'
  @build[:pkgs] = @build[:root] + 'pkgs'
  @build[:debs] = @build[:root] + 'debs'

  @packages = {}
  @signatures = {'packages' => {}, 'metapackage' => nil}
end

Public Instance Methods

run!() click to toggle source
# File lib/spriggan/buildjob.rb, line 42
def run!
  [@project[:cache], @build[:files], @build[:pkgs], @build[:debs]].each(&:mkpath)

  begin
    execute_mutations!
  ensure
    @build[:root].rmtree if @build[:root].directory?
  end
end

Private Instance Methods

checkout_repo!() click to toggle source
# File lib/spriggan/buildjob.rb, line 66
def checkout_repo!
  @project[:sha] = `git --git-dir=#{@bare_repo} show-ref -s master`.strip
  @project[:tag] = `git --git-dir=#{@bare_repo} describe --tags #{@project[:sha]}`.strip

  @build[:files].parent.mkpath
  system!("git clone -s #{@bare_repo} #{@build[:files]}")

  Dir.chdir(@build[:files]) do
    system!("git checkout -q -f #{@project[:sha]}")
  end

  @project[:conf] = @build[:files] + 'spriggan'
  @project.update(YAML.load((@project[:conf] + 'project.yml').open))

  @project[:signatures] = @project[:root] + 'signatures.yml'
  if @project[:signatures].file?
    @signatures = YAML.load(@project[:signatures].open)
  end
end
debian_version_str(project_tag, build_timestamp, pkg_sha) click to toggle source
# File lib/spriggan/buildjob.rb, line 208
def debian_version_str(project_tag, build_timestamp, pkg_sha)
  rev_parts = project_tag.split('-')
  rev_base_version = rev_parts.shift.sub(/^v/, '')

  rev_advance = nil
  if rev_parts.last =~ /^g[0-9a-f]{6,}$/
    rev_cur_ref = rev_parts.pop
    rev_commits_since = rev_parts.pop
    rev_advance = [rev_commits_since, rev_cur_ref].join('+')
  end

  if rev_parts.length > 0
    rev_full_version = [rev_base_version, rev_parts.join('+')].join('~')
  else
    rev_full_version = rev_base_version
  end

  upstream_ver = [rev_full_version, rev_advance].compact.join('+')
  deb_ver = [build_timestamp, pkg_sha[0...7]].join('+')

  [upstream_ver, deb_ver].join('-')
end
do_project_specific_build!() click to toggle source
# File lib/spriggan/buildjob.rb, line 86
def do_project_specific_build!
  Dir.chdir(@build[:files]) do
    if bootstrap = @project['bootstrap']
      system!(bootstrap['command'])
    end

    if resolve_deps = @project['resolve dependencies']
      if mountpoint = resolve_deps['keep in shared cache']
        with_cache_mounted(@project[:cache], @build[:files] + mountpoint) do
          system!(resolve_deps['command'])
        end
      else
        system!(resolve_deps['command'])
      end
    end

    if build = @project['build']
      system!(build['command'])
    end

    @project['packages'].each do |pkg_name, pkg|
      pkg_dir = @build[:pkgs] + pkg_name
      pkg_dir.mkpath

      system!(pkg['install command'] + " " + Shellwords.escape(pkg_dir.to_s))
    end
  end
end
execute_mutations!() click to toggle source
# File lib/spriggan/buildjob.rb, line 55
def execute_mutations!
  checkout_repo!

  do_project_specific_build!

  generate_packages!
  generate_metapackage!

  publish_packages!
end
generate_metapackage!() click to toggle source
# File lib/spriggan/buildjob.rb, line 160
def generate_metapackage!
  @build[:signature] = hash_metapackage
  @build[:vsn] = debian_version_str(@project[:tag], @build[:timestamp], @build[:signature])

  deps_str = @packages.map{ |pkg_name, pkg| "-d #{pkg[:name]}" }.sort.join(' ')
  hooks_str = %w(before-install after-install before-remove after-remove).map{ |h| @project[:conf] + h }.find_all{ |h| h.file? }.map{ |h| "--#{h.basename.to_s} #{h}" }.join(" ")

  if @build[:signature] != @signatures['metapackage']
    Dir.mktmpdir do |tmpdir|
      Dir.chdir(@build[:debs]) do
        system("fpm -s dir -t deb --deb-user root --deb-group root -C #{Shellwords.escape(tmpdir.to_s)} -n #{Shellwords.escape(@project[:name])} -v #{@build[:vsn]} -a all #{hooks_str} #{deps_str} .")
      end
    end

    @signatures['metapackage'] = @build[:signature]
  end
end
generate_package!(pkg_dir) click to toggle source
# File lib/spriggan/buildjob.rb, line 124
def generate_package!(pkg_dir)
  pkg = {}

  pkg[:shortname] = pkg_dir.basename.to_s
  pkg[:name] = "#{@project[:name]}-#{pkg[:shortname]}"
  pkg[:signature] = hash_package(pkg_dir)
  pkg[:vsn] = debian_version_str(@project[:tag], @build[:timestamp], pkg[:signature])

  pkg.update(@project['packages'][pkg_dir.basename.to_s])

  deps_str = ""
  if pkg_deps = pkg['dependencies']
    deps_str = pkg_deps.map{ |dep| "-d #{Shellwords.escape(dep.to_s)}" }.join(" ")
  end

  if pkg[:signature] != @signatures['packages'][pkg[:shortname]]
    Dir.chdir(@build[:debs]) do
      system("fpm -s dir -t deb --deb-user root --deb-group root -C #{Shellwords.escape(pkg_dir.to_s)} -n #{Shellwords.escape(pkg[:name])} -v #{Shellwords.escape(pkg[:vsn])} -a all #{deps_str} .")
    end

    @signatures['packages'][pkg[:shortname]] = pkg[:signature]
  end

  pkg
end
generate_packages!() click to toggle source
# File lib/spriggan/buildjob.rb, line 115
def generate_packages!
  @packages = {}

  @build[:pkgs].children.each do |pkg_dir|
    next unless pkg_dir.directory? and (pkg_dir.children.length > 0)
    @packages[pkg_dir.basename.to_s] = generate_package!(pkg_dir)
  end
end
hash_metapackage() click to toggle source
# File lib/spriggan/buildjob.rb, line 178
def hash_metapackage
  sigs = {}
  @packages.each{ |pkg_name, pkg| sigs[pkg_name] = @signatures['packages'][pkg_name] }
  manifest = sigs.to_a.sort.map{ |(pkg_name,sha)| "#{pkg_name} #{sha}" }.join("\n")
  Digest::SHA1.hexdigest(manifest)
end
hash_package(pkg_dir) click to toggle source
# File lib/spriggan/buildjob.rb, line 150
def hash_package(pkg_dir)
  hashes = []
  pkg_dir.find do |f|
    next unless f.file?
    hashes << [f.relative_path_from(pkg_dir).to_s, Digest::SHA1.file(f).hexdigest]
  end
  manifest = hashes.sort.map{ |(name, hsh)| "#{name} #{hsh}" }.join("\n")
  Digest::SHA1.hexdigest(manifest)
end
publish_packages!() click to toggle source
# File lib/spriggan/buildjob.rb, line 185
def publish_packages!
  @build[:debs].children.each do |deb|
    system("dpkg-sig -k spriggan@peripia.com --sign builder #{Shellwords.escape(deb.to_s)}")
    system("reprepro -b #{Shellwords.escape(@apt_root.to_s)} includedeb stable #{Shellwords.escape(deb.to_s)}")
  end

  @project[:signatures].open('w'){ |f| f.write( @signatures.to_yaml ) }
end
with_cache_mounted(cache_dir, mountpoint) { || ... } click to toggle source
# File lib/spriggan/buildjob.rb, line 194
def with_cache_mounted(cache_dir, mountpoint)
  cache_dir.mkpath
  mountpoint.parent.mkpath

  mountpoint.make_symlink(cache_dir)

  begin
    yield
  ensure
    mountpoint.unlink
    FileUtils.cp_r(cache_dir, mountpoint)
  end
end