diff --git a/Gemfile b/Gemfile index 68de76b3d..c770fbf38 100644 --- a/Gemfile +++ b/Gemfile @@ -50,5 +50,6 @@ gem "hoptoad_notifier", "~> 2.3" gem "russian" gem "grit" gem 'unicorn' +gem 'delayed_job' gem 'jammit' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index cca96982a..e0082a62f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -42,6 +42,10 @@ GEM closure-compiler (1.1.1) compass (0.10.6) haml (>= 3.0.4) + daemons (1.1.0) + delayed_job (2.1.4) + activesupport (~> 3.0) + daemons devise (1.1.7) bcrypt-ruby (~> 2.1.2) warden (~> 1.0.2) @@ -142,6 +146,7 @@ DEPENDENCIES capistrano capistrano-ext compass (>= 0.10.6) + delayed_job devise factory_girl_rails grit diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 84c1464e5..76f69c1f7 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -2,7 +2,7 @@ class ProjectsController < ApplicationController before_filter :authenticate_user! before_filter :find_platform before_filter :find_repository - before_filter :find_project, :only => [:show, :destroy] + before_filter :find_project, :only => [:show, :destroy, :build, :process_build] def new @project = @repository.projects.new @@ -12,6 +12,37 @@ class ProjectsController < ApplicationController @current_build_lists = @project.build_lists.current.recent.paginate :page => params[:page] end + def build + @branches = @project.git_repository.branches + @arches = Arch.recent + end + + def process_build + @arch_ids = params[:build][:arches].select{|_,v| v == "1"}.collect{|x| x[0].to_i } + @arches = Arch.where(:id => @arch_ids) + + @branches = @project.git_repository.branches + @branch = @branches.select{|branch| branch.name == params[:build][:branch] }.first + + if !check_arches || !check_branches + @arches = Arch.recent + render :action => "build" + else + flash[:notice], flash[:error] = "", "" + @arches.each do |arch| + build_list = @project.build_lists.new(:arch => arch, :branch_name => @branch.name) + + if build_list.save + flash[:notice] += t("flash.build_list.saved", :branch_name => @branch.name, :arch => arch.name) + else + flash[:error] += t("flash.build_list.save_error", :branch_name => @branch.name, :arch => arch.name) + end + end + + redirect_to platform_repository_project_path(@platform, @repository, @project) + end + end + def create @project = @repository.projects.new params[:project] if @project.save @@ -43,4 +74,28 @@ class ProjectsController < ApplicationController def find_project @project = @repository.projects.find params[:id] end + + def check_arches + if @arch_ids.blank? + flash[:error] = t("flash.build_list.no_arch_selected") + false + elsif @arch_ids.length != @arches.length + flash[:error] = t("flash.build_list.no_arch_found") + false + else + true + end + end + + def check_branches + if @branch.blank? + flash[:error] = t("flash.build_list.no_branch_selected") + false + elsif !@branches.include?(@branch) + flash[:error] = t("flash.build_list.no_branch_found") + false + else + true + end + end end diff --git a/app/models/build_list.rb b/app/models/build_list.rb index f798e700e..eceba83e2 100644 --- a/app/models/build_list.rb +++ b/app/models/build_list.rb @@ -6,18 +6,36 @@ class BuildList < ActiveRecord::Base validates :project_id, :presence => true validates :branch_name, :presence => true - BUILD_PENDING = 2 - BUILD_STARTED = 3 + WAITING_FOR_RESPONSE = 4000 + BUILD_PENDING = 2000 + BUILD_STARTED = 3000 + + STATUSES = [WAITING_FOR_RESPONSE, + BuildServer::SUCCESS, + BUILD_PENDING, + BUILD_STARTED, + BuildServer::BUILD_ERROR, + BuildServer::PLATFORM_NOT_FOUND, + BuildServer::PLATFORM_PENDING, + BuildServer::PROJECT_NOT_FOUND, + BuildServer::BRANCH_NOT_FOUND] - STATUSES = [BuildServer::SUCCESS, BUILD_PENDING, BUILD_STARTED, BuildServer::BUILD_ERROR] HUMAN_STATUSES = { BuildServer::BUILD_ERROR => :build_error, BUILD_PENDING => :build_pending, BUILD_STARTED => :build_started, - BuildServer::SUCCESS => :success + BuildServer::SUCCESS => :success, + WAITING_FOR_RESPONSE => :waiting_for_response, + BuildServer::PLATFORM_NOT_FOUND => :platform_not_found, + BuildServer::PLATFORM_PENDING => :platform_pending, + BuildServer::PROJECT_NOT_FOUND => :project_not_found, + BuildServer::BRANCH_NOT_FOUND => :branch_not_found } scope :recent, order("created_at DESC") - scope :current, lambda { where(["status in (?) OR (status in (?) AND notified_at >= ?)", [BUILD_PENDING, BUILD_STARTED], [BuildServer::SUCCESS, BuildServer::ERROR], Time.now - 2.days]) } + scope :current, lambda { + outdatable_statuses = [BuildServer::SUCCESS, BuildServer::ERROR, BuildServer::PLATFORM_NOT_FOUND, BuildServer::PLATFORM_PENDING, BuildServer::PROJECT_NOT_FOUND, BuildServer::BRANCH_NOT_FOUND] + where(["status in (?) OR (status in (?) AND notified_at >= ?)", [WAITING_FOR_RESPONSE, BUILD_PENDING, BUILD_STARTED], outdatable_statuses, Time.now - 2.days]) + } scope :for_status, lambda {|status| where(:status => status) } scope :scoped_to_arch, lambda {|arch| where(:arch_id => arch) } scope :scoped_to_branch, lambda {|branch| where(:branch_name => branch) } @@ -42,6 +60,9 @@ class BuildList < ActiveRecord::Base } serialize :additional_repos + + before_create :set_default_status + after_create :place_build def self.human_status(status) I18n.t("layout.build_lists.statuses.#{HUMAN_STATUSES[status]}") @@ -61,4 +82,16 @@ class BuildList < ActiveRecord::Base end end + private + def set_default_status + self.status = WAITING_FOR_RESPONSE unless self.status.present? + end + + def place_build + self.status = BuildServer.add_build_list project.name, branch_name, project.platform.name, arch.name + self.status = BUILD_PENDING if self.status == 0 + save + end + handle_asynchronously :place_build + end \ No newline at end of file diff --git a/app/views/build_lists/_build_lists.html.haml b/app/views/build_lists/_build_lists.html.haml index dd5155944..3f4142409 100644 --- a/app/views/build_lists/_build_lists.html.haml +++ b/app/views/build_lists/_build_lists.html.haml @@ -10,12 +10,12 @@ - build_lists.each do |build_list| %tr{:class => cycle("odd", "even")} - %td= link_to build_list.bs_id, platform_repository_project_build_list_path(@platform, @repository, @project, build_list) + %td= link_to (build_list.bs_id.present? ? build_list.bs_id : t("layout.build_lists.bs_id_not_set")), platform_repository_project_build_list_path(@platform, @repository, @project, build_list) %td= build_list.human_status %td= link_to build_list.branch_name, "#" %td= link_to build_list.project.name, platform_repository_project_path(@platform, @repository, @project) %td= build_list.arch.name - %td= build_list.is_circle? + %td= t("layout.#{build_list.is_circle?}_") %td.last= build_list.notified_at = will_paginate build_lists \ No newline at end of file diff --git a/app/views/build_lists/_sidebar.html.haml b/app/views/build_lists/_sidebar.html.haml new file mode 100644 index 000000000..cf9f05506 --- /dev/null +++ b/app/views/build_lists/_sidebar.html.haml @@ -0,0 +1,14 @@ +.block.notice + %h3= t("layout.platforms.current_platform_header") + .content + %p= link_to @platform.name, platform_path(@platform) + +.block.notice + %h3= t("layout.repositories.current_repository_header") + .content + %p= link_to @repository.name + repository_name_postfix(@platform), platform_repository_path(@platform, @repository) + +.block.notice + %h3= t("layout.projects.current_project_header") + .content + %p= link_to @project.name, platform_repository_path(@platform, @repository) + "#projects" \ No newline at end of file diff --git a/app/views/build_lists/index.html.haml b/app/views/build_lists/index.html.haml index 8a3ac16ba..3e369ecb2 100644 --- a/app/views/build_lists/index.html.haml +++ b/app/views/build_lists/index.html.haml @@ -12,4 +12,4 @@ .inner = render :partial => "build_lists/build_lists", :object => @build_lists --#- content_for :sidebar, render(:partial => 'sidebar') \ No newline at end of file +- content_for :sidebar, render(:partial => 'sidebar') \ No newline at end of file diff --git a/app/views/build_lists/show.html.haml b/app/views/build_lists/show.html.haml index 77e31e5cc..5e6b6fc2c 100644 --- a/app/views/build_lists/show.html.haml +++ b/app/views/build_lists/show.html.haml @@ -3,6 +3,7 @@ %ul.wat-cf %li.first= link_to t("layout.build_lists.current"), platform_repository_project_path(@platform, @repository, @project) + "#build_lists" %li= link_to t("layout.build_lists.all"), platform_repository_project_build_lists_path(@platform, @repository, @project) + %li.active= link_to t("layout.build_lists.show"), platform_repository_project_build_list_path(@platform, @repository, @project, @build_list) .content .inner @@ -50,7 +51,7 @@ %b = t("activerecord.attributes.build_list.is_circle") \: - = @build_list.is_circle + = t("layout.#{@build_list.is_circle?}_") %p %b @@ -60,7 +61,12 @@ .block .content + %h2= t("layout.build_lists.items_header") + .inner + - if @item_groups.blank? + = t("layout.build_lists.no_items_data") + - @item_groups.each_with_index do |group, level| %h3.title Level ##{level} %table.table @@ -73,4 +79,4 @@ %td= item.name %td.last= item.human_status --#- content_for :sidebar, render(:partial => 'sidebar') +- content_for :sidebar, render(:partial => 'sidebar') \ No newline at end of file diff --git a/app/views/projects/build.html.haml b/app/views/projects/build.html.haml new file mode 100644 index 000000000..e50f2fd32 --- /dev/null +++ b/app/views/projects/build.html.haml @@ -0,0 +1,37 @@ +.block + .secondary-navigation + %ul.wat-cf + %li.first= link_to t("layout.projects.list"), platform_repository_path(@platform, @repository) + "#projects" + %li= link_to t("layout.projects.new"), new_platform_repository_project_path(@platform, @repository) + %li= link_to t("layout.projects.show"), platform_repository_project_path(@platform, @repository, @project) + %li= link_to "git-repo", platform_repository_project_repo_path(@platform, @repository, @project) + %li.active= link_to t("layout.projects.build"), build_platform_repository_project_path(@platform, @repository, @project) + + .content + %h2.title= t("layout.projects.new_build") + + .inner + = form_for :build, :url => process_build_platform_repository_project_path(@platform, @repository, @project), :html => { :class => :form, :method => :post } do |f| + .columns.wat-cf + .column.left + .group + = f.label :branch, "Branch", :class => :label + = f.select :branch, @branches.collect{|branch| [branch.name, branch.name]} + + .column.right + .group + = f.label :arches, t("activerecord.attributes.build_list.arch"), :class => :label + - @arches.each do |arch| + = f.check_box "arches[#{arch.id}]" + = arch.name + %br + + .group.navform.wat-cf + %button.button{:type => "submit"} + = image_tag("web-app-theme/icons/tick.png", :alt => t("layout.projects.build_button")) + = t("layout.projects.build_button") + %span.text_button_padding= t("layout.or") + = link_to t("layout.cancel"), platform_repository_path(@platform, @repository), :class => "text_button_padding link_button" + + +- content_for :sidebar, render(:partial => 'sidebar') diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 54aaee9e0..ff1751dfe 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -3,6 +3,9 @@ %ul.wat-cf %li.first= link_to t("layout.projects.list"), platform_repository_path(@platform, @repository) + "#projects" %li.active= link_to t("layout.projects.new"), new_platform_repository_project_path(@platform, @repository) + %li= link_to "git-repo", platform_repository_project_repo_path(@platform, @repository, @project) + %li= link_to t("layout.projects.build"), build_platform_repository_project_path(@platform, @repository, @project) + .content %h2.title= t("layout.projects.new_header") .inner diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index a8c12d65d..417edd325 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -5,6 +5,7 @@ %li= link_to t("layout.projects.new"), new_platform_repository_project_path(@platform, @repository) %li.active= link_to t("layout.projects.show"), platform_repository_project_path(@platform, @repository, @project) %li= link_to "git-repo", platform_repository_project_repo_path(@platform, @repository, @project) + %li= link_to t("layout.projects.build"), build_platform_repository_project_path(@platform, @repository, @project) .content .inner diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 82639c4c0..c15c204ac 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -16,6 +16,8 @@ ru: or: или yes_: Да no_: Нет + true_: Да + false_: Нет menu: users: Пользователи platforms: Платформы @@ -56,15 +58,17 @@ ru: list: Список list_header: Проекты show: Проект + build: Собрать + new_build: Новая сборка confirm_delete: Вы уверены, что хотите удалить этот проект? new: Новый проект new_header: Новый проект new_header: Новый проект location: Расположение git_repo_location: Путь к git-репозиторию - back_to_the_list: ⇐ К списку проектов репозитория current_project_header: Текущий проект current_build_lists: Текущие сборки + build_button: Начать сборку users: list: Список new: Создать @@ -90,6 +94,10 @@ ru: created_at_end: "Время постановки на сборку по:" notified_at_start: "Время последнего обновления от BS с:" notified_at_end: "Время последнего обновления от BS по:" + bs_id_not_set: Id еще не присвоен + items_header: Элементы сборки + no_items_data: Данных нет + show: Просмотр items: statuses: build_error: ошибка сборки @@ -102,9 +110,14 @@ ru: mock_not_found: mock не найден dependencies_fail: dependencies fail srpm_not_found: srpm не найден + waiting_for_response: ожидает ответа build_pending: ожидает сборку success: собран build_started: собирается + platform_not_found: платформа не найдена + platform_pending: платформа в процессе создания + project_not_found: проект не найден + branch_not_found: бранч не найден flash: project: @@ -127,7 +140,13 @@ ru: unfreezed: Платформа успешно разморожена unfreeze_error: Не удалось разморозить платформу, попробуйте еще раз destroyed: Платформа успешно удалена - + build_list: + saved: Билд лист для бранча '%{branch_name}' и архитектуры '%{arch}' создан успешно + save_error: Не удалось сохранить билд лист для бранча '%{branch_name}' и архитектуры '%{arch}' + no_branch_selected: Выберите какой-нибудь бранч + no_branch_found: Выбранный бранч '%{branch_name}' не найден + no_arch_selected: Выберите хотя бы одну ахритектуру + no_arch_found: Выбранные ахритектуры не найдены attributes: password: Пароль diff --git a/config/routes.rb b/config/routes.rb index ad5902f1e..bc9da8597 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -18,6 +18,12 @@ Rosa::Application.routes.draw do post :filter end end + + member do + get :build + post :process_build + end + end end end diff --git a/db/migrate/20110411160955_create_delayed_jobs.rb b/db/migrate/20110411160955_create_delayed_jobs.rb new file mode 100644 index 000000000..ac579dfcd --- /dev/null +++ b/db/migrate/20110411160955_create_delayed_jobs.rb @@ -0,0 +1,21 @@ +class CreateDelayedJobs < ActiveRecord::Migration + def self.up + create_table :delayed_jobs, :force => true do |table| + table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue + table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually. + table.text :handler # YAML-encoded string of the object that will do work + table.text :last_error # reason for last failure (See Note below) + table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future. + table.datetime :locked_at # Set when a client is working on this object + table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead) + table.string :locked_by # Who is working on this object (if locked) + table.timestamps + end + + add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority' + end + + def self.down + drop_table :delayed_jobs + end +end \ No newline at end of file diff --git a/db/schema.rb b/db/schema.rb index b8d388cc6..62c6f592b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20110411082826) do +ActiveRecord::Schema.define(:version => 20110411160955) do create_table "arches", :force => true do |t| t.string "name", :null => false @@ -58,6 +58,21 @@ ActiveRecord::Schema.define(:version => 20110411082826) do t.datetime "updated_at" end + create_table "delayed_jobs", :force => true do |t| + t.integer "priority", :default => 0 + t.integer "attempts", :default => 0 + t.text "handler" + t.text "last_error" + t.datetime "run_at" + t.datetime "locked_at" + t.datetime "failed_at" + t.string "locked_by" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "delayed_jobs", ["priority", "run_at"], :name => "delayed_jobs_priority" + create_table "platforms", :force => true do |t| t.string "name" t.string "unixname" diff --git a/script/delayed_job b/script/delayed_job new file mode 100755 index 000000000..edf195985 --- /dev/null +++ b/script/delayed_job @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby + +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment')) +require 'delayed/command' +Delayed::Command.new(ARGV).daemonize