Class: PKGWizard::BuildBot

Inherits:
Command
  • Object
show all
Defined in:
lib/pkg-wizard/commands/build_bot.rb

Defined Under Namespace

Classes: Webapp

Class Method Summary collapse

Methods inherited from Command

registry, #run

Class Method Details

.performObject



296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
# File 'lib/pkg-wizard/commands/build_bot.rb', line 296

def self.perform
  cli = BuildBot.new
  cli.banner = "\nUsage: pkgwiz build-bot (options)\n\n"
  cli.parse_options

  ## Node.JS log server stuff
  public_dir = File.join(File.dirname(__FILE__), '/../../../resources/public/')
  node_port = cli.config[:log_server_port]
  if File.exist?('public')
    FileUtils.rm_rf 'public'
  end
  FileUtils.cp_r public_dir, 'public'
  html = File.read('public/index.html.tmpl').gsub('@@NODEJSPORT@@', node_port)
  serverjs = File.read('public/server.js.tmpl').gsub('@@NODEJSPORT@@', node_port)
  File.open 'public/index.html', 'w' do |f|
    f.puts html
  end
  File.open 'public/server.js', 'w' do |f|
    f.puts serverjs 
  end

  if cli.config[:log_format] == 'web'
    String.class_eval do include FakeColor; end
  else
    String.class_eval do include Term::ANSIColor; end
  end

  pwd = cli.config[:working_dir] || Dir.pwd
  NodeRunner.logfile = (cli.config[:working_dir] || Dir.pwd) + '/build-bot.log'
  pwd = File.expand_path pwd
  if cli.config[:daemonize]
    umask = File.umask
    Daemons.daemonize :app_name => 'build-bot', :dir_mode => :normal, :dir => pwd
    Dir.chdir pwd
    log = File.new("build-bot.log", "a")
    $stdout.reopen(log)
    $stderr.reopen(log)
    $stdout.sync = true
    $stderr.sync = true
    File.umask umask
  else
    Dir.chdir pwd
  end

  mock_profile = cli.config[:mock_profile]
  BuildBotConfig.instance.mock_profile = mock_profile
  if not mock_profile
    $stderr.puts 'Invalid mock profile.'
    $stderr.puts cli.opt_parser.help
    exit 1
  end

  if not File.exist? '/usr/bin/rpmbuild'
    $stderr.puts 'rpmbuild command not found. Install it first.'
    exit 1
  end
  
  if not File.exist? '/usr/sbin/mock'
    $stderr.puts 'mock command not found. Install it first.'
    exit 1
  end

  meta = { :mock_profile  => mock_profile }
  
  
  Dir.mkdir 'incoming' if not File.exist?('incoming')
  Dir.mkdir 'output' if not File.exist?('output')
  Dir.mkdir 'workspace' if not File.exist?('workspace')
  Dir.mkdir 'archive' if not File.exist?('archive')
  Dir.mkdir 'failed' if not File.exist?('failed')
  Dir.mkdir 'snapshot' if not File.exist?('snapshot')
  Dir.mkdir 'tags' if not File.exist?('tags')
  FileUtils.ln_sf 'output', 'repo' if not File.exist?('repo')
  
  cleaner = Rufus::Scheduler.start_new
  cleaner.every '2s', :blocking => true do
    if File.exist?('failed/.clean')
      puts '* cleaning FAILED jobs'
      Dir["failed/job_*"].each do |d|
        FileUtils.rm_rf d
      end
      FileUtils.rm 'failed/.clean'
      FileUtils.rm_rf "failed/last"
    end
    if File.exist?('output/.clean')
      puts '* cleaning OUTPUT jobs'
      Dir["output/job_*"].each do |d|
        FileUtils.rm_rf d
      end
      FileUtils.rm 'output/.clean'
      FileUtils.rm_rf 'output/repodata'
      FileUtils.rm_rf 'output/last'
    end
  end

  # tag routine
  tag_sched = Rufus::Scheduler.start_new
  tag_sched.every '2s', :blocking => true do
    if File.exist?('tags/.tag')
      tag_meta = YAML.load_file 'tags/.tag' rescue nil
      if tag_meta
        tag_dir = "tags/#{tag_meta[:name]}"
        tag_mock_profile = tag_meta[:mock_profile]

        tag_pkgs = []
        Dir["output/*/result/*.rpm"].sort.each do |rpm|
          p = YAML.load_file(File.join(File.dirname(rpm), '/../meta.yml'))[:mock_profile]
          if p == tag_mock_profile or tag_mock_profile.nil?
            tag_pkgs << rpm
          end
        end
            
        if not tag_pkgs.empty?
          puts "* create tag #{tag_meta[:name]} repo START"
          Dir.mkdir(tag_dir) if not File.exist?(tag_dir)
          tag_pkgs.each do |rpm|
            FileUtils.cp rpm, tag_dir 
          end
          output = `createrepo -q -o #{tag_dir} --update -d #{tag_dir} 2>&1`
          if $? != 0
            puts "create tag #{tag_meta[:name]} operation failed: #{output}".red.bold
          else
            puts "* create tag #{tag_meta[:name]} DONE"
          end
        else
          puts "* WARNING: trying to create a tag with no packages."
        end
        FileUtils.rm 'tags/.tag'
      else
        puts "* ERROR: error creating tag, could not parse tag metadata."
      end
    else
      
    end
  end
  
  # createrepo snapshot
  snapshot_sched = Rufus::Scheduler.start_new
  snapshot_sched.every '2s', :blocking => true do
    if File.exist?('snapshot/.createsnapshot')
      puts '* snapshot START'
      stamp = Time.now.strftime '%Y%m%d_%H%M%S'
      snapshot_dir = "snapshot/snapshot_#{stamp}"
      Dir.mkdir snapshot_dir
      begin
        Dir["output/*/result/*.rpm"].sort.each do |rpm|
          FileUtils.cp rpm, snapshot_dir
        end
        puts '* snapshot DONE'
      rescue Exception => e
        $stderr.puts "snapshot operation failed".red.bold
      ensure
        FileUtils.rm 'snapshot/.createsnapshot'
      end
    end
  end

  # createrepo scheduler
  createrepo_sched = Rufus::Scheduler.start_new
  createrepo_sched.every '2s', :blocking => true do
    if File.exist?('repo/.createrepo')
      puts '* createrepo START'
      begin
        output = `createrepo -q -o repo/ --update -d output/ 2>&1`
        if $? != 0
          raise Exception.new(output)
        end
        puts '* createrepo DONE'
      rescue Exception => e
        $stderr.puts "createrepo operation failed".red.bold
        File.open('repo/createrepo.log', 'a') { |f| f.puts e.message }
      ensure
        FileUtils.rm 'repo/.createrepo'
      end
    end
  end

  # Build queue
  scheduler = Rufus::Scheduler.start_new
  scheduler.every '2s', :blocking => true do
    meta[:start_time] = Time.now
    meta[:mock_profile] = mock_profile
    queue = Dir['incoming/*.src.rpm'].sort_by {|filename| File.mtime(filename) }
    if not queue.empty?
      # Clean workspace first
      Dir["workspace/job_*"].each do |j|
        FileUtils.rm_rf j
      end
      job_dir = "workspace/job_#{Time.now.strftime '%Y%m%d_%H%M%S'}"
      imeta_file = "#{queue.first}.metadata"
      qfile = File.join(job_dir, File.basename(queue.first))
      if File.exist?(imeta_file)
        begin
        imeta = YAML.load_file(imeta_file)
        if imeta
          m = YAML.load_file(imeta_file)
          meta[:mock_profile] = m[:mock_profile] || BuildBotConfig.mock_profile
        end
        rescue Exception
          puts "* ERROR: parsing #{queue.first} metadata"
        end
        FileUtils.rm imeta_file 
      else
        puts "* WARNING: #{queue.first} does not have metadata!"
      end
      job_time = Time.now.strftime '%Y%m%d_%H%M%S'
      result_dir = job_dir + '/result'
      FileUtils.mkdir_p result_dir
      meta[:source] = File.basename(queue.first)
      meta[:status] = 'building'
      File.open("workspace/job_#{job_time}/meta.yml", 'w') do |f|
        f.puts meta.to_yaml
      end
      FileUtils.mv queue.first, qfile
      puts "Building pkg [job_#{job_time}][#{meta[:mock_profile]}] ".ljust(40).yellow.bold +  "#{File.basename(qfile)}"

      rdir = nil
      begin
        PKGWizard::Mock.srpm :srpm => qfile, :profile => meta[:mock_profile], :resultdir => result_dir
        meta[:status] = 'ok'
        meta[:end_time] = Time.now
        meta[:build_time] = meta[:end_time] - meta[:start_time]
        puts "Build OK [job_#{job_time}][#{meta[:mock_profile]}] ".ljust(40).green.bold + "#{File.basename(qfile)}"
      rescue Exception => e
        meta[:status] = 'error'
        puts "Build FAILED [job_#{job_time}][#{meta[:mock_profile]}]".ljust(40).red.bold + "#{File.basename(qfile)}"
        File.open(job_dir + '/buildbot.log', 'w') do |f|
          f.puts "#{e.backtrace.join("\n")}"
          f.puts "#{e.message}"
        end
      ensure
        File.open(job_dir + '/meta.yml', 'w') do |f|
          f.puts meta.to_yaml
        end
        if meta[:status] == 'error'  
          FileUtils.mv job_dir, 'failed/'
          FileUtils.rm_f 'failed/last' if File.exist?('failed/last')
          FileUtils.ln_sf "#{File.basename(job_dir)}", "failed/last"
        else
          FileUtils.mv job_dir, 'output/'
          FileUtils.rm_f 'output/last' if File.exist?('output/last')
          FileUtils.ln_sf "#{File.basename(job_dir)}", "output/last"
        end
      end
    end
  end
  Webapp.set :port => cli.config[:port]
  Webapp.set :public => 'public'
  Webapp.run!
  at_exit do 
    NodeRunner.kill
  end 
end