From 1f1cf175fdc1bee9ce47f0320759f22d7f2bd147 Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Thu, 7 Mar 2013 14:24:33 +0400 Subject: [PATCH 01/24] #6: remove "ProductBuildList#arch_id" field from UI --- app/models/product_build_list.rb | 4 ++-- app/views/platforms/product_build_lists/new.html.haml | 4 ---- app/views/platforms/product_build_lists/show.html.haml | 1 - 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/app/models/product_build_list.rb b/app/models/product_build_list.rb index 98d0b68e6..a5ec9e656 100644 --- a/app/models/product_build_list.rb +++ b/app/models/product_build_list.rb @@ -34,7 +34,8 @@ class ProductBuildList < ActiveRecord::Base belongs_to :arch belongs_to :user - + # see: Issue #6 + before_validation lambda { self.arch_id = Arch.find_by_name('x86_64').id } validates :product_id, :status, :project_id, @@ -52,7 +53,6 @@ class ProductBuildList < ActiveRecord::Base :params, :project_version, :commit_hash, - :arch_id, :product_id attr_readonly :product_id serialize :results, Array diff --git a/app/views/platforms/product_build_lists/new.html.haml b/app/views/platforms/product_build_lists/new.html.haml index d1fbd0c82..6f03eb78a 100644 --- a/app/views/platforms/product_build_lists/new.html.haml +++ b/app/views/platforms/product_build_lists/new.html.haml @@ -12,10 +12,6 @@ .rightlist= f.select :project_version, versions_for_group_select(pbl.project), :selected => params[:product_build_lists].try(:fetch, :project_version) || pbl.project.default_branch .both - .leftlist= f.label :arch, t("activerecord.attributes.product_build_list.arch"), :class => :label - .rightlist= f.select :arch_id, Arch.recent.map{ |a| [a.name, a.id]} - .both - = render 'platforms/products/def_fields', :f => f = f.submit t("layout.projects.build_button") diff --git a/app/views/platforms/product_build_lists/show.html.haml b/app/views/platforms/product_build_lists/show.html.haml index 6af23ac72..a46024bbd 100644 --- a/app/views/platforms/product_build_lists/show.html.haml +++ b/app/views/platforms/product_build_lists/show.html.haml @@ -19,7 +19,6 @@ = render 'show_field', :key => :project_version, :value => product_build_list_version_link(pbl, true) -= render 'show_field', :key => :arch, :value => pbl.arch.name - [:main_script, :params].each do |el| = render 'show_field', :key => el, :value => pbl.send(el) From bf8e2db1bd3bc01d6c813eabee277714bf17439b Mon Sep 17 00:00:00 2001 From: Alexander Machehin Date: Mon, 11 Mar 2013 16:55:45 +0600 Subject: [PATCH 02/24] [#16] fix creating build lists for mass build --- app/models/mass_build.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/mass_build.rb b/app/models/mass_build.rb index ffa14c480..4c3fba122 100644 --- a/app/models/mass_build.rb +++ b/app/models/mass_build.rb @@ -28,7 +28,7 @@ class MassBuild < ActiveRecord::Base def build_all # later with resque - arches_list = arches ? Arch.where(:id => arches) : Arch.all + arches_list = arch_names ? Arch.where(:name => arch_names) : Arch.all auto_publish ||= false projects_list.lines.each do |name| From 52291a8d76031f1862b1ddc290b55666073c3b48 Mon Sep 17 00:00:00 2001 From: Alexander Machehin Date: Mon, 11 Mar 2013 17:19:13 +0600 Subject: [PATCH 03/24] [#16] fix error with several arches --- app/models/mass_build.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/mass_build.rb b/app/models/mass_build.rb index 4c3fba122..c080b5f0a 100644 --- a/app/models/mass_build.rb +++ b/app/models/mass_build.rb @@ -28,7 +28,7 @@ class MassBuild < ActiveRecord::Base def build_all # later with resque - arches_list = arch_names ? Arch.where(:name => arch_names) : Arch.all + arches_list = arch_names ? Arch.where(:name => arch_names.split(', ')) : Arch.all auto_publish ||= false projects_list.lines.each do |name| From a6ed68fbc4daff87996e6460ee3391ce992cb56b Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Mon, 11 Mar 2013 16:47:45 +0400 Subject: [PATCH 04/24] #6: remove "arch" field from ProductBuildList API --- app/views/api/v1/product_build_lists/show.json.jbuilder | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/views/api/v1/product_build_lists/show.json.jbuilder b/app/views/api/v1/product_build_lists/show.json.jbuilder index 04362cf4a..d735eecf8 100644 --- a/app/views/api/v1/product_build_lists/show.json.jbuilder +++ b/app/views/api/v1/product_build_lists/show.json.jbuilder @@ -12,10 +12,6 @@ json.product_build_list do |json| :project => @product_build_list.project, :json => json_project end - json.arch do |json_arch| - json_arch.(@product_build_list.arch, :id, :name) - end - json.created_at @product_build_list.created_at.to_i json.updated_at @product_build_list.updated_at.to_i From 1a44b634f9e644ac97a5fc34765e040572cb390e Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Wed, 20 Mar 2013 01:11:16 +0400 Subject: [PATCH 05/24] Update BuildListsPublishTaskManager and PublishObserver --- lib/abf_worker/base_observer.rb | 2 +- lib/abf_worker/build_lists_publish_task_manager.rb | 3 ++- lib/abf_worker/publish_observer.rb | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/abf_worker/base_observer.rb b/lib/abf_worker/base_observer.rb index 45151da79..d7d1ec01a 100644 --- a/lib/abf_worker/base_observer.rb +++ b/lib/abf_worker/base_observer.rb @@ -32,7 +32,7 @@ module AbfWorker def sort_results_and_save(results, item = subject) item.results = results.sort_by{ |r| r['file_name'] } - item.save! + item.save(false) end end diff --git a/lib/abf_worker/build_lists_publish_task_manager.rb b/lib/abf_worker/build_lists_publish_task_manager.rb index dc731f2dc..4ec75ea8a 100644 --- a/lib/abf_worker/build_lists_publish_task_manager.rb +++ b/lib/abf_worker/build_lists_publish_task_manager.rb @@ -237,8 +237,9 @@ module AbfWorker build_lists = build_lists.where('build_lists.id NOT IN (?)', locked_ids) unless locked_ids.empty? build_lists = build_lists.limit(50) + project_ids = build_lists.map(&:project_id).uniq projects_for_cleanup = @redis.lrange(PROJECTS_FOR_CLEANUP, 0, -1). - select{ |k| k =~ /#{save_to_repository_id}\-#{build_for_platform_id}$/ } + select{ |k| k =~ /#{save_to_repository_id}\-#{build_for_platform_id}$/ && k !~ /^(#{project_ids.join('|')})\-/ } old_packages = {:sources => [], :binaries => {:x86_64 => [], :i586 => []}} diff --git a/lib/abf_worker/publish_observer.rb b/lib/abf_worker/publish_observer.rb index a3958510f..c4acc9204 100644 --- a/lib/abf_worker/publish_observer.rb +++ b/lib/abf_worker/publish_observer.rb @@ -36,9 +36,10 @@ module AbfWorker update_results build_list case status when COMPLETED - build_list.published + # `update_column` - when build_list has been removed from repository + build_list.published || build_list.update_column(:status, BuildList::BUILD_PUBLISHED) when FAILED, CANCELED - build_list.fail_publish + build_list.fail_publish || build_list.update_column(:status, BuildList::FAILED_PUBLISH) end AbfWorker::BuildListsPublishTaskManager.unlock_build_list build_list end From 3d2d945e417bc2b59ccd70c8a6d1795604a4e2e3 Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Wed, 20 Mar 2013 01:23:31 +0400 Subject: [PATCH 06/24] update comment --- lib/abf_worker/publish_observer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/abf_worker/publish_observer.rb b/lib/abf_worker/publish_observer.rb index c4acc9204..56dee3ac3 100644 --- a/lib/abf_worker/publish_observer.rb +++ b/lib/abf_worker/publish_observer.rb @@ -36,7 +36,7 @@ module AbfWorker update_results build_list case status when COMPLETED - # `update_column` - when build_list has been removed from repository + # 'update_column' - when build_list has been removed from repository build_list.published || build_list.update_column(:status, BuildList::BUILD_PUBLISHED) when FAILED, CANCELED build_list.fail_publish || build_list.update_column(:status, BuildList::FAILED_PUBLISH) From cff502bf482a23ae71038be421c8ca31733ebf98 Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Thu, 21 Mar 2013 18:41:45 +0400 Subject: [PATCH 07/24] #40: added ability to publish build lists of mass build. --- app/assets/stylesheets/design/custom.scss | 4 ++++ .../platforms/mass_builds_controller.rb | 13 +++++++++++-- app/models/ability.rb | 2 +- app/models/mass_build.rb | 16 ++++++++++++++++ app/views/platforms/mass_builds/index.html.haml | 13 +++++++++++-- config/locales/models/mass_build.en.yml | 4 +++- config/locales/models/mass_build.ru.yml | 4 +++- config/locales/models/platform.en.yml | 1 + config/locales/models/platform.ru.yml | 1 + config/routes.rb | 1 + 10 files changed, 52 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/design/custom.scss b/app/assets/stylesheets/design/custom.scss index 0fba4a2d9..180de2816 100644 --- a/app/assets/stylesheets/design/custom.scss +++ b/app/assets/stylesheets/design/custom.scss @@ -1537,6 +1537,10 @@ table.tablesorter.platform-maintainers.static-search thead tr.search th input[ty width: 430px; } +.tablesorter .right { + text-align: right; +} + .all_platforms { > .both { margin: 0 0 5px; } .build_for_pl { font-weight: bold; } diff --git a/app/controllers/platforms/mass_builds_controller.rb b/app/controllers/platforms/mass_builds_controller.rb index f35127f1d..ff90c71b2 100644 --- a/app/controllers/platforms/mass_builds_controller.rb +++ b/app/controllers/platforms/mass_builds_controller.rb @@ -6,8 +6,8 @@ class Platforms::MassBuildsController < Platforms::BaseController load_and_authorize_resource skip_load_and_authorize_resource :only => [:index, :create] - skip_load_and_authorize_resource :platform, :only => [:cancel, :failed_builds_list] - skip_authorize_resource :platform, :only => [:create, :index] + skip_load_and_authorize_resource :platform, :only => [:cancel, :failed_builds_list, :publish] + skip_authorize_resource :platform, :only => [:index, :create] def create mass_build = @platform.mass_builds.new(:arches => params[:arches], @@ -27,6 +27,15 @@ class Platforms::MassBuildsController < Platforms::BaseController end end + def publish + if params[:status] == 'test_failed' + @mass_build.publish_test_faild_builds + else + @mass_build.publish_success_builds + end + redirect_to(platform_mass_builds_path(@mass_build.platform), :notice => t("flash.platform.publish_success")) + end + def index authorize! :local_admin_manage, @platform diff --git a/app/models/ability.rb b/app/models/ability.rb index f7d26c011..7e4ff93e6 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -98,7 +98,7 @@ class Ability can([:update, :destroy], Platform) {|platform| owner?(platform) } can([:local_admin_manage, :members, :add_member, :remove_member, :remove_members] , Platform) {|platform| owner?(platform) || local_admin?(platform) } - can([:get_list, :create], MassBuild) {|mass_build| (owner?(mass_build.platform) || local_admin?(mass_build.platform)) && mass_build.platform.main?} + can([:get_list, :create, :publish], MassBuild) {|mass_build| (owner?(mass_build.platform) || local_admin?(mass_build.platform)) && mass_build.platform.main?} can(:cancel, MassBuild) {|mass_build| (owner?(mass_build.platform) || local_admin?(mass_build.platform)) && !mass_build.stop_build && mass_build.platform.main?} can [:read, :projects_list, :projects], Repository, :platform => {:owner_type => 'User', :owner_id => user.id} diff --git a/app/models/mass_build.rb b/app/models/mass_build.rb index ffa14c480..cfcdf61ba 100644 --- a/app/models/mass_build.rb +++ b/app/models/mass_build.rb @@ -70,8 +70,24 @@ class MassBuild < ActiveRecord::Base end later :cancel_all, :queue => :clone_build + def publish_success_builds + publish BuildList::SUCCESS, BuildList::FAILED_PUBLISH + end + later :publish_success_builds, :queue => :clone_build + + def publish_test_faild_builds + publish BuildList::TESTS_FAILED + end + later :publish_test_faild_builds, :queue => :clone_build + private + def publish(*statuses) + build_lists.where(:status => statuses).order(:id).find_in_batches(:batch_size => 50) do |bls| + bls.each{ |bl| bl.can_publish? && bl.now_publish } + end + end + def set_data if new_record? self.name = "#{Time.now.utc.to_date.strftime("%d.%b")}-#{platform.name}" diff --git a/app/views/platforms/mass_builds/index.html.haml b/app/views/platforms/mass_builds/index.html.haml index 77b52a940..cf8ffa71e 100644 --- a/app/views/platforms/mass_builds/index.html.haml +++ b/app/views/platforms/mass_builds/index.html.haml @@ -27,7 +27,7 @@ %th.lpadding16= t('activerecord.attributes.mass_build.name') %th.lpadding16= t("layout.mass_builds.statuses") %th.lpadding16= t("layout.mass_builds.failed_builds_list") - %th.lpadding16= t("layout.mass_builds.cancel_mass_build") + %th.lpadding16= t("layout.mass_builds.actions") %th.lpadding16= t("layout.mass_builds.extended_data") - @mass_builds.each do |mass_build| %tr @@ -43,7 +43,16 @@ = mass_build.read_attribute 'missed_projects_count' %td =link_to_list @platform, mass_build, 'failed_builds_list' - %td= link_to image_tag('x.png'), cancel_platform_mass_build_path(@platform, mass_build.id), :method => :post, :confirm => t("layout.mass_builds.cancel_confirm") if can?(:cancel, mass_build) + %td.right + - if can?(:publish, mass_build) + - if mass_build.auto_publish? + = link_to t("layout.mass_builds.publish_success"), publish_platform_mass_build_path(@platform, mass_build.id), :method => :post + %br + = link_to t("layout.mass_builds.publish_test_failed"), publish_platform_mass_build_path(@platform, mass_build.id, :status => 'test_failed'), :method => :post + %br + + - if can?(:cancel, mass_build) + = link_to image_tag('x.png'), cancel_platform_mass_build_path(@platform, mass_build.id), :method => :post, :confirm => t('layout.mass_builds.cancel_confirm') %td %a.toggle_btn{:href => "#toggle_#{ mass_build.id }", :'data-target' => "#toggle_#{ mass_build.id }"}= t("layout.mass_builds.extended_data") .toggle{:id => "toggle_#{ mass_build.id }"} diff --git a/config/locales/models/mass_build.en.yml b/config/locales/models/mass_build.en.yml index b8adf2f1f..bafb62b01 100644 --- a/config/locales/models/mass_build.en.yml +++ b/config/locales/models/mass_build.en.yml @@ -1,11 +1,13 @@ en: layout: mass_builds: + publish_success: Publish success builds + publish_test_failed: Publish test failed builds repositories: Repositories extended_data: Extended data failed_builds_list: Failed Builds List statuses: Statuses - cancel_mass_build: Cancel + actions: Actions cancel_confirm: Are you sure you want to cancel mass build? projects_list: Projects list missed_projects_list: 'Missed projects: ' diff --git a/config/locales/models/mass_build.ru.yml b/config/locales/models/mass_build.ru.yml index f202ba420..974e7f09d 100644 --- a/config/locales/models/mass_build.ru.yml +++ b/config/locales/models/mass_build.ru.yml @@ -1,11 +1,13 @@ ru: layout: mass_builds: + publish_success: Опубликовать успешные сборки + publish_test_failed: Опубликовать сборки с проваленными тестами repositories: Репозитории extended_data: Параметры задания failed_builds_list: Список ошибок сборок statuses: Статусы - cancel_mass_build: Отмена + actions: Действия cancel_confirm: Вы уверены, что хотите отменить массовую сборку? projects_list: Список проектов missed_projects_list: 'Несуществующие проекты: ' diff --git a/config/locales/models/platform.en.yml b/config/locales/models/platform.en.yml index afb83a510..b834312d1 100644 --- a/config/locales/models/platform.en.yml +++ b/config/locales/models/platform.en.yml @@ -62,6 +62,7 @@ en: build_all_success: All project build in progress build_all_error: Mass build failed cancel_mass_build: Mass build canceled + publish_success: Builds have been sent to queue for publishing successfully clone_success: Cloned successfully members: successfully_added: "%{name} successfully added to the platform" diff --git a/config/locales/models/platform.ru.yml b/config/locales/models/platform.ru.yml index a379f293b..366852ada 100644 --- a/config/locales/models/platform.ru.yml +++ b/config/locales/models/platform.ru.yml @@ -62,6 +62,7 @@ ru: build_all_success: Все проекты успешно отправлены на сборку build_all_error: Сборка не удалась! cancel_mass_build: Массовая сборка отменена + publish_success: Сборки отправленны в очередь на публикацию успешно clone_success: Клонирование успешно members: successfully_added: "Участник %{name} успешно добавлен к платформе" diff --git a/config/routes.rb b/config/routes.rb index 4d5e5c11c..6d0e10947 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -145,6 +145,7 @@ Rosa::Application.routes.draw do resources :mass_builds, :only => [:create, :index] do member do post :cancel + post :publish get '/:kind.:format' => "mass_builds#get_list", :as => :get_list, :kind => /failed_builds_list|missed_projects_list|projects_list/ end end From c6ca02510c407348a78969b477cdc0fbafbea993 Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Thu, 21 Mar 2013 19:17:30 +0400 Subject: [PATCH 08/24] #40: add specs for MassBuilds#publish action --- app/models/ability.rb | 2 +- .../platforms/mass_builds_controller_spec.rb | 36 +++++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index 7e4ff93e6..98a4939dc 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -158,7 +158,7 @@ class Ability cannot :create_container, BuildList, :new_core => false cannot(:publish, BuildList) {|build_list| !build_list.can_publish? } - cannot([:get_list, :create], MassBuild) {|mass_build| mass_build.platform.personal?} + cannot([:get_list, :create, :publish], MassBuild) {|mass_build| mass_build.platform.personal?} cannot(:cancel, MassBuild) {|mass_build| mass_build.platform.personal? || mass_build.stop_build} cannot(:regenerate_metadata, Repository) {|repository| !repository.platform.main?} diff --git a/spec/controllers/platforms/mass_builds_controller_spec.rb b/spec/controllers/platforms/mass_builds_controller_spec.rb index e7a449a2c..b459124fd 100644 --- a/spec/controllers/platforms/mass_builds_controller_spec.rb +++ b/spec/controllers/platforms/mass_builds_controller_spec.rb @@ -22,6 +22,16 @@ shared_examples_for 'mass_build platform owner' do @mass_build.reload.stop_build.should == true end + it 'should be able to perform publish action' do + post :publish, :platform_id => @platform, :id => @mass_build + response.should redirect_to(platform_mass_builds_path(@platform)) + end + + it 'should change build_publish on publish' do + post :publish, :platform_id => @platform, :id => @mass_build + @mass_build.reload.build_publish_count.should == 1 + end + it 'should not be able to perform cancel action if stop_build is true' do @mass_build.stop_build = true; @mass_build.save post :cancel, :platform_id => @platform, :id => @mass_build @@ -34,10 +44,10 @@ shared_examples_for 'mass_build platform owner' do context 'for personal platform' do before(:each) do - Platform.update_all("platform_type = 'personal'") + Platform.update_all(:platform_type => 'personal') end - [:cancel, :get_list, :create].each do |action| + [:cancel, :get_list, :create, :publish].each do |action| it "should not be able to perform #{ action } action" do get action, :platform_id => @platform, :id => @mass_build.id response.should redirect_to(forbidden_path) @@ -54,7 +64,7 @@ shared_examples_for 'mass_build platform reader' do end end - [:cancel, :get_list].each do |action| + [:cancel, :get_list, :publish].each do |action| it "should not be able to perform #{ action } action" do get action, :platform_id => @platform, :id => @mass_build.id response.should redirect_to(forbidden_path) @@ -69,6 +79,11 @@ shared_examples_for 'mass_build platform reader' do post :cancel, :platform_id => @platform, :id => @mass_build @mass_build.reload.stop_build.should == false end + + it 'should not change build_publish on publish' do + post :publish, :platform_id => @platform, :id => @mass_build + @mass_build.reload.build_publish_count.should == 0 + end end @@ -92,6 +107,7 @@ describe Platforms::MassBuildsController do } @mass_build = FactoryGirl.create(:mass_build, :platform => @platform, :user => @user, :projects_list => project.name) + FactoryGirl.create(:build_list_core, :mass_build => @mass_build, :status => BuildList::SUCCESS) end context 'for guest' do @@ -107,9 +123,11 @@ describe Platforms::MassBuildsController do response.should redirect_to(new_user_session_path) end - it "should not be able to perform cancel action" do - post :cancel, :platform_id => @platform, :id => @mass_build - response.should redirect_to(new_user_session_path) + [:cancel, :publish].each do |action| + it "should not be able to perform #{action} action" do + post action, :platform_id => @platform, :id => @mass_build + response.should redirect_to(new_user_session_path) + end end it 'should not change objects count on create success' do @@ -120,6 +138,12 @@ describe Platforms::MassBuildsController do post :cancel, :platform_id => @platform, :id => @mass_build @mass_build.reload.stop_build.should == false end + + it 'should not change build_publish_count on publish' do + post :publish, :platform_id => @platform, :id => @mass_build + @mass_build.reload.build_publish_count.should == 0 + end + end context 'for global admin' do From a4522bc3c7ab71bff3d603503d509399dd6f049f Mon Sep 17 00:00:00 2001 From: Alexander Machehin Date: Thu, 21 Mar 2013 22:55:12 +0600 Subject: [PATCH 09/24] add one more check for merging pull requests --- app/models/pull_request.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/models/pull_request.rb b/app/models/pull_request.rb index 77a769dce..446e839a2 100644 --- a/app/models/pull_request.rb +++ b/app/models/pull_request.rb @@ -78,12 +78,17 @@ class PullRequest < ActiveRecord::Base def merge!(who) return false unless can_merging? Dir.chdir(path) do + old_commit = to_project.repo.commits(to_ref).first commit = repo.commits(to_ref).first system "git config user.name \"#{who.uname}\" && git config user.email \"#{who.email}\"" res = merge if commit.id != repo.commits(to_ref).first.id - system("export GL_ID=user-#{who.id} && git push origin HEAD") + res2 = %x(export GL_ID=user-#{who.id} && git push origin HEAD) system("git reset --hard HEAD^") # for diff maybe FIXME + + if old_commit.id == to_project.repo.commits(to_ref).first.id + raise "merge result pull_request #{id}: #{res}; #{res2}" + end set_user_and_time who merging else # Try to catch no merge errors From 6bed54bec6bcf6e328159be2c8f30b34cb6f33e3 Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Thu, 21 Mar 2013 21:10:25 +0400 Subject: [PATCH 10/24] #40: update text message --- config/locales/models/platform.ru.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/models/platform.ru.yml b/config/locales/models/platform.ru.yml index 366852ada..a59233458 100644 --- a/config/locales/models/platform.ru.yml +++ b/config/locales/models/platform.ru.yml @@ -62,7 +62,7 @@ ru: build_all_success: Все проекты успешно отправлены на сборку build_all_error: Сборка не удалась! cancel_mass_build: Массовая сборка отменена - publish_success: Сборки отправленны в очередь на публикацию успешно + publish_success: Сборки успешно отправлены в очередь на публикацию clone_success: Клонирование успешно members: successfully_added: "Участник %{name} успешно добавлен к платформе" From 915204a66fd66e0eef5bdde64c0916c2f3f45199 Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Thu, 21 Mar 2013 21:37:13 +0400 Subject: [PATCH 11/24] small refactoring according to Alexander's comment --- lib/abf_worker/build_lists_publish_task_manager.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/abf_worker/build_lists_publish_task_manager.rb b/lib/abf_worker/build_lists_publish_task_manager.rb index 4ec75ea8a..73854c766 100644 --- a/lib/abf_worker/build_lists_publish_task_manager.rb +++ b/lib/abf_worker/build_lists_publish_task_manager.rb @@ -237,7 +237,7 @@ module AbfWorker build_lists = build_lists.where('build_lists.id NOT IN (?)', locked_ids) unless locked_ids.empty? build_lists = build_lists.limit(50) - project_ids = build_lists.map(&:project_id).uniq + project_ids = build_lists.pluck(:project_id).uniq projects_for_cleanup = @redis.lrange(PROJECTS_FOR_CLEANUP, 0, -1). select{ |k| k =~ /#{save_to_repository_id}\-#{build_for_platform_id}$/ && k !~ /^(#{project_ids.join('|')})\-/ } From 1574595cab9ebbe7e71755ee15629e8656f1910e Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Thu, 21 Mar 2013 22:37:22 +0400 Subject: [PATCH 12/24] Fix: undefined method 'touch' for Grit::Commit --- lib/ext/git/grit.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/ext/git/grit.rb b/lib/ext/git/grit.rb index 6200a079f..596d2a4fd 100644 --- a/lib/ext/git/grit.rb +++ b/lib/ext/git/grit.rb @@ -1,5 +1,14 @@ # -*- encoding : utf-8 -*- module Grit + class Commit + + # Fix: NoMethodError: undefined method 'touch' for Grit::Commit + # see: model Comment belongs_to :commentable + def touch + true + end + end + class Blob include Linguist::BlobHelper From 2da7db221797a8c264e0d06344a32e24e11d1177 Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Fri, 22 Mar 2013 01:43:00 +0400 Subject: [PATCH 13/24] add comments to code --- lib/abf_worker/build_lists_publish_task_manager.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/abf_worker/build_lists_publish_task_manager.rb b/lib/abf_worker/build_lists_publish_task_manager.rb index 73854c766..3cae6d46f 100644 --- a/lib/abf_worker/build_lists_publish_task_manager.rb +++ b/lib/abf_worker/build_lists_publish_task_manager.rb @@ -238,6 +238,9 @@ module AbfWorker build_lists = build_lists.limit(50) project_ids = build_lists.pluck(:project_id).uniq + # Projects which should be removed: + # - /#{save_to_repository_id}\-#{build_for_platform_id}$/ - from repository; + # - /^(#{project_ids.join('|')})\-/ - not published in current transaction. projects_for_cleanup = @redis.lrange(PROJECTS_FOR_CLEANUP, 0, -1). select{ |k| k =~ /#{save_to_repository_id}\-#{build_for_platform_id}$/ && k !~ /^(#{project_ids.join('|')})\-/ } From 67cf79fac2a7ff77ee2e0b4a28849e83e713bdae Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Fri, 22 Mar 2013 15:43:22 +0400 Subject: [PATCH 14/24] #46: not publish new builds into repository if project of builds has been removed from repository --- .../build_lists_publish_task_manager.rb | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/abf_worker/build_lists_publish_task_manager.rb b/lib/abf_worker/build_lists_publish_task_manager.rb index 3cae6d46f..bba427d49 100644 --- a/lib/abf_worker/build_lists_publish_task_manager.rb +++ b/lib/abf_worker/build_lists_publish_task_manager.rb @@ -228,6 +228,17 @@ module AbfWorker end def create_rpm_build_task(save_to_repository_id, build_for_platform_id) + projects_for_cleanup = @redis.lrange(PROJECTS_FOR_CLEANUP, 0, -1). + select{ |k| k =~ /#{save_to_repository_id}\-#{build_for_platform_id}$/ } + + # We should not to publish new builds into repository + # if project of builds has been removed from repository. + BuildList.where( + :project_id => projects_for_cleanup.map{ |k| k.split('-')[0] }.uniq, + :save_to_repository_id => repository.id, + :status => BuildList::BUILD_PUBLISH + ).update_all(:status => BuildList::FAILED_PUBLISH) + build_lists = BuildList. where(:new_core => true, :status => BuildList::BUILD_PUBLISH). where(:save_to_repository_id => save_to_repository_id). @@ -237,13 +248,6 @@ module AbfWorker build_lists = build_lists.where('build_lists.id NOT IN (?)', locked_ids) unless locked_ids.empty? build_lists = build_lists.limit(50) - project_ids = build_lists.pluck(:project_id).uniq - # Projects which should be removed: - # - /#{save_to_repository_id}\-#{build_for_platform_id}$/ - from repository; - # - /^(#{project_ids.join('|')})\-/ - not published in current transaction. - projects_for_cleanup = @redis.lrange(PROJECTS_FOR_CLEANUP, 0, -1). - select{ |k| k =~ /#{save_to_repository_id}\-#{build_for_platform_id}$/ && k !~ /^(#{project_ids.join('|')})\-/ } - old_packages = {:sources => [], :binaries => {:x86_64 => [], :i586 => []}} projects_for_cleanup.each do |key| From 8fec7cb13315b8ef1375c39f487036ca626fa01f Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Fri, 22 Mar 2013 15:46:22 +0400 Subject: [PATCH 15/24] #46: update comment to code --- lib/abf_worker/publish_observer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/abf_worker/publish_observer.rb b/lib/abf_worker/publish_observer.rb index 56dee3ac3..a6166535a 100644 --- a/lib/abf_worker/publish_observer.rb +++ b/lib/abf_worker/publish_observer.rb @@ -36,7 +36,7 @@ module AbfWorker update_results build_list case status when COMPLETED - # 'update_column' - when build_list has been removed from repository + # 'update_column' - when project of build_list has been removed from repository build_list.published || build_list.update_column(:status, BuildList::BUILD_PUBLISHED) when FAILED, CANCELED build_list.fail_publish || build_list.update_column(:status, BuildList::FAILED_PUBLISH) From 03dc35b70e9c0eae1557e8f559ddc124a35c12e3 Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Fri, 22 Mar 2013 15:59:33 +0400 Subject: [PATCH 16/24] #46: hot fix --- lib/abf_worker/build_lists_publish_task_manager.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/abf_worker/build_lists_publish_task_manager.rb b/lib/abf_worker/build_lists_publish_task_manager.rb index bba427d49..e8a9e88c7 100644 --- a/lib/abf_worker/build_lists_publish_task_manager.rb +++ b/lib/abf_worker/build_lists_publish_task_manager.rb @@ -235,7 +235,7 @@ module AbfWorker # if project of builds has been removed from repository. BuildList.where( :project_id => projects_for_cleanup.map{ |k| k.split('-')[0] }.uniq, - :save_to_repository_id => repository.id, + :save_to_repository_id => save_to_repository_id, :status => BuildList::BUILD_PUBLISH ).update_all(:status => BuildList::FAILED_PUBLISH) From 0ae460682785bec7adcdb3fb3d0cc452f18ed9cb Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Fri, 22 Mar 2013 16:13:25 +0400 Subject: [PATCH 17/24] #46: update specs, fix '[]' for false:FalseClass --- lib/abf_worker/base_observer.rb | 2 +- .../build_lists_publish_task_manager_spec.rb | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/abf_worker/base_observer.rb b/lib/abf_worker/base_observer.rb index d7d1ec01a..cc217648f 100644 --- a/lib/abf_worker/base_observer.rb +++ b/lib/abf_worker/base_observer.rb @@ -32,7 +32,7 @@ module AbfWorker def sort_results_and_save(results, item = subject) item.results = results.sort_by{ |r| r['file_name'] } - item.save(false) + item.save(:validate => false) end end diff --git a/spec/lib/abf-worker/build_lists_publish_task_manager_spec.rb b/spec/lib/abf-worker/build_lists_publish_task_manager_spec.rb index d33481f48..dc95cea49 100644 --- a/spec/lib/abf-worker/build_lists_publish_task_manager_spec.rb +++ b/spec/lib/abf-worker/build_lists_publish_task_manager_spec.rb @@ -186,11 +186,18 @@ describe AbfWorker::BuildListsPublishTaskManager do :save_to_repository => build_list.save_to_repository, :build_for_platform => build_list.build_for_platform ) } + let(:build_list3) { FactoryGirl.create(:build_list_core, + :new_core => true, + :save_to_platform => build_list.save_to_platform, + :save_to_repository => build_list.save_to_repository, + :build_for_platform => build_list.build_for_platform + ) } before do stub_redis build_list.update_column(:status, BuildList::BUILD_PUBLISH) build_list2.update_column(:status, BuildList::BUILD_PUBLISHED) - ProjectToRepository.where(:project_id => build_list.project_id, :repository_id => build_list.save_to_repository_id).destroy_all + build_list3.update_column(:status, BuildList::BUILD_PUBLISHED) + ProjectToRepository.where(:project_id => build_list3.project_id, :repository_id => build_list3.save_to_repository_id).destroy_all 2.times{ subject.new.run } end @@ -212,7 +219,7 @@ describe AbfWorker::BuildListsPublishTaskManager do it "ensure that 'locked projects for cleanup' has only one item" do queue = @redis_instance.lrange(subject::LOCKED_PROJECTS_FOR_CLEANUP, 0, -1) queue.should have(1).item - queue.should include("#{build_list.project_id}-#{build_list.save_to_repository_id}-#{build_list.build_for_platform_id}") + queue.should include("#{build_list3.project_id}-#{build_list3.save_to_repository_id}-#{build_list3.build_for_platform_id}") end it "ensure that new task for publishing has been created" do From f746b39093c9cbad8940351d132c0b0bafe2a76a Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Fri, 22 Mar 2013 16:58:06 +0400 Subject: [PATCH 18/24] #40: change links to buttons on mass_builds page --- app/assets/stylesheets/design/custom.scss | 4 ++++ app/views/platforms/mass_builds/index.html.haml | 17 ++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/design/custom.scss b/app/assets/stylesheets/design/custom.scss index 180de2816..5f55d1c88 100644 --- a/app/assets/stylesheets/design/custom.scss +++ b/app/assets/stylesheets/design/custom.scss @@ -982,6 +982,10 @@ table.diff { } /* Mass build forms */ +.publish-mass-build form input { + margin-bottom: 5px; +} + form.mass_build input[type="checkbox"] { width: 10px; height: 11px; diff --git a/app/views/platforms/mass_builds/index.html.haml b/app/views/platforms/mass_builds/index.html.haml index cf8ffa71e..5832ee65b 100644 --- a/app/views/platforms/mass_builds/index.html.haml +++ b/app/views/platforms/mass_builds/index.html.haml @@ -43,16 +43,19 @@ = mass_build.read_attribute 'missed_projects_count' %td =link_to_list @platform, mass_build, 'failed_builds_list' - %td.right + %td.right.publish-mass-build - if can?(:publish, mass_build) - - if mass_build.auto_publish? - = link_to t("layout.mass_builds.publish_success"), publish_platform_mass_build_path(@platform, mass_build.id), :method => :post - %br - = link_to t("layout.mass_builds.publish_test_failed"), publish_platform_mass_build_path(@platform, mass_build.id, :status => 'test_failed'), :method => :post - %br + - url = publish_platform_mass_build_path(@platform, mass_build.id) + - unless mass_build.auto_publish? + = form_for :build, :url => url do |f| + = f.submit t("layout.mass_builds.publish_success") + = form_for :build, :url => url do |f| + = hidden_field_tag "build-status", 'test_failed', :name => "status" + = f.submit t("layout.mass_builds.publish_test_failed") - if can?(:cancel, mass_build) - = link_to image_tag('x.png'), cancel_platform_mass_build_path(@platform, mass_build.id), :method => :post, :confirm => t('layout.mass_builds.cancel_confirm') + = form_for :build, :url => cancel_platform_mass_build_path(@platform, mass_build.id) do |f| + = f.submit t('layout.cancel'), :confirm => t('layout.mass_builds.cancel_confirm') %td %a.toggle_btn{:href => "#toggle_#{ mass_build.id }", :'data-target' => "#toggle_#{ mass_build.id }"}= t("layout.mass_builds.extended_data") .toggle{:id => "toggle_#{ mass_build.id }"} From 580068f93da237571cc1d953032af78692ef8e8d Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Fri, 22 Mar 2013 17:05:14 +0400 Subject: [PATCH 19/24] #40: rename css class --- app/assets/stylesheets/design/custom.scss | 2 +- app/views/platforms/mass_builds/index.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/design/custom.scss b/app/assets/stylesheets/design/custom.scss index 5f55d1c88..96a2997bc 100644 --- a/app/assets/stylesheets/design/custom.scss +++ b/app/assets/stylesheets/design/custom.scss @@ -982,7 +982,7 @@ table.diff { } /* Mass build forms */ -.publish-mass-build form input { +.mass-build-actions form input { margin-bottom: 5px; } diff --git a/app/views/platforms/mass_builds/index.html.haml b/app/views/platforms/mass_builds/index.html.haml index 5832ee65b..0a3309580 100644 --- a/app/views/platforms/mass_builds/index.html.haml +++ b/app/views/platforms/mass_builds/index.html.haml @@ -43,7 +43,7 @@ = mass_build.read_attribute 'missed_projects_count' %td =link_to_list @platform, mass_build, 'failed_builds_list' - %td.right.publish-mass-build + %td.right.mass-build-actions - if can?(:publish, mass_build) - url = publish_platform_mass_build_path(@platform, mass_build.id) - unless mass_build.auto_publish? From 817715b9c44adb254068009a22e933fa2a4dec26 Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Fri, 22 Mar 2013 18:29:18 +0400 Subject: [PATCH 20/24] #40: small refactoring of DOM model --- app/assets/stylesheets/design/custom.scss | 2 +- .../platforms/mass_builds/index.html.haml | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/design/custom.scss b/app/assets/stylesheets/design/custom.scss index 96a2997bc..3596c1973 100644 --- a/app/assets/stylesheets/design/custom.scss +++ b/app/assets/stylesheets/design/custom.scss @@ -982,7 +982,7 @@ table.diff { } /* Mass build forms */ -.mass-build-actions form input { +.mass-build-actions a.button { margin-bottom: 5px; } diff --git a/app/views/platforms/mass_builds/index.html.haml b/app/views/platforms/mass_builds/index.html.haml index 0a3309580..a5276ea0e 100644 --- a/app/views/platforms/mass_builds/index.html.haml +++ b/app/views/platforms/mass_builds/index.html.haml @@ -45,17 +45,18 @@ =link_to_list @platform, mass_build, 'failed_builds_list' %td.right.mass-build-actions - if can?(:publish, mass_build) - - url = publish_platform_mass_build_path(@platform, mass_build.id) - unless mass_build.auto_publish? - = form_for :build, :url => url do |f| - = f.submit t("layout.mass_builds.publish_success") - = form_for :build, :url => url do |f| - = hidden_field_tag "build-status", 'test_failed', :name => "status" - = f.submit t("layout.mass_builds.publish_test_failed") - + = link_to t('layout.mass_builds.publish_success'), + publish_platform_mass_build_path(@platform, mass_build.id), + :method => :post, :confirm => t("layout.confirm"), :class => 'button' + = link_to t('layout.mass_builds.publish_test_failed'), + publish_platform_mass_build_path(@platform, mass_build.id, :status => 'test_failed'), + :method => :post, :confirm => t("layout.confirm"), :class => 'button' - if can?(:cancel, mass_build) - = form_for :build, :url => cancel_platform_mass_build_path(@platform, mass_build.id) do |f| - = f.submit t('layout.cancel'), :confirm => t('layout.mass_builds.cancel_confirm') + = link_to t('layout.cancel'), + cancel_platform_mass_build_path(@platform, mass_build.id), + :method => :post, :class => 'button', + :confirm => t('layout.mass_builds.cancel_confirm') %td %a.toggle_btn{:href => "#toggle_#{ mass_build.id }", :'data-target' => "#toggle_#{ mass_build.id }"}= t("layout.mass_builds.extended_data") .toggle{:id => "toggle_#{ mass_build.id }"} From 3f6afeacb10f7b07c91336aed153839666a89b86 Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Fri, 22 Mar 2013 22:31:24 +0400 Subject: [PATCH 21/24] update product_build_list Factory --- spec/factories/product_build_lists.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/factories/product_build_lists.rb b/spec/factories/product_build_lists.rb index 4c2a12e2b..978851e86 100644 --- a/spec/factories/product_build_lists.rb +++ b/spec/factories/product_build_lists.rb @@ -9,5 +9,8 @@ FactoryGirl.define do params 'ENV=i586' time_living 150 project_version 'master' + + # see: before_validation in ProductBuildList model + before(:create) { Arch.find_or_create_by_name('x86_64') } end end From 4d046dd99062fc7ffec34ee57d56cdd6b0ae5804 Mon Sep 17 00:00:00 2001 From: Alexander Machehin Date: Mon, 25 Mar 2013 16:34:37 +0600 Subject: [PATCH 22/24] add exitstatus to debug merging pull requests --- app/models/pull_request.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/pull_request.rb b/app/models/pull_request.rb index 446e839a2..47a2ff04d 100644 --- a/app/models/pull_request.rb +++ b/app/models/pull_request.rb @@ -87,7 +87,7 @@ class PullRequest < ActiveRecord::Base system("git reset --hard HEAD^") # for diff maybe FIXME if old_commit.id == to_project.repo.commits(to_ref).first.id - raise "merge result pull_request #{id}: #{res}; #{res2}" + raise "merge result pull_request #{id}: #{$?.exitstatus}; #{res2}; #{res}" end set_user_and_time who merging From 654ad10c6d23fc72d53602b7199fe97251a9db6e Mon Sep 17 00:00:00 2001 From: "Vladmir Sharhsov(warpc)" Date: Mon, 25 Mar 2013 16:16:46 +0400 Subject: [PATCH 23/24] New favicon --- public/favicon.ico | Bin 1150 -> 1150 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/public/favicon.ico b/public/favicon.ico index 4ec0d298859a6e33a6cc40f7a502354b8cad5f76..f7966978df47c8be66e4f904c752bf521e6fdaa3 100644 GIT binary patch literal 1150 zcmbtT&ubGw6rP~sLBxV6c&i6d5R}k;lT;7`p2TzUAU%0dk>W|bF6i!T6I(0jAK*!> z^x#QEB;?2JCQW0lrIZqil+aTNA|gdX7{9mK32DHS8@|l!`|-W+y_ppvh2Pkiz%wrH z9Tws=b{7CPjRUFpUx*xhqmMBr6~lE84fe3omiB1+W8u;<%dSP(;3c3-hH66zA$OD z@BHFe^ju*MwyhL0u+u8?HSDc0AhY%I3fnk_bRcKoE8=|{z9!a2a}GI*y4Zt@c47|9 z1N#!NhIsJEccDkuSwq!#?m%Y*d$;*4&TS*NiCC`D09Jt?-xjj{St@#ud8T9ifqV>K zd*cP`r=Q^9c*agLN7lkRBIw_&VU|1x#Obm(F@ZClN&1B@r_P$O_WI{WJ>;20QwKiQ zq0uMLVW|?x%dDZ|IS;_czP%Ob$DV5rfcGAsA=d`SHr|Wnr%oo;ucU{1n_Q3mTzfdz z?ky*uF>GBRs`~OO<~EJ<{QdoSyEsAdL2x4!bzt0Wa8QZVDyUeWvXReM~Hpi~R-$FP)c@i8C5#L5{ tJ?=dk`{`5GKU|A#LKyQu_zO6;hPD6z literal 1150 zcmZvc&rcIU6vv0?T~3}Vg?4v#yIp9zU1&k*FC;0yN&pE-r71xP79vXcVT>0+io~c1 z@uXK{B+(d6OeFpaN@9YEe?#HwsR-_La3ytienMto~)nRwQ- ztEU+|%NSFI377ELelaj6V$~l2tkU-O_ua*{^@FsA{g}@8q9dxKtviRl!Uob8+Ta!EAg$QvH?ga$D;u=7jC-I3O|)O%LUjBohDWow zICCGdrSIq+zm4V)7dd%ttzd{992)p}!V915J%{L}2T0!ff%9UY*uq!DZ@fotY!u;; zX657PiH$^CemQGdZh)@35gRKaxwM1C!WSeLx6xjBh3x1cW+(a)Yd0au_8EwEcrMo? z6gJQ~@d&A#Hk+9Lgv9)3Bo@CRI{6%#ksRiyhLGurTeTsU&cH?9ej|$5l_!YLeL(Em z8!>K)-xsXdBQ^KV${{lK5~)lcopIBu6|wgHNl^7h_yW!FH3p#g6d1kJ)*Ym0pP{F_ z6SAg53mD*f@Z|i&p43m&UQtACgmJkJTBrpbb03kpyo^>;fh5%*$L-vy=EPDDIQ`?? zBaNIlpe_Fx10z}ZRJ-Ptai@QXr(T?%DZH+X6CdcvAYh0yD}QTF8q!wwuhYl$ZW!U< z0rjZWr<9K>?&f$+vH!h44o_ogZ{NMVvQ|u{(z`B~x9oCBWtx+ZVwGB&AG#SUpJ8nK U6k~U4Y^(l{aMn(ow8Rkm2iEifo&W#< From 13c349b65e4a5af03a6cd9078e0c263305c6cad7 Mon Sep 17 00:00:00 2001 From: "Vladmir Sharhsov(warpc)" Date: Mon, 25 Mar 2013 16:32:44 +0400 Subject: [PATCH 24/24] Add logo for Google Oauth --- public/pics/abf-logo.png | Bin 0 -> 8787 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/pics/abf-logo.png diff --git a/public/pics/abf-logo.png b/public/pics/abf-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..dfc93268ca200b842c70629977a8432b879db362 GIT binary patch literal 8787 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 JPDHLkV1lvE_%{Fm literal 0 HcmV?d00001