diff options
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/ci/build.rb | 39 | ||||
-rw-r--r-- | app/models/ci/job_artifact.rb | 2 | ||||
-rw-r--r-- | app/models/ci/pipeline.rb | 23 | ||||
-rw-r--r-- | app/models/clusters/cluster.rb | 17 | ||||
-rw-r--r-- | app/models/concerns/deployable.rb | 29 | ||||
-rw-r--r-- | app/models/deploy_token.rb | 5 | ||||
-rw-r--r-- | app/models/deployment.rb | 76 | ||||
-rw-r--r-- | app/models/environment.rb | 4 | ||||
-rw-r--r-- | app/models/environment_status.rb | 6 | ||||
-rw-r--r-- | app/models/issue.rb | 14 | ||||
-rw-r--r-- | app/models/label.rb | 2 | ||||
-rw-r--r-- | app/models/members_preloader.rb | 16 | ||||
-rw-r--r-- | app/models/namespace.rb | 6 | ||||
-rw-r--r-- | app/models/project.rb | 14 | ||||
-rw-r--r-- | app/models/project_services/issue_tracker_service.rb | 4 | ||||
-rw-r--r-- | app/models/repository.rb | 4 |
16 files changed, 182 insertions, 79 deletions
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index d7eab57763e..360c9924a7d 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -9,19 +9,18 @@ module Ci include Presentable include Importable include Gitlab::Utils::StrongMemoize + include Deployable belongs_to :project, inverse_of: :builds belongs_to :runner belongs_to :trigger_request belongs_to :erased_by, class_name: 'User' - has_many :deployments, as: :deployable - RUNNER_FEATURES = { upload_multiple_artifacts: -> (build) { build.publishes_artifacts_reports? } }.freeze - has_one :last_deployment, -> { order('deployments.id DESC') }, as: :deployable, class_name: 'Deployment' + has_one :deployment, as: :deployable, class_name: 'Deployment' has_many :trace_sections, class_name: 'Ci::BuildTraceSection' has_many :trace_chunks, class_name: 'Ci::BuildTraceChunk', foreign_key: :build_id @@ -195,6 +194,8 @@ module Ci end after_transition pending: :running do |build| + build.deployment&.run + build.run_after_commit do BuildHooksWorker.perform_async(id) end @@ -207,14 +208,18 @@ module Ci end after_transition any => [:success] do |build| + build.deployment&.succeed + build.run_after_commit do - BuildSuccessWorker.perform_async(id) PagesWorker.perform_async(:deploy, id) if build.pages_generator? end end before_transition any => [:failed] do |build| next unless build.project + + build.deployment&.drop + next if build.retries_max.zero? if build.retries_count < build.retries_max @@ -233,6 +238,10 @@ module Ci after_transition running: any do |build| Ci::BuildRunnerSession.where(build: build).delete_all end + + after_transition any => [:skipped, :canceled] do |build| + build.deployment&.cancel + end end def ensure_metadata @@ -342,8 +351,12 @@ module Ci self.options.fetch(:environment, {}).fetch(:action, 'start') if self.options end + def has_deployment? + !!self.deployment + end + def outdated_deployment? - success? && !last_deployment.try(:last?) + success? && !deployment.try(:last?) end def depends_on_builds @@ -358,6 +371,10 @@ module Ci user == current_user end + def on_stop + options&.dig(:environment, :on_stop) + end + # A slugified version of the build ref, suitable for inclusion in URLs and # domain names. Rules: # @@ -725,7 +742,7 @@ module Ci if success? return successful_deployment_status - elsif complete? && !success? + elsif failed? return :failed end @@ -742,13 +759,11 @@ module Ci end def successful_deployment_status - if success? && last_deployment&.last? - return :last - elsif success? && last_deployment.present? - return :out_of_date + if deployment&.last? + :last + else + :out_of_date end - - :creating end def each_report(report_types) diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index 34a889057ab..11c88200c37 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -15,7 +15,7 @@ module Ci metadata: nil, trace: nil, junit: 'junit.xml', - codequality: 'codequality.json', + codequality: 'gl-code-quality-report.json', sast: 'gl-sast-report.json', dependency_scanning: 'gl-dependency-scanning-report.json', container_scanning: 'gl-container-scanning-report.json', diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index aeee7f0a5d2..56010e899a4 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -181,22 +181,31 @@ module Ci # # ref - The name (or names) of the branch(es)/tag(s) to limit the list of # pipelines to. - def self.newest_first(ref = nil) + # limit - This limits a backlog search, default to 100. + def self.newest_first(ref: nil, limit: 100) relation = order(id: :desc) + relation = relation.where(ref: ref) if ref + + if limit + ids = relation.limit(limit).select(:id) + # MySQL does not support limit in subquery + ids = ids.pluck(:id) if Gitlab::Database.mysql? + relation = relation.where(id: ids) + end - ref ? relation.where(ref: ref) : relation + relation end def self.latest_status(ref = nil) - newest_first(ref).pluck(:status).first + newest_first(ref: ref).pluck(:status).first end def self.latest_successful_for(ref) - newest_first(ref).success.take + newest_first(ref: ref).success.take end def self.latest_successful_for_refs(refs) - relation = newest_first(refs).success + relation = newest_first(ref: refs).success relation.each_with_object({}) do |pipeline, hash| hash[pipeline.ref] ||= pipeline @@ -238,6 +247,10 @@ module Ci end end + def self.latest_successful_ids_per_project + success.group(:project_id).select('max(id) as id') + end + def self.truncate_sha(sha) sha[0...8] end diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb index 2bd373e0950..e80d35d0f3c 100644 --- a/app/models/clusters/cluster.rb +++ b/app/models/clusters/cluster.rb @@ -3,6 +3,7 @@ module Clusters class Cluster < ActiveRecord::Base include Presentable + include Gitlab::Utils::StrongMemoize self.table_name = 'clusters' @@ -24,9 +25,6 @@ module Clusters has_many :cluster_groups, class_name: 'Clusters::Group' has_many :groups, through: :cluster_groups, class_name: '::Group' - has_one :cluster_group, -> { order(id: :desc) }, class_name: 'Clusters::Group' - has_one :group, through: :cluster_group, class_name: '::Group' - # we force autosave to happen when we save `Cluster` model has_one :provider_gcp, class_name: 'Clusters::Providers::Gcp', autosave: true @@ -119,12 +117,19 @@ module Clusters end def first_project - return @first_project if defined?(@first_project) - - @first_project = projects.first + strong_memoize(:first_project) do + projects.first + end end alias_method :project, :first_project + def first_group + strong_memoize(:first_group) do + groups.first + end + end + alias_method :group, :first_group + def kubeclient platform_kubernetes.kubeclient if kubernetes? end diff --git a/app/models/concerns/deployable.rb b/app/models/concerns/deployable.rb new file mode 100644 index 00000000000..f4f1989f0a9 --- /dev/null +++ b/app/models/concerns/deployable.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Deployable + extend ActiveSupport::Concern + + included do + after_create :create_deployment + + def create_deployment + return unless starts_environment? && !has_deployment? + + environment = project.environments.find_or_create_by( + name: expanded_environment_name + ) + + environment.deployments.create!( + project_id: environment.project_id, + environment: environment, + ref: ref, + tag: tag, + sha: sha, + user: user, + deployable: self, + on_stop: on_stop).tap do |_| + self.reload # Reload relationships + end + end + end +end diff --git a/app/models/deploy_token.rb b/app/models/deploy_token.rb index 0b2eedf3631..e3524305346 100644 --- a/app/models/deploy_token.rb +++ b/app/models/deploy_token.rb @@ -4,6 +4,7 @@ class DeployToken < ActiveRecord::Base include Expirable include TokenAuthenticatable include PolicyActor + include Gitlab::Utils::StrongMemoize add_authentication_token_field :token AVAILABLE_SCOPES = %i(read_repository read_registry).freeze @@ -49,7 +50,9 @@ class DeployToken < ActiveRecord::Base # to a single project, later we're going to extend # that to be for multiple projects and namespaces. def project - projects.first + strong_memoize(:project) do + projects.first + end end def expires_at diff --git a/app/models/deployment.rb b/app/models/deployment.rb index 37efbb04fce..54a900a3b85 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -3,6 +3,7 @@ class Deployment < ActiveRecord::Base include AtomicInternalId include IidRoutes + include AfterCommitQueue belongs_to :project, required: true belongs_to :environment, required: true @@ -16,11 +17,44 @@ class Deployment < ActiveRecord::Base delegate :name, to: :environment, prefix: true - after_create :create_ref - after_create :invalidate_cache - scope :for_environment, -> (environment) { where(environment_id: environment) } + state_machine :status, initial: :created do + event :run do + transition created: :running + end + + event :succeed do + transition any - [:success] => :success + end + + event :drop do + transition any - [:failed] => :failed + end + + event :cancel do + transition any - [:canceled] => :canceled + end + + before_transition any => [:success, :failed, :canceled] do |deployment| + deployment.finished_at = Time.now + end + + after_transition any => :success do |deployment| + deployment.run_after_commit do + Deployments::SuccessWorker.perform_async(id) + end + end + end + + enum status: { + created: 0, + running: 1, + success: 2, + failed: 3, + canceled: 4 + } + def self.last_for_environment(environment) ids = self .for_environment(environment) @@ -69,15 +103,15 @@ class Deployment < ActiveRecord::Base end def update_merge_request_metrics! - return unless environment.update_merge_request_metrics? + return unless environment.update_merge_request_metrics? && success? merge_requests = project.merge_requests .joins(:metrics) .where(target_branch: self.ref, merge_request_metrics: { first_deployed_to_production_at: nil }) - .where("merge_request_metrics.merged_at <= ?", self.created_at) + .where("merge_request_metrics.merged_at <= ?", finished_at) if previous_deployment - merge_requests = merge_requests.where("merge_request_metrics.merged_at >= ?", previous_deployment.created_at) + merge_requests = merge_requests.where("merge_request_metrics.merged_at >= ?", previous_deployment.finished_at) end # Need to use `map` instead of `select` because MySQL doesn't allow `SELECT`ing from the same table @@ -91,7 +125,7 @@ class Deployment < ActiveRecord::Base MergeRequest::Metrics .where(merge_request_id: merge_request_ids, first_deployed_to_production_at: nil) - .update_all(first_deployed_to_production_at: self.created_at) + .update_all(first_deployed_to_production_at: finished_at) end def previous_deployment @@ -109,8 +143,18 @@ class Deployment < ActiveRecord::Base @stop_action ||= manual_actions.find_by(name: on_stop) end + def finished_at + read_attribute(:finished_at) || legacy_finished_at + end + + def deployed_at + return unless success? + + finished_at + end + def formatted_deployment_time - created_at.to_time.in_time_zone.to_s(:medium) + deployed_at&.to_time&.in_time_zone&.to_s(:medium) end def has_metrics? @@ -118,21 +162,17 @@ class Deployment < ActiveRecord::Base end def metrics - return {} unless has_metrics? + return {} unless has_metrics? && success? metrics = prometheus_adapter.query(:deployment, self) - metrics&.merge(deployment_time: created_at.to_i) || {} + metrics&.merge(deployment_time: finished_at.to_i) || {} end def additional_metrics - return {} unless has_metrics? + return {} unless has_metrics? && success? metrics = prometheus_adapter.query(:additional_metrics_deployment, self) - metrics&.merge(deployment_time: created_at.to_i) || {} - end - - def status - 'success' + metrics&.merge(deployment_time: finished_at.to_i) || {} end private @@ -144,4 +184,8 @@ class Deployment < ActiveRecord::Base def ref_path File.join(environment.ref_path, 'deployments', iid.to_s) end + + def legacy_finished_at + self.created_at if success? && !read_attribute(:finished_at) + end end diff --git a/app/models/environment.rb b/app/models/environment.rb index 1c31c01eb9f..7d104bb0c25 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -8,9 +8,9 @@ class Environment < ActiveRecord::Base belongs_to :project, required: true - has_many :deployments, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :deployments, -> { success }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent - has_one :last_deployment, -> { order('deployments.id DESC') }, class_name: 'Deployment' + has_one :last_deployment, -> { success.order('deployments.id DESC') }, class_name: 'Deployment' before_validation :nullify_external_url before_validation :generate_slug, if: ->(env) { env.slug.blank? } diff --git a/app/models/environment_status.rb b/app/models/environment_status.rb index a84871f7253..7efc8da09ad 100644 --- a/app/models/environment_status.rb +++ b/app/models/environment_status.rb @@ -8,8 +8,8 @@ class EnvironmentStatus delegate :id, to: :environment delegate :name, to: :environment delegate :project, to: :environment + delegate :status, to: :deployment, allow_nil: true delegate :deployed_at, to: :deployment, allow_nil: true - delegate :status, to: :deployment def self.for_merge_request(mr, user) build_environments_status(mr, user, mr.head_pipeline) @@ -33,10 +33,6 @@ class EnvironmentStatus end end - def deployed_at - deployment&.created_at - end - def changes return [] if project.route_map_for(sha).nil? diff --git a/app/models/issue.rb b/app/models/issue.rb index 0de5e434b02..abdb3448d4e 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -231,20 +231,6 @@ class Issue < ActiveRecord::Base def as_json(options = {}) super(options).tap do |json| - if options.key?(:issue_endpoints) && project - url_helper = Gitlab::Routing.url_helpers - - issue_reference = options[:include_full_project_path] ? to_reference(full: true) : to_reference - - json.merge!( - reference_path: issue_reference, - real_path: url_helper.project_issue_path(project, self), - issue_sidebar_endpoint: url_helper.project_issue_path(project, self, format: :json, serializer: 'sidebar'), - toggle_subscription_endpoint: url_helper.toggle_subscription_project_issue_path(project, self), - assignable_labels_endpoint: url_helper.project_labels_path(project, format: :json, include_ancestor_groups: true) - ) - end - if options.key?(:labels) json[:labels] = labels.as_json( project: project, diff --git a/app/models/label.rb b/app/models/label.rb index 43b49445765..165e4a8f3e5 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -41,8 +41,8 @@ class Label < ActiveRecord::Base scope :templates, -> { where(template: true) } scope :with_title, ->(title) { where(title: title) } scope :with_lists_and_board, -> { joins(lists: :board).merge(List.movable) } - scope :on_group_boards, ->(group_id) { with_lists_and_board.where(boards: { group_id: group_id }) } scope :on_project_boards, ->(project_id) { with_lists_and_board.where(boards: { project_id: project_id }) } + scope :on_board, ->(board_id) { with_lists_and_board.where(boards: { id: board_id }) } scope :order_name_asc, -> { reorder(title: :asc) } scope :order_name_desc, -> { reorder(title: :desc) } scope :subscribed_by, ->(user_id) { joins(:subscriptions).where(subscriptions: { user_id: user_id, subscribed: true }) } diff --git a/app/models/members_preloader.rb b/app/models/members_preloader.rb new file mode 100644 index 00000000000..33855191ca8 --- /dev/null +++ b/app/models/members_preloader.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class MembersPreloader + attr_reader :members + + def initialize(members) + @members = members + end + + def preload_all + ActiveRecord::Associations::Preloader.new.preload(members, :user) + ActiveRecord::Associations::Preloader.new.preload(members, :source) + ActiveRecord::Associations::Preloader.new.preload(members.map(&:user), :status) + ActiveRecord::Associations::Preloader.new.preload(members.map(&:user), :u2f_registrations) + end +end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 74d48d0a9af..4a6627d3ca1 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -232,6 +232,12 @@ class Namespace < ActiveRecord::Base Project.inside_path(full_path) end + # Includes pipelines from this namespace and pipelines from all subgroups + # that belongs to this namespace + def all_pipelines + Ci::Pipeline.where(project: all_projects) + end + def has_parent? parent.present? end diff --git a/app/models/project.rb b/app/models/project.rb index 872bea46e7c..d5a4ae79c47 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -95,8 +95,7 @@ class Project < ActiveRecord::Base unless: :ci_cd_settings, if: proc { ProjectCiCdSetting.available? } - after_create :set_last_activity_at - after_create :set_last_repository_updated_at + after_create :set_timestamps_for_create after_update :update_forks_visibility_level before_destroy :remove_private_deploy_keys @@ -255,7 +254,7 @@ class Project < ActiveRecord::Base has_many :variables, class_name: 'Ci::Variable' has_many :triggers, class_name: 'Ci::Trigger' has_many :environments - has_many :deployments + has_many :deployments, -> { success } has_many :pipeline_schedules, class_name: 'Ci::PipelineSchedule' has_many :project_deploy_tokens has_many :deploy_tokens, through: :project_deploy_tokens @@ -2103,13 +2102,8 @@ class Project < ActiveRecord::Base gitlab_shell.exists?(repository_storage, "#{disk_path}.git") end - # set last_activity_at to the same as created_at - def set_last_activity_at - update_column(:last_activity_at, self.created_at) - end - - def set_last_repository_updated_at - update_column(:last_repository_updated_at, self.created_at) + def set_timestamps_for_create + update_columns(last_activity_at: self.created_at, last_repository_updated_at: self.created_at) end def cross_namespace_reference?(from) diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index 7cff0e30e8d..a399982e5ec 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -12,9 +12,9 @@ class IssueTrackerService < Service # overridden patterns. See ReferenceRegexes::EXTERNAL_PATTERN def self.reference_pattern(only_long: false) if only_long - /(\b[A-Z][A-Z0-9_]+-)(?<issue>\d+)/ + /(\b[A-Z][A-Z0-9_]*-)(?<issue>\d+)/ else - /(\b[A-Z][A-Z0-9_]+-|#{Issue.reference_prefix})(?<issue>\d+)/ + /(\b[A-Z][A-Z0-9_]*-|#{Issue.reference_prefix})(?<issue>\d+)/ end end diff --git a/app/models/repository.rb b/app/models/repository.rb index 37a1dd64052..ee5579329a8 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -912,10 +912,6 @@ class Repository async_remove_remote(remote_name) if tmp_remote_name end - def fetch_remote(remote, forced: false, ssh_auth: nil, no_tags: false, prune: true) - gitlab_shell.fetch_remote(raw_repository, remote, ssh_auth: ssh_auth, forced: forced, no_tags: no_tags, prune: prune) - end - def async_remove_remote(remote_name) return unless remote_name |