Skip to content

Commit

Permalink
Merge pull request #6224 from bgeuken/staging_project
Browse files Browse the repository at this point in the history
Add StagingProject model
  • Loading branch information
bgeuken committed Nov 14, 2018
2 parents 82a2101 + c6295bb commit c2bb93c
Show file tree
Hide file tree
Showing 14 changed files with 371 additions and 24 deletions.
3 changes: 1 addition & 2 deletions src/api/app/controllers/staging/projects_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ class Staging::ProjectsController < ApplicationController
private

def set_project
@project = Project.find_by(name: params[:project])
raise ActiveRecord::RecordNotFound unless @project
@project = Staging::StagingProject.find_by!(name: params[:project])
end
end
6 changes: 0 additions & 6 deletions src/api/app/models/project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@ def autocomplete(search)
has_many :target_of_bs_requests, through: :target_of_bs_request_actions, source: :bs_request

has_one :staging, class_name: 'Staging::Workflow', inverse_of: :project
belongs_to :staging_workflow, inverse_of: :staging_projects, class_name: 'Staging::Workflow'
has_many :staged_requests, class_name: 'BsRequest', foreign_key: 'staging_project_id', dependent: :nullify

default_scope { where('projects.id not in (?)', Relationship.forbidden_project_ids) }

Expand Down Expand Up @@ -1692,10 +1690,6 @@ def dashboard
packages.find_by(name: 'dashboard')
end

def staging_identifier
name[/.*:Staging:(.*)/, 1]
end

private

def discard_cache
Expand Down
143 changes: 143 additions & 0 deletions src/api/app/models/staging/staging_project.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
module Staging
class StagingProject < Project
has_many :staged_requests, class_name: 'BsRequest', foreign_key: :staging_project_id, dependent: :nullify
has_many :status_reports, through: :repositories, inverse_of: :checkable
belongs_to :staging_workflow, class_name: 'Staging::Workflow'

def staging_identifier
name[/.*:Staging:(.*)/, 1]
end

def untracked_requests
requests_to_review - staged_requests
end

# The difference between staged requests and requests to review is that staged requests are assigned to the staging project.
# The requests to review are requests which are not related to the staging project (unless they are also staged).
# They simply need a review from the maintainers of the staging project.
def requests_to_review
@requests_to_review ||= BsRequest.with_open_reviews_for(by_project: name)
end

def building_repositories
set_buildinfo unless @building_repositories
@building_repositories
end

def broken_packages
set_buildinfo unless @broken_packages
@broken_packages
end

def missing_reviews
return @missing_reviews if @missing_reviews

@missing_reviews = []
attribs = [:by_group, :by_user, :by_project, :by_package]

(requests_to_review + staged_requests).uniq.each do |request|
request.reviews.each do |review|
next if review.state == :accepted || review.by_project == name
# FIXME: this loop (and the inner if) would not be needed
# if every review only has one valid by_xxx.
# I'm keeping it to mimic the python implementation.
# Instead, we could have something like
# who = review.by_group || review.by_user || review.by_project || review.by_package
attribs.each do |att|
who = review.send(att)
if who
@missing_reviews << { id: review.id, request: request.number, state: review.state.to_s, package: request.first_target_package, by: who }
end
end
end
end

@missing_reviews
end

def overall_state
@overall_state ||= state
end

private

def state
return :empty unless staged_requests.exists?
return :unacceptable if untracked_requests.present? || staged_requests.obsolete.present?

overall_state = build_state
overall_state = check_state if overall_state == :acceptable

return :review if overall_state == :acceptable && missing_reviews.present?

overall_state
end

def build_state
set_buildinfo

return :building if building_repositories.present?
return :failed if broken_packages.present?

:acceptable
end

def relevant_status_reports
@relevant_status_reports ||= status_reports.where(uuid: repositories.map(&:build_id))
end

def missing_checks?
relevant_status_reports.any? { |report| report.missing_checks.present? }
end

def check_state
status_checks = Status::Check.where(status_reports_id: relevant_status_reports)
return :testing if missing_checks? || status_checks.pending.exists?
return :failed if status_checks.failed.exists?
return :acceptable
end

def set_buildinfo
buildresult = Xmlhash.parse(Backend::Api::BuildResults::Status.failed_results(name))

@broken_packages = []
@building_repositories = []

buildresult.elements('result') do |result|
building = ['published', 'unpublished'].exclude?(result['state']) || result['dirty'] == 'true'

result.elements('status') do |status|
code = status.get('code')

