#!/usr/bin/ruby # This script mirrors a base distribution from the opensuse.org Build Service. # You can use it to create initial base projects in your build service to build for. # # This script does mirror only build packages, not the sources. # require 'optparse' # This hash will hold all of the options # parsed from the command-line by OptionParser. options = {} optparse = OptionParser.new do |opts| # Set a banner, displayed at the top of the help screen. opts.banner = "------------------------------------------------------------------------------------------- Usage: obs_mirror_project.rb -p PROJECT -r REPOSITORY [-a ARCHITECTURE] [-d DESTINATION] [-A APIURL] [-t] [-v] Example: (mirror openSUSE 13.1 as base distro) obs_mirror_project -p openSUSE:13.1 -r standard -a i586,x86_64 ------------------------------------------------------------------------------------------- Options help: " # Define the options, and what they do options[:proj] = '' opts.on('-p', '--proj PROJECT', 'Project Name: eg. openSUSE:13.1,Ubuntu:14.04,etc.') do |f| options[:proj] = f end options[:repo] = '' opts.on('-r', '--repo REPOSITORY', 'Repository Name: eg. standard,qemu,etc.') do |f| options[:repo] = f end options[:arch] = '' opts.on('-a', '--arch Architecture', 'Architecture Name: eg. i586,x86_64,etc.') do |f| options[:arch] = f end options[:dest] = '' opts.on('-d', '--dest DESTINATION', 'Destination Path: eg. /obs Default: PWD (current working directory)') do |f| options[:dest] = f end options[:apiurl] = '' opts.on('-A', '--api APIURL', 'OSC API URL: Default: https://api.opensuse.org') do |f| options[:apiurl] = f end options[:trialrun] = false opts.on('-t', '--trialrun', 'Trial run: not executing actions') do options[:trialrun] = true end options[:verbose] = false opts.on('-v', '--verbose', 'Verbose') do options[:verbose] = true end # This displays the help screen, all programs are # assumed to have this option. opts.on('-h', '--help', 'Display this screen') do puts opts exit end end # Parse the command-line. Remember there are two forms of the parse method: # 1. 'parse' method simply parses ARGV, # 2. 'parse!' method parses ARGV and removes any options found there, # as well as any parameters for the options. # What's left is the list of files to resize. optparse.parse! proj = options[:proj] repo = options[:repo] arch = options[:arch] dest = options[:dest].empty? ? Dir.pwd : options[:dest] apiurl = options[:apiurl].empty? ? 'https://api.opensuse.org' : options[:apiurl] trialrun = options[:trialrun] verbose = options[:verbose] if verbose puts Options: options puts ARGC: ARGV puts proj: proj puts repo: repo puts arch: arch puts dest: dest puts apiurl: apiurl puts trialrun: trialrun puts verbose: verbose end puts " ##################### # Data Verification # #####################" # proj and repo are mandatory if proj.empty? || repo.empty? || arch.empty? puts "ERROR! Miss mandatory options: '-p' or '-r' or '-a'" puts "Options: #{options}" puts optparse.help exit(1) end # verify apiurl if verbose puts "\nVerify API URL '#{apiurl}': osc -A #{apiurl} api -m GET /about > /dev/null" end if system("osc -A #{apiurl} api -m GET /about > /dev/null") puts "Verify API URL '#{apiurl}': ok" else puts "Verify API URL '#{apiurl}': failed" exit(1) end # verify proj if verbose puts "\nVerify proj '#{proj}': osc -A #{apiurl} api -m GET /build/#{proj} > /dev/null" end if system("osc -A #{apiurl} api -m GET /build/#{proj} > /dev/null") puts "Verify proj '#{proj}': ok" else puts "Verify proj '#{proj}': failed" exit(1) end # verify repo if verbose puts "\nVerify repo '#{repo}': osc -A #{apiurl} api -m GET /build/#{proj}/#{repo} > /dev/null" end if system("osc -A #{apiurl} api -m GET /build/#{proj}/#{repo} > /dev/null") puts "Verify repo '#{repo}': ok" else puts "Verify repo '#{repo}': failed" exit(1) end # verify arch if verbose puts "\nVerify arch '#{arch}': osc -A #{apiurl} api -m GET /build/#{proj}/#{repo}/#{arch} > /dev/null" end if system("osc -A #{apiurl} api -m GET /build/#{proj}/#{repo}/#{arch} > /dev/null") puts "Verify arch '#{arch}': ok" else puts "Verify arch '#{arch}': failed" exit(1) end # method for verify directory def directory_verify(path, name, verbose, trialrun) puts "\nVerify #{name} directory '#{path}':" if verbose unless FileTest.directory?(path.to_s) puts "Creating #{name} directory: #{path}" system("mkdir -p #{path}") unless trialrun end if FileTest.directory?(path.to_s) && FileTest.writable?(path.to_s) puts "Verify #{name} directory '#{path}': ok" else puts "Verify #{name} directory '#{path}': failed" exit(1) unless trialrun end return end # verify dest dir directory_verify(dest.to_s, 'dest', verbose, trialrun) # verify project dir directory_verify("#{dest}/projects", 'projects', verbose, trialrun) # verify :full dir directory_verify("#{dest}/build/#{proj}/#{repo}/#{arch}/:full", ':full', verbose, trialrun) puts " ################ # Project meta # ################" # retrieve project meta and configuration data if verbose puts "\nRetrieve project meta data: osc -A #{apiurl} meta prj #{proj} > #{dest}/projects/#{proj}.xml" end unless trialrun system(" osc -A #{apiurl} meta prj #{proj} > #{dest}/projects/#{proj}.xml") end if verbose puts "\nRetrieve project configuration data: osc -A #{apiurl} meta prjconf #{proj} > #{dest}/projects/#{proj}.conf" end unless trialrun system(" osc -A #{apiurl} meta prjconf #{proj} > #{dest}/projects/#{proj}.conf") end puts " #################### # Project binaries # ####################" require 'rexml/document' include REXML # retrieve full binary lists unless trialrun puts "\nRetrieve full binary list: osc -A #{apiurl} api -m GET /build/#{proj}/#{repo}/#{arch}/_repository?view=names > #{dest}/build/#{proj}/#{repo}/#{arch}/binarylist.lst" system(" osc -A #{apiurl} api -m GET /build/#{proj}/#{repo}/#{arch}/_repository?view=names > #{dest}/build/#{proj}/#{repo}/#{arch}/binarylist.lst") end # open full binary list file if File.file?("#{dest}/build/#{proj}/#{repo}/#{arch}/binarylist.lst") puts "\nOpen full binary list file: File::open(\"#{dest}/build/#{proj}/#{repo}/#{arch}/binarylist.lst\", \"r\")" process = File.open("#{dest}/build/#{proj}/#{repo}/#{arch}/binarylist.lst", 'r') else puts "\nOpen full binary list file: File::popen(\"osc -A #{apiurl} api -m GET /build/#{proj}/#{repo}/#{arch}/_repository?view=names\", \"r\")" process = File.popen("osc -A #{apiurl} api -m GET /build/#{proj}/#{repo}/#{arch}/_repository?view=names", 'r') end # process full binary list file puts '' filelist = Document.new(process) filelist.elements.each('binarylist/binary') do |binary| fname = binary.attributes['filename'] fsize = binary.attributes['size'] fmtime = binary.attributes['mtime'] puts "Process: #{fname} (#{fsize})" # skip src, nosrc, debuginfo, and debugsource packages if fname[-7..-1] == 'src.rpm' || fname.include?('-debuginfo') || fname.include?('-debugsource') puts " skip debug: #{fname}" next end # check for existing files if File.file?("#{dest}/build/#{proj}/#{repo}/#{arch}/:full/#{fname}") puts " #{dest}/build/#{proj}/#{repo}/#{arch}/:full/#{fname} already exists!" tsize = File.size?("#{dest}/build/#{proj}/#{repo}/#{arch}/:full/#{fname}") puts " size from existing target: #{tsize}" if verbose puts " size from original source: #{fsize}" if verbose if tsize.to_i == fsize.to_i puts ' skip download: size identical' next else puts ' remove: size differnt' puts " remove: #{dest}/build/#{proj}/#{repo}/#{arch}/:full/#{fname}" File.delete("#{dest}/build/#{proj}/#{repo}/#{arch}/:full/#{fname}") unless trialrun end end cmd = "osc -A #{apiurl} api -m GET /build/#{proj}/#{repo}/#{arch}/_repository/#{fname} > #{dest}/build/#{proj}/#{repo}/#{arch}/:full/#{fname}" if verbose puts " download: #{cmd}" end if trialrun puts ' trialrun: skip download' next end # re-try download 3 times 3.times do |i| if system(cmd) puts ' success: download finished' if verbose break end puts " retry #{i}: download failure" if verbose puts ' failure: download failure' if i == 2 end end