if code.in?(['broken', 'failed']) || (code == 'unresolvable' && !building)
@broken_packages << { package: status['package'],
project: name,
state: code,
details: status['details'],
repository: result['repository'],
arch: result['arch'] }
end
end

if building
current_repo = result.slice('repository', 'arch', 'code', 'state', 'dirty')
current_repo[:tobuild] = 0
current_repo[:final] = 0

buildresult = Buildresult.find_hashed(project: name, view: 'summary', repository: current_repo['repository'], arch: current_repo['arch'])
buildresult = buildresult.get('result').get('summary')
buildresult.elements('statuscount') do |status_count|
if status_count['code'].in?(['excluded', 'broken', 'failed', 'unresolvable', 'succeeded', 'excluded', 'disabled'])
current_repo[:final] += status_count['count'].to_i
else
current_repo[:tobuild] += status_count['count'].to_i
end
end
@building_repositories << current_repo
end
end

@broken_packages.reject! { |package| package['state'] == 'unresolvable' } if @building_repositories.present?
end
end
end
5 changes: 3 additions & 2 deletions src/api/app/models/staging/workflow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ def self.table_name_prefix
end

belongs_to :project, inverse_of: :staging
has_many :staging_projects, class_name: 'Project', inverse_of: :staging_workflow, dependent: :nullify, foreign_key: 'staging_workflow_id' do
has_many :staging_projects, class_name: 'Staging::StagingProject', inverse_of: :staging_workflow, dependent: :nullify,
foreign_key: 'staging_workflow_id' do
def without_staged_requests
left_outer_joins(:staged_requests).where(bs_requests: { id: nil })
end
Expand Down Expand Up @@ -40,7 +41,7 @@ def ignored_requests

def create_staging_projects
['A', 'B'].each do |letter|
staging_project = Project.find_or_initialize_by(name: "#{project.name}:Staging:#{letter}")
staging_project = Staging::StagingProject.find_or_initialize_by(name: "#{project.name}:Staging:#{letter}")
next if staging_project.staging_workflow # if it belongs to another staging workflow skip it
staging_project.staging_workflow = self
staging_project.store
Expand Down
2 changes: 2 additions & 0 deletions src/api/app/models/status/check.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class Status::Check < ApplicationRecord
#### Callbacks macros: before_save, after_save, etc.

#### Scopes (first the default_scope macro if is used)
scope :pending, -> { where(state: 'pending') }
scope :failed, -> { where(state: ['error', 'failure']) }

#### Validations macros

Expand Down
1 change: 1 addition & 0 deletions src/api/app/views/models/_staging_project.xml.builder

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions src/api/spec/factories/bs_requests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
target_repository { nil }
reviewer { nil }
request_state { nil }
review_by_project { nil }
end

before(:create) do |request, evaluator|
Expand Down Expand Up @@ -79,6 +80,21 @@
end
end

factory :review_bs_request_by_project do
after(:create) do |request, evaluator|
request.bs_request_actions.delete_all
request.bs_request_actions << create(
:bs_request_action_submit,
target_project: evaluator.target_project,
target_package: evaluator.target_package,
source_project: evaluator.source_project,
source_package: evaluator.source_package
)
request.reviews << Review.new(by_project: evaluator.review_by_project)
request.state = 'review'
request.save!
end
end
factory :review_bs_request_by_group do
after(:create) do |request, evaluator|
request.bs_request_actions.delete_all
Expand Down
8 changes: 0 additions & 8 deletions src/api/spec/factories/project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,5 @@
end
end
end

factory :staging_project do
transient do
sequence(:workflow_project_name) { |n| "workflow_project_#{n}" }
end

sequence(:name, [*'A'..'Z'].cycle) { |letter| "#{workflow_project_name}:Staging:#{letter}" }
end
end
end
5 changes: 5 additions & 0 deletions src/api/spec/factories/staging_projects.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FactoryBot.define do
factory :staging_project, class: 'Staging::StagingProject', parent: :project do
sequence(:name, [*'A'..'Z'].cycle) { |letter| "#{staging_workflow.project.name}:Staging:#{letter}" }
end
end
6 changes: 0 additions & 6 deletions src/api/spec/models/project_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -603,10 +603,4 @@ def reset_project_in_backend
'sourceaccess' => [['enable', {}]], 'access' => [['enable', {}]])
}
end

describe '#staging_identifier' do
let(:staging_project) { create(:project, name: 'openSUSE_41:Staging:myStagingProject') }

it { expect(staging_project.staging_identifier).to eq('myStagingProject') }
end
end

0 comments on commit c2bb93c

Please sign in to comment.