summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Speicher <robert@gitlab.com>2018-03-19 22:15:00 +0000
committerRobert Speicher <robert@gitlab.com>2018-03-19 22:15:00 +0000
commit14d18a125e4763bdba2248cfa08913bd26689674 (patch)
tree60b14206a045706dbaafb0566927dc320e66fad1
parent709f4d1226c98810073fd6f8f1e6fc6a579d3da1 (diff)
parent35f4c261d84a35a1223c2fd7d372e0bfcd18194c (diff)
downloadgitlab-ce-14d18a125e4763bdba2248cfa08913bd26689674.tar.gz
Merge branch '10-6-stable-prepare-rc7' into '10-6-stable'
Prepare 10.6-rc7 See merge request gitlab/gitlabhq!2362
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/notes.js10
-rw-r--r--app/assets/javascripts/pages/ci/lints/new/index.js (renamed from app/assets/javascripts/pages/ci/lints/create/index.js)0
-rw-r--r--app/assets/javascripts/pages/profiles/notifications/show/index.js7
-rw-r--r--app/assets/javascripts/pages/projects/show/index.js22
-rw-r--r--app/finders/admin/projects_finder.rb4
-rw-r--r--app/models/application_setting.rb6
-rw-r--r--app/models/ci/build.rb8
-rw-r--r--app/models/ci/pipeline.rb2
-rw-r--r--app/models/concerns/storage/legacy_namespace.rb51
-rw-r--r--app/models/member.rb2
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/repository.rb74
-rw-r--r--app/services/merge_requests/conflicts/list_service.rb10
-rw-r--r--app/services/notification_service.rb4
-rw-r--r--app/services/projects/destroy_service.rb6
-rw-r--r--changelogs/unreleased/44330-docs-for-ingress-ip.yml5
-rw-r--r--changelogs/unreleased/cache-refactor.yml5
-rw-r--r--changelogs/unreleased/fix-dropzone-project-show.yml5
-rw-r--r--changelogs/unreleased/mk-fix-move-upload-files-on-group-transfer.yml5
-rw-r--r--changelogs/unreleased/osw-43951-single-batch-blob-request-to-gitaly.yml5
-rw-r--r--changelogs/unreleased/sh-cache-column-exists.yml5
-rw-r--r--changelogs/unreleased/sh-cache-table-exists.yml5
-rw-r--r--changelogs/unreleased/sh-fix-failure-project-destroy.yml5
-rw-r--r--changelogs/unreleased/sh-optimize-admin-projects-page.yml5
-rw-r--r--doc/ci/examples/code_climate.md22
-rw-r--r--doc/user/project/clusters/index.md29
-rw-r--r--doc/user/project/integrations/prometheus.md30
-rw-r--r--lib/gitlab/conflict/file_collection.rb32
-rw-r--r--lib/gitlab/current_settings.rb2
-rw-r--r--lib/gitlab/database.rb9
-rw-r--r--lib/gitlab/diff/file_collection/merge_request_diff.rb18
-rw-r--r--lib/gitlab/project_transfer.rb16
-rw-r--r--lib/gitlab/repository_cache.rb33
-rw-r--r--lib/gitlab/repository_cache_adapter.rb84
-rw-r--r--lib/repository_cache.rb30
-rw-r--r--spec/controllers/admin/projects_controller_spec.rb10
-rw-r--r--spec/features/ci_lint_spec.rb1
-rw-r--r--spec/features/profiles/user_visits_notifications_tab_spec.rb2
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb12
-rw-r--r--spec/features/projects/show_project_spec.rb22
-rw-r--r--spec/lib/gitlab/conflict/file_collection_spec.rb32
-rw-r--r--spec/lib/gitlab/database_spec.rb23
-rw-r--r--spec/lib/gitlab/project_transfer_spec.rb59
-rw-r--r--spec/lib/gitlab/repository_cache_adapter_spec.rb76
-rw-r--r--spec/lib/gitlab/repository_cache_spec.rb50
-rw-r--r--spec/lib/repository_cache_spec.rb34
-rw-r--r--spec/migrations/migrate_old_artifacts_spec.rb2
-rw-r--r--spec/models/ci/build_spec.rb36
-rw-r--r--spec/models/namespace_spec.rb57
-rw-r--r--spec/models/repository_spec.rb69
-rw-r--r--vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml11
52 files changed, 730 insertions, 326 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 5aee1345c52..8f63f4f9a10 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.89.0
+0.91.0
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index c640003d958..750ed27873f 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -82,6 +82,9 @@ export default class Notes {
this.basePollingInterval = 15000;
this.maxPollingSteps = 4;
+ this.$wrapperEl = hasVueMRDiscussionsCookie()
+ ? $(document).find('.diffs')
+ : $(document);
this.cleanBinding();
this.addBinding();
this.setPollingInterval();
@@ -106,8 +109,6 @@ export default class Notes {
}
addBinding() {
- this.$wrapperEl = hasVueMRDiscussionsCookie() ? $(document).find('.diffs') : $(document);
-
// Edit note link
this.$wrapperEl.on('click', '.js-note-edit', this.showEditForm.bind(this));
this.$wrapperEl.on('click', '.note-edit-cancel', this.cancelEdit);
@@ -150,14 +151,9 @@ export default class Notes {
$(window).on('hashchange', this.onHashChange);
this.boundGetContent = this.getContent.bind(this);
document.addEventListener('refreshLegacyNotes', this.boundGetContent);
- this.eventsBound = true;
}
cleanBinding() {
- if (!this.eventsBound) {
- return;
- }
-
this.$wrapperEl.off('click', '.js-note-edit');
this.$wrapperEl.off('click', '.note-edit-cancel');
this.$wrapperEl.off('click', '.js-note-delete');
diff --git a/app/assets/javascripts/pages/ci/lints/create/index.js b/app/assets/javascripts/pages/ci/lints/new/index.js
index 8e8a843da0b..8e8a843da0b 100644
--- a/app/assets/javascripts/pages/ci/lints/create/index.js
+++ b/app/assets/javascripts/pages/ci/lints/new/index.js
diff --git a/app/assets/javascripts/pages/profiles/notifications/show/index.js b/app/assets/javascripts/pages/profiles/notifications/show/index.js
new file mode 100644
index 00000000000..2e24a10fa5c
--- /dev/null
+++ b/app/assets/javascripts/pages/profiles/notifications/show/index.js
@@ -0,0 +1,7 @@
+import NotificationsForm from '../../../../notifications_form';
+import notificationsDropdown from '../../../../notifications_dropdown';
+
+document.addEventListener('DOMContentLoaded', () => {
+ new NotificationsForm(); // eslint-disable-line no-new
+ notificationsDropdown();
+});
diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js
index 9b87f249f09..3b0f0f960b8 100644
--- a/app/assets/javascripts/pages/projects/show/index.js
+++ b/app/assets/javascripts/pages/projects/show/index.js
@@ -1,3 +1,5 @@
+import $ from 'jquery';
+import initBlob from '~/blob_edit/blob_bundle';
import ShortcutsNavigation from '~/shortcuts_navigation';
import NotificationsForm from '~/notifications_form';
import UserCallout from '~/user_callout';
@@ -18,10 +20,22 @@ document.addEventListener('DOMContentLoaded', () => {
className: 'js-autodevops-banner',
});
- if ($('#tree-slider').length) new TreeView(); // eslint-disable-line no-new
- if ($('.blob-viewer').length) new BlobViewer(); // eslint-disable-line no-new
- if ($('.project-show-activity').length) new Activities(); // eslint-disable-line no-new
- $('#tree-slider').waitForImages(() => {
+ // Project show page loads different overview content based on user preferences
+ const treeSlider = document.querySelector('#tree-slider');
+ if (treeSlider) {
+ new TreeView(); // eslint-disable-line no-new
+ initBlob();
+ }
+
+ if (document.querySelector('.blob-viewer')) {
+ new BlobViewer(); // eslint-disable-line no-new
+ }
+
+ if (document.querySelector('.project-show-activity')) {
+ new Activities(); // eslint-disable-line no-new
+ }
+
+ $(treeSlider).waitForImages(() => {
ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
});
});
diff --git a/app/finders/admin/projects_finder.rb b/app/finders/admin/projects_finder.rb
index d6bcd939522..2c8f21c2400 100644
--- a/app/finders/admin/projects_finder.rb
+++ b/app/finders/admin/projects_finder.rb
@@ -16,8 +16,8 @@ class Admin::ProjectsFinder
items = by_archived(items)
items = by_personal(items)
items = by_name(items)
- items = sort(items)
- items.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page])
+ items = items.includes(namespace: [:owner])
+ sort(items).page(params[:page])
end
private
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 4281d7d267f..862933bf127 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -348,15 +348,15 @@ class ApplicationSetting < ActiveRecord::Base
end
def home_page_url_column_exists?
- ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url)
+ ::Gitlab::Database.cached_column_exists?(:application_settings, :home_page_url)
end
def help_page_support_url_column_exists?
- ActiveRecord::Base.connection.column_exists?(:application_settings, :help_page_support_url)
+ ::Gitlab::Database.cached_column_exists?(:application_settings, :help_page_support_url)
end
def sidekiq_throttling_column_exists?
- ActiveRecord::Base.connection.column_exists?(:application_settings, :sidekiq_throttling_enabled)
+ ::Gitlab::Database.cached_column_exists?(:application_settings, :sidekiq_throttling_enabled)
end
def domain_whitelist_raw
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index b230b7f47ef..5a381bdd6a2 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -41,12 +41,12 @@ module Ci
scope :unstarted, ->() { where(runner_id: nil) }
scope :ignore_failures, ->() { where(allow_failure: false) }
- scope :with_artifacts, ->() do
+ scope :with_artifacts_archive, ->() do
where('(artifacts_file IS NOT NULL AND artifacts_file <> ?) OR EXISTS (?)',
- '', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id'))
+ '', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').archive)
end
- scope :with_artifacts_not_expired, ->() { with_artifacts.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) }
- scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) }
+ scope :with_artifacts_not_expired, ->() { with_artifacts_archive.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) }
+ scope :with_expired_artifacts, ->() { with_artifacts_archive.where('artifacts_expire_at < ?', Time.now) }
scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
scope :manual_actions, ->() { where(when: :manual, status: COMPLETED_STATUSES + [:manual]) }
scope :ref_protected, -> { where(protected: true) }
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index a72a815bfe8..8492d293343 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -514,7 +514,7 @@ module Ci
# We purposely cast the builds to an Array here. Because we always use the
# rows if there are more than 0 this prevents us from having to run two
# queries: one to get the count and one to get the rows.
- @latest_builds_with_artifacts ||= builds.latest.with_artifacts.to_a
+ @latest_builds_with_artifacts ||= builds.latest.with_artifacts_archive.to_a
end
private
diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb
index 67a988addbe..f05e606995d 100644
--- a/app/models/concerns/storage/legacy_namespace.rb
+++ b/app/models/concerns/storage/legacy_namespace.rb
@@ -7,29 +7,24 @@ module Storage
raise Gitlab::UpdatePathError.new('Namespace cannot be moved, because at least one project has tags in container registry')
end
- expires_full_path_cache
-
- # Move the namespace directory in all storage paths used by member projects
- repository_storage_paths.each do |repository_storage_path|
- # Ensure old directory exists before moving it
- gitlab_shell.add_namespace(repository_storage_path, full_path_was)
-
- # Ensure new directory exists before moving it (if there's a parent)
- gitlab_shell.add_namespace(repository_storage_path, parent.full_path) if parent
+ parent_was = if parent_changed? && parent_id_was.present?
+ Namespace.find(parent_id_was) # raise NotFound early if needed
+ end
- unless gitlab_shell.mv_namespace(repository_storage_path, full_path_was, full_path)
+ expires_full_path_cache
- Rails.logger.error "Exception moving path #{repository_storage_path} from #{full_path_was} to #{full_path}"
+ move_repositories
- # if we cannot move namespace directory we should rollback
- # db changes in order to prevent out of sync between db and fs
- raise Gitlab::UpdatePathError.new('namespace directory cannot be moved')
- end
+ if parent_changed?
+ former_parent_full_path = parent_was&.full_path
+ parent_full_path = parent&.full_path
+ Gitlab::UploadsTransfer.new.move_namespace(path, former_parent_full_path, parent_full_path)
+ Gitlab::PagesTransfer.new.move_namespace(path, former_parent_full_path, parent_full_path)
+ else
+ Gitlab::UploadsTransfer.new.rename_namespace(full_path_was, full_path)
+ Gitlab::PagesTransfer.new.rename_namespace(full_path_was, full_path)
end
- Gitlab::UploadsTransfer.new.rename_namespace(full_path_was, full_path)
- Gitlab::PagesTransfer.new.rename_namespace(full_path_was, full_path)
-
remove_exports!
# If repositories moved successfully we need to
@@ -57,6 +52,26 @@ module Storage
private
+ def move_repositories
+ # Move the namespace directory in all storage paths used by member projects
+ repository_storage_paths.each do |repository_storage_path|
+ # Ensure old directory exists before moving it
+ gitlab_shell.add_namespace(repository_storage_path, full_path_was)
+
+ # Ensure new directory exists before moving it (if there's a parent)
+ gitlab_shell.add_namespace(repository_storage_path, parent.full_path) if parent
+
+ unless gitlab_shell.mv_namespace(repository_storage_path, full_path_was, full_path)
+
+ Rails.logger.error "Exception moving path #{repository_storage_path} from #{full_path_was} to #{full_path}"
+
+ # if we cannot move namespace directory we should rollback
+ # db changes in order to prevent out of sync between db and fs
+ raise Gitlab::UpdatePathError.new('namespace directory cannot be moved')
+ end
+ end
+ end
+
def old_repository_storage_paths
@old_repository_storage_paths ||= repository_storage_paths
end
diff --git a/app/models/member.rb b/app/models/member.rb
index 36090676051..ec8156bbb01 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -55,7 +55,7 @@ class Member < ActiveRecord::Base
scope :active_without_invites, -> do
left_join_users
.where(users: { state: 'active' })
- .where(requested_at: nil)
+ .non_request
.reorder(nil)
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 43e006ad3ee..15c63dd743e 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -545,7 +545,7 @@ class Project < ActiveRecord::Base
latest_pipeline = pipelines.latest_successful_for(ref)
if latest_pipeline
- latest_pipeline.builds.latest.with_artifacts
+ latest_pipeline.builds.latest.with_artifacts_archive
else
builds.none
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 386fd0b1c9a..42f1ac43e29 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -16,6 +16,7 @@ class Repository
].freeze
include Gitlab::ShellAdapter
+ include Gitlab::RepositoryCacheAdapter
attr_accessor :full_path, :disk_path, :project, :is_wiki
@@ -57,22 +58,6 @@ class Repository
merge_request_template: :merge_request_template_names
}.freeze
- # Wraps around the given method and caches its output in Redis and an instance
- # variable.
- #
- # This only works for methods that do not take any arguments.
- def self.cache_method(name, fallback: nil, memoize_only: false)
- original = :"_uncached_#{name}"
-
- alias_method(original, name)
-
- define_method(name) do
- cache_method_output(name, fallback: fallback, memoize_only: memoize_only) do
- __send__(original) # rubocop:disable GitlabSecurity/PublicSend
- end
- end
- end
-
def initialize(full_path, project, disk_path: nil, is_wiki: false)
@full_path = full_path
@disk_path = disk_path || full_path
@@ -302,17 +287,6 @@ class Repository
expire_method_caches(CACHED_METHODS)
end
- # Expires the caches of a specific set of methods
- def expire_method_caches(methods)
- methods.each do |key|
- cache.expire(key)
-
- ivar = cache_instance_variable_name(key)
-
- remove_instance_variable(ivar) if instance_variable_defined?(ivar)
- end
- end
-
def expire_avatar_cache
expire_method_caches(%i(avatar))
end
@@ -924,49 +898,6 @@ class Repository
end
end
- # Caches the supplied block both in a cache and in an instance variable.
- #
- # The cache key and instance variable are named the same way as the value of
- # the `key` argument.
- #
- # This method will return `nil` if the corresponding instance variable is also
- # set to `nil`. This ensures we don't keep yielding the block when it returns
- # `nil`.
- #
- # key - The name of the key to cache the data in.
- # fallback - A value to fall back to in the event of a Git error.
- def cache_method_output(key, fallback: nil, memoize_only: false, &block)
- ivar = cache_instance_variable_name(key)
-
- if instance_variable_defined?(ivar)
- instance_variable_get(ivar)
- else
- # If the repository doesn't exist and a fallback was specified we return
- # that value inmediately. This saves us Rugged/gRPC invocations.
- return fallback unless fallback.nil? || exists?
-
- begin
- value =
- if memoize_only
- yield
- else
- cache.fetch(key, &block)
- end
-
- instance_variable_set(ivar, value)
- rescue Gitlab::Git::Repository::NoRepository
- # Even if the above `#exists?` check passes these errors might still
- # occur (for example because of a non-existing HEAD). We want to
- # gracefully handle this and not cache anything
- fallback
- end
- end
- end
-
- def cache_instance_variable_name(key)
- :"@#{key.to_s.tr('?!', '')}"
- end
-
def file_on_head(type)
if head = tree(:head)
head.blobs.find do |blob|
@@ -1021,8 +952,7 @@ class Repository
end
def cache
- # TODO: should we use UUIDs here? We could move repositories without clearing this cache
- @cache ||= RepositoryCache.new(full_path, @project.id)
+ @cache ||= Gitlab::RepositoryCache.new(self)
end
def tags_sorted_by_committed_date
diff --git a/app/services/merge_requests/conflicts/list_service.rb b/app/services/merge_requests/conflicts/list_service.rb
index ca9a33678e4..72cbc49adb2 100644
--- a/app/services/merge_requests/conflicts/list_service.rb
+++ b/app/services/merge_requests/conflicts/list_service.rb
@@ -17,15 +17,7 @@ module MergeRequests
return @conflicts_can_be_resolved_in_ui = false unless merge_request.has_complete_diff_refs?
return @conflicts_can_be_resolved_in_ui = false if merge_request.branch_missing?
- begin
- # Try to parse each conflict. If the MR's mergeable status hasn't been
- # updated, ensure that we don't say there are conflicts to resolve
- # when there are no conflict files.
- conflicts.files.each(&:lines)
- @conflicts_can_be_resolved_in_ui = conflicts.files.length > 0
- rescue Gitlab::Git::CommandError, Gitlab::Git::Conflict::Parser::UnresolvableError, Gitlab::Git::Conflict::Resolver::ConflictSideMissing
- @conflicts_can_be_resolved_in_ui = false
- end
+ @conflicts_can_be_resolved_in_ui = conflicts.can_be_resolved_in_ui?
end
def conflicts
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index e07ecda27b5..ab94db2c1e5 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -208,9 +208,9 @@ class NotificationService
def new_access_request(member)
return true unless member.notifiable?(:subscription)
- recipients = member.source.members.owners_and_masters
+ recipients = member.source.members.active_without_invites.owners_and_masters
if fallback_to_group_owners_masters?(recipients, member)
- recipients = member.source.group.members.owners_and_masters
+ recipients = member.source.group.members.active_without_invites.owners_and_masters
end
recipients.each { |recipient| deliver_access_request_email(recipient, member) }
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index 81972df9b3c..4b8f955ae69 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -88,7 +88,11 @@ module Projects
def attempt_rollback(project, message)
return unless project
- project.update_attributes(delete_error: message, pending_delete: false)
+ # It's possible that the project was destroyed, but some after_commit
+ # hook failed and caused us to end up here. A destroyed model will be a frozen hash,
+ # which cannot be altered.
+ project.update_attributes(delete_error: message, pending_delete: false) unless project.destroyed?
+
log_error("Deletion failed on #{project.full_path} with the following message: #{message}")
end
diff --git a/changelogs/unreleased/44330-docs-for-ingress-ip.yml b/changelogs/unreleased/44330-docs-for-ingress-ip.yml
new file mode 100644
index 00000000000..3dfaea6e17e
--- /dev/null
+++ b/changelogs/unreleased/44330-docs-for-ingress-ip.yml
@@ -0,0 +1,5 @@
+---
+title: Add documentation for displayed K8s Ingress IP address (#44330)
+merge_request: 17836
+author:
+type: other
diff --git a/changelogs/unreleased/cache-refactor.yml b/changelogs/unreleased/cache-refactor.yml
new file mode 100644
index 00000000000..dec7a0392a5
--- /dev/null
+++ b/changelogs/unreleased/cache-refactor.yml
@@ -0,0 +1,5 @@
+---
+title: Cache MergeRequests can_be_resolved_in_ui? git operations
+merge_request: 17589
+author:
+type: performance
diff --git a/changelogs/unreleased/fix-dropzone-project-show.yml b/changelogs/unreleased/fix-dropzone-project-show.yml
new file mode 100644
index 00000000000..660780812d8
--- /dev/null
+++ b/changelogs/unreleased/fix-dropzone-project-show.yml
@@ -0,0 +1,5 @@
+---
+title: Fix file upload on project show page
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/mk-fix-move-upload-files-on-group-transfer.yml b/changelogs/unreleased/mk-fix-move-upload-files-on-group-transfer.yml
new file mode 100644
index 00000000000..ba366b81600
--- /dev/null
+++ b/changelogs/unreleased/mk-fix-move-upload-files-on-group-transfer.yml
@@ -0,0 +1,5 @@
+---
+title: Fix missing uploads after group transfer
+merge_request: 17658
+author:
+type: fixed
diff --git a/changelogs/unreleased/osw-43951-single-batch-blob-request-to-gitaly.yml b/changelogs/unreleased/osw-43951-single-batch-blob-request-to-gitaly.yml
new file mode 100644
index 00000000000..34f834298b6
--- /dev/null
+++ b/changelogs/unreleased/osw-43951-single-batch-blob-request-to-gitaly.yml
@@ -0,0 +1,5 @@
+---
+title: Submit a single batch blob RPC to Gitaly per HTTP request when viewing diffs
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-cache-column-exists.yml b/changelogs/unreleased/sh-cache-column-exists.yml
new file mode 100644
index 00000000000..8bc648f2b32
--- /dev/null
+++ b/changelogs/unreleased/sh-cache-column-exists.yml
@@ -0,0 +1,5 @@
+---
+title: Cache column_exists? for application settings
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-cache-table-exists.yml b/changelogs/unreleased/sh-cache-table-exists.yml
new file mode 100644
index 00000000000..37407b2a005
--- /dev/null
+++ b/changelogs/unreleased/sh-cache-table-exists.yml
@@ -0,0 +1,5 @@
+---
+title: Cache table_exists?('application_settings') to reduce repeated schema reloads
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-fix-failure-project-destroy.yml b/changelogs/unreleased/sh-fix-failure-project-destroy.yml
new file mode 100644
index 00000000000..d5f5cd3f954
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-failure-project-destroy.yml
@@ -0,0 +1,5 @@
+---
+title: Fix "Can't modify frozen hash" error when project is destroyed
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-optimize-admin-projects-page.yml b/changelogs/unreleased/sh-optimize-admin-projects-page.yml
new file mode 100644
index 00000000000..242ea758dab
--- /dev/null
+++ b/changelogs/unreleased/sh-optimize-admin-projects-page.yml
@@ -0,0 +1,5 @@
+---
+title: Fix timeouts loading /admin/projects page
+merge_request:
+author:
+type: performance
diff --git a/doc/ci/examples/code_climate.md b/doc/ci/examples/code_climate.md
index d7df53494ed..ec5e5afb8c6 100644
--- a/doc/ci/examples/code_climate.md
+++ b/doc/ci/examples/code_climate.md
@@ -16,18 +16,26 @@ codequality:
- docker:dind
script:
- docker pull codeclimate/codeclimate
- - docker run --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock --volume /tmp/cc:/tmp/cc codeclimate/codeclimate:0.69.0 init
- - docker run --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock --volume /tmp/cc:/tmp/cc codeclimate/codeclimate:0.69.0 analyze -f json > codeclimate.json || true
+ - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
+ - docker run
+ --env SOURCE_CODE="$PWD" \
+ --volume "$PWD":/code \
+ --volume /var/run/docker.sock:/var/run/docker.sock \
+ "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
artifacts:
paths: [codeclimate.json]
```
-This will create a `codequality` job in your CI pipeline and will allow you to
-download and analyze the report artifact in JSON format.
+The above example will create a `codequality` job in your CI/CD pipeline which
+will scan your source code for code quality issues. The report will be saved
+as an artifact that you can later download and analyze.
-For [GitLab Starter][ee] users, this information can be automatically
-extracted and shown right in the merge request widget. [Learn more on code quality
-diffs in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html).
+TIP: **Tip:**
+Starting with [GitLab Starter][ee] 9.3, this information will
+be automatically extracted and shown right in the merge request widget. To do
+so, the CI/CD job must be named `codequality` and the artifact path must be
+`codeclimate.json`.
+[Learn more on code quality diffs in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html).
[cli]: https://github.com/codeclimate/codeclimate
[dind]: ../docker/using_docker_build.md#use-docker-in-docker-executor
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index 661697aaeb7..bd9bcfadb99 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -167,6 +167,17 @@ external IP address with the following procedure. It can be deployed using the
In order to publish your web application, you first need to find the external IP
address associated to your load balancer.
+### Let GitLab fetch the IP address
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17052) in GitLab 10.6.
+
+If you installed the Ingress [via the **Applications**](#installing-applications),
+you should see the Ingress IP address on this same page within a few minutes.
+If you don't see this, GitLab might not be able to determine the IP address of
+your ingress application in which case you should manually determine it.
+
+### Manually determining the IP address
+
If the cluster is on GKE, click on the **Google Kubernetes Engine** link in the
**Advanced settings**, or go directly to the
[Google Kubernetes Engine dashboard](https://console.cloud.google.com/kubernetes/)
@@ -193,6 +204,24 @@ The output is the external IP address of your cluster. This information can then
be used to set up DNS entries and forwarding rules that allow external access to
your deployed applications.
+### Using a static IP
+
+By default, an ephemeral external IP address is associated to the cluster's load
+balancer. If you associate the ephemeral IP with your DNS and the IP changes,
+your apps will not be able to be reached, and you'd have to change the DNS
+record again. In order to avoid that, you should change it into a static
+reserved IP.
+
+[Read how to promote an ephemeral external IP address in GKE.](https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address#promote_ephemeral_ip)
+
+### Pointing your DNS at the cluster IP
+
+Once you've set up the static IP, you should associate it to a [wildcard DNS
+record](https://en.wikipedia.org/wiki/Wildcard_DNS_record), in order to be able
+to reach your apps. This heavily depends on your domain provider, but in case
+you aren't sure, just create an A record with a wildcard host like
+`*.example.com.`.
+
## Setting the environment scope
NOTE: **Note:**
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index 249463fb86e..fa7e504c4aa 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -2,7 +2,7 @@
> [Introduced][ce-8935] in GitLab 9.0.
-GitLab offers powerful integration with [Prometheus] for monitoring key metrics your apps, directly within GitLab.
+GitLab offers powerful integration with [Prometheus] for monitoring key metrics of your apps, directly within GitLab.
Metrics for each environment are retrieved from Prometheus, and then displayed
within the GitLab interface.
@@ -12,17 +12,21 @@ There are two ways to setup Prometheus integration, depending on where your apps
* For deployments on Kubernetes, GitLab can automatically [deploy and manage Prometheus](#managed-prometheus-on-kubernetes)
* For other deployment targets, simply [specify the Prometheus server](#manual-configuration-of-prometheus).
-## Managed Prometheus on Kubernetes
+Once enabled, GitLab will automatically detect metrics from known services in the [metric library](#monitoring-ci-cd-environments).
+
+## Enabling Prometheus Integration
+
+### Managed Prometheus on Kubernetes
> **Note**: [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/28916) in GitLab 10.5
GitLab can seamlessly deploy and manage Prometheus on a [connected Kubernetes cluster](../clusters/index.md), making monitoring of your apps easy.
-### Requirements
+#### Requirements
* A [connected Kubernetes cluster](../clusters/index.md)
* Helm Tiller [installed by GitLab](../clusters/index.md#installing-applications)
-### Getting started
+#### Getting started
Once you have a connected Kubernetes cluster with Helm installed, deploying a managed Prometheus is as easy as a single click.
@@ -32,7 +36,7 @@ Once you have a connected Kubernetes cluster with Helm installed, deploying a ma
![Managed Prometheus Deploy](img/prometheus_deploy.png)
-### About managed Prometheus deployments
+#### About managed Prometheus deployments
Prometheus is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/kubernetes/charts/tree/master/stable/prometheus). Prometheus is only accessible within the cluster, with GitLab communicating through the [Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/).
@@ -45,9 +49,9 @@ CPU and Memory consumption is monitored, but requires [naming conventions](prome
The [NGINX Ingress](../clusters/index.md#installing-applications) that is deployed by GitLab to clusters, is automatically annotated for monitoring providing key response metrics: latency, throughput, and error rates.
-## Manual configuration of Prometheus
+### Manual configuration of Prometheus
-### Requirements
+#### Requirements
Integration with Prometheus requires the following:
@@ -56,7 +60,7 @@ Integration with Prometheus requires the following:
1. Each metric must be have a label to indicate the environment
1. GitLab must have network connectivity to the Prometheus server
-### Getting started
+#### Getting started
Installing and configuring Prometheus to monitor applications is fairly straight forward.
@@ -64,7 +68,7 @@ Installing and configuring Prometheus to monitor applications is fairly straight
1. Set up one of the [supported monitoring targets](prometheus_library/metrics.md)
1. Configure the Prometheus server to [collect their metrics](https://prometheus.io/docs/operating/configuration/#scrape_config)
-### Configuration in GitLab
+#### Configuration in GitLab
The actual configuration of Prometheus integration within GitLab is very simple.
All you will need is the DNS or IP address of the Prometheus server you'd like
@@ -83,9 +87,9 @@ to integrate with.
Once configured, GitLab will attempt to retrieve performance metrics for any
environment which has had a successful deployment.
-GitLab will automatically scan the Prometheus server for known metrics and attempt to identify the metrics for a particular environment. The supported metrics and scan process is detailed in our [Prometheus Metric Library documentation](prometheus_library/metrics.html).
+GitLab will automatically scan the Prometheus server for metrics from known serves like Kubernetes and NGINX, and attempt to identify individual environment. The supported metrics and scan process is detailed in our [Prometheus Metric Library documentation](prometheus_library/metrics.html).
-[Learn more about monitoring environments.](../../../ci/environments.md#monitoring-environments)
+You can view the performance dashboard for an environment by [clicking on the monitoring button](../../../ci/environments.md#monitoring-environments).
## Determining the performance impact of a merge
@@ -93,7 +97,7 @@ GitLab will automatically scan the Prometheus server for known metrics and attem
> GitLab 9.3 added the [numeric comparison](https://gitlab.com/gitlab-org/gitlab-ce/issues/27439) of the 30 minute averages.
> Requires [Kubernetes](prometheus_library/kubernetes.md) metrics
-Developers can view theperformance impact of their changes within the merge
+Developers can view the performance impact of their changes within the merge
request workflow. When a source branch has been deployed to an environment, a sparkline and numeric comparison of the average memory consumption will appear. On the sparkline, a dot
indicates when the current changes were deployed, with up to 30 minutes of
performance data displayed before and after. The comparison shows the difference between the 30 minute average before and after the deployment. This information is updated after
@@ -109,7 +113,7 @@ Prometheus server.
## Troubleshooting
-If the "Attempting to load performance data" screen continues to appear, it could be due to:
+If the "No data found" screen continues to appear, it could be due to:
- No successful deployments have occurred to this environment.
- Prometheus does not have performance data for this environment, or the metrics
diff --git a/lib/gitlab/conflict/file_collection.rb b/lib/gitlab/conflict/file_collection.rb
index 0a3ae2c3760..3ccfd9a739d 100644
--- a/lib/gitlab/conflict/file_collection.rb
+++ b/lib/gitlab/conflict/file_collection.rb
@@ -1,14 +1,18 @@
module Gitlab
module Conflict
class FileCollection
+ include Gitlab::RepositoryCacheAdapter
+
attr_reader :merge_request, :resolver
def initialize(merge_request)
our_commit = merge_request.source_branch_head.raw
their_commit = merge_request.target_branch_head.raw
- target_repo = merge_request.target_project.repository.raw
+ @target_repo = merge_request.target_project.repository
@source_repo = merge_request.source_project.repository.raw
- @resolver = Gitlab::Git::Conflict::Resolver.new(target_repo, our_commit.id, their_commit.id)
+ @our_commit_id = our_commit.id
+ @their_commit_id = their_commit.id
+ @resolver = Gitlab::Git::Conflict::Resolver.new(@target_repo.raw, @our_commit_id, @their_commit_id)
@merge_request = merge_request
end
@@ -30,6 +34,17 @@ module Gitlab
end
end
+ def can_be_resolved_in_ui?
+ # Try to parse each conflict. If the MR's mergeable status hasn't been
+ # updated, ensure that we don't say there are conflicts to resolve
+ # when there are no conflict files.
+ files.each(&:lines)
+ files.any?
+ rescue Gitlab::Git::CommandError, Gitlab::Git::Conflict::Parser::UnresolvableError, Gitlab::Git::Conflict::Resolver::ConflictSideMissing
+ false
+ end
+ cache_method :can_be_resolved_in_ui?
+
def file_for_path(old_path, new_path)
files.find { |file| file.their_path == old_path && file.our_path == new_path }
end
@@ -56,6 +71,19 @@ Merge branch '#{merge_request.target_branch}' into '#{merge_request.source_branc
#{conflict_filenames.join("\n")}
EOM
end
+
+ private
+
+ def cache
+ @cache ||= begin
+ # Use the commit ids as a namespace so if the MR branches get
+ # updated we instantiate the cache under a different namespace. That
+ # way don't have to worry about explicitly invalidating the cache
+ namespace = "#{@our_commit_id}:#{@their_commit_id}"
+
+ Gitlab::RepositoryCache.new(@target_repo, extra_namespace: namespace)
+ end
+ end
end
end
end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index b7c596a973d..e392a015b91 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -70,7 +70,7 @@ module Gitlab
active_db_connection = ActiveRecord::Base.connection.active? rescue false
active_db_connection &&
- ActiveRecord::Base.connection.table_exists?('application_settings')
+ Gitlab::Database.cached_table_exists?('application_settings')
rescue ActiveRecord::NoDatabaseError
false
end
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index e51794fef99..76501dd50e8 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -183,6 +183,15 @@ module Gitlab
ActiveRecord::Base.connection
end
+ def self.cached_column_exists?(table_name, column_name)
+ connection.schema_cache.columns_hash(table_name).has_key?(column_name.to_s)
+ end
+
+ def self.cached_table_exists?(table_name)
+ # Rails 5 uses data_source_exists? instead of table_exists?
+ connection.schema_cache.table_exists?(table_name)
+ end
+
private_class_method :connection
def self.database_version
diff --git a/lib/gitlab/diff/file_collection/merge_request_diff.rb b/lib/gitlab/diff/file_collection/merge_request_diff.rb
index fcda1fe2233..ff68bc7303a 100644
--- a/lib/gitlab/diff/file_collection/merge_request_diff.rb
+++ b/lib/gitlab/diff/file_collection/merge_request_diff.rb
@@ -13,7 +13,16 @@ module Gitlab
end
def diff_files
- super.tap { |_| store_highlight_cache }
+ # Make sure to _not_ send any method call to Gitlab::Diff::File
+ # _before_ all of them were collected (`super`). Premature method calls will
+ # trigger N+1 RPCs to Gitaly through BatchLoader records (Blob.lazy).
+ #
+ diff_files = super
+
+ diff_files.each { |diff_file| cache_highlight!(diff_file) if cacheable?(diff_file) }
+ store_highlight_cache
+
+ diff_files
end
def real_size
@@ -22,13 +31,6 @@ module Gitlab
private
- # Extracted method to highlight in the same iteration to the diff_collection.
- def decorate_diff!(diff)
- diff_file = super
- cache_highlight!(diff_file) if cacheable?(diff_file)
- diff_file
- end
-
def highlight_diff_file_from_cache!(diff_file, cache_diff_lines)
diff_file.highlighted_diff_lines = cache_diff_lines.map do |line|
Gitlab::Diff::Line.init_from_hash(line)
diff --git a/lib/gitlab/project_transfer.rb b/lib/gitlab/project_transfer.rb
index 1bba0b78e2f..690c38737c0 100644
--- a/lib/gitlab/project_transfer.rb
+++ b/lib/gitlab/project_transfer.rb
@@ -1,13 +1,19 @@
module Gitlab
+ # This class is used to move local, unhashed files owned by projects to their new location
class ProjectTransfer
- def move_project(project_path, namespace_path_was, namespace_path)
- new_namespace_folder = File.join(root_dir, namespace_path)
- FileUtils.mkdir_p(new_namespace_folder) unless Dir.exist?(new_namespace_folder)
- from = File.join(root_dir, namespace_path_was, project_path)
- to = File.join(root_dir, namespace_path, project_path)
+ # nil parent_path (or parent_path_was) represents a root namespace
+ def move_namespace(path, parent_path_was, parent_path)
+ parent_path_was ||= ''
+ parent_path ||= ''
+ new_parent_folder = File.join(root_dir, parent_path)
+ FileUtils.mkdir_p(new_parent_folder)
+ from = File.join(root_dir, parent_path_was, path)
+ to = File.join(root_dir, parent_path, path)
move(from, to, "")
end
+ alias_method :move_project, :move_namespace
+
def rename_project(path_was, path, namespace_path)
base_dir = File.join(root_dir, namespace_path)
move(path_was, path, base_dir)
diff --git a/lib/gitlab/repository_cache.rb b/lib/gitlab/repository_cache.rb
new file mode 100644
index 00000000000..b1bf3ca4143
--- /dev/null
+++ b/lib/gitlab/repository_cache.rb
@@ -0,0 +1,33 @@
+# Interface to the Redis-backed cache store
+module Gitlab
+ class RepositoryCache
+ attr_reader :repository, :namespace, :backend
+
+ def initialize(repository, extra_namespace: nil, backend: Rails.cache)
+ @repository = repository
+ @namespace = "#{repository.full_path}:#{repository.project.id}"
+ @namespace += ":#{extra_namespace}" if extra_namespace
+ @backend = backend
+ end
+
+ def cache_key(type)
+ "#{type}:#{namespace}"
+ end
+
+ def expire(key)
+ backend.delete(cache_key(key))
+ end
+
+ def fetch(key, &block)
+ backend.fetch(cache_key(key), &block)
+ end
+
+ def exist?(key)
+ backend.exist?(cache_key(key))
+ end
+
+ def read(key)
+ backend.read(cache_key(key))
+ end
+ end
+end
diff --git a/lib/gitlab/repository_cache_adapter.rb b/lib/gitlab/repository_cache_adapter.rb
new file mode 100644
index 00000000000..7f64a8c9e46
--- /dev/null
+++ b/lib/gitlab/repository_cache_adapter.rb
@@ -0,0 +1,84 @@
+module Gitlab
+ module RepositoryCacheAdapter
+ extend ActiveSupport::Concern
+
+ class_methods do
+ # Wraps around the given method and caches its output in Redis and an instance
+ # variable.
+ #
+ # This only works for methods that do not take any arguments.
+ def cache_method(name, fallback: nil, memoize_only: false)
+ original = :"_uncached_#{name}"
+
+ alias_method(original, name)
+
+ define_method(name) do
+ cache_method_output(name, fallback: fallback, memoize_only: memoize_only) do
+ __send__(original) # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
+ end
+ end
+
+ # RepositoryCache to be used. Should be overridden by the including class
+ def cache
+ raise NotImplementedError
+ end
+
+ # Caches the supplied block both in a cache and in an instance variable.
+ #
+ # The cache key and instance variable are named the same way as the value of
+ # the `key` argument.
+ #
+ # This method will return `nil` if the corresponding instance variable is also
+ # set to `nil`. This ensures we don't keep yielding the block when it returns
+ # `nil`.
+ #
+ # key - The name of the key to cache the data in.
+ # fallback - A value to fall back to in the event of a Git error.
+ def cache_method_output(key, fallback: nil, memoize_only: false, &block)
+ ivar = cache_instance_variable_name(key)
+
+ if instance_variable_defined?(ivar)
+ instance_variable_get(ivar)
+ else
+ # If the repository doesn't exist and a fallback was specified we return
+ # that value inmediately. This saves us Rugged/gRPC invocations.
+ return fallback unless fallback.nil? || cache.repository.exists?
+
+ begin
+ value =
+ if memoize_only
+ yield
+ else
+ cache.fetch(key, &block)
+ end
+
+ instance_variable_set(ivar, value)
+ rescue Gitlab::Git::Repository::NoRepository
+ # Even if the above `#exists?` check passes these errors might still
+ # occur (for example because of a non-existing HEAD). We want to
+ # gracefully handle this and not cache anything
+ fallback
+ end
+ end
+ end
+
+ # Expires the caches of a specific set of methods
+ def expire_method_caches(methods)
+ methods.each do |key|
+ cache.expire(key)
+
+ ivar = cache_instance_variable_name(key)
+
+ remove_instance_variable(ivar) if instance_variable_defined?(ivar)
+ end
+ end
+
+ private
+
+ def cache_instance_variable_name(key)
+ :"@#{key.to_s.tr('?!', '')}"
+ end
+ end
+end
diff --git a/lib/repository_cache.rb b/lib/repository_cache.rb
deleted file mode 100644
index 068a95790c0..00000000000
--- a/lib/repository_cache.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-# Interface to the Redis-backed cache store used by the Repository model
-class RepositoryCache
- attr_reader :namespace, :backend, :project_id
-
- def initialize(namespace, project_id, backend = Rails.cache)
- @namespace = namespace
- @backend = backend
- @project_id = project_id
- end
-
- def cache_key(type)
- "#{type}:#{namespace}:#{project_id}"
- end
-
- def expire(key)
- backend.delete(cache_key(key))
- end
-
- def fetch(key, &block)
- backend.fetch(cache_key(key), &block)
- end
-
- def exist?(key)
- backend.exist?(cache_key(key))
- end
-
- def read(key)
- backend.read(cache_key(key))
- end
-end
diff --git a/spec/controllers/admin/projects_controller_spec.rb b/spec/controllers/admin/projects_controller_spec.rb
index d5a3c250f31..cc200b9fed9 100644
--- a/spec/controllers/admin/projects_controller_spec.rb
+++ b/spec/controllers/admin/projects_controller_spec.rb
@@ -31,5 +31,15 @@ describe Admin::ProjectsController do
expect(response.body).not_to match(pending_delete_project.name)
expect(response.body).to match(project.name)
end
+
+ it 'does not have N+1 queries', :use_clean_rails_memory_store_caching, :request_store do
+ get :index
+
+ control_count = ActiveRecord::QueryRecorder.new { get :index }.count
+
+ create(:project)
+
+ expect { get :index }.not_to exceed_query_limit(control_count)
+ end
end
end
diff --git a/spec/features/ci_lint_spec.rb b/spec/features/ci_lint_spec.rb
index b1dceec9da8..220b934154e 100644
--- a/spec/features/ci_lint_spec.rb
+++ b/spec/features/ci_lint_spec.rb
@@ -39,6 +39,7 @@ describe 'CI Lint', :js do
it 'displays information about an error' do
expect(page).to have_content('Status: syntax is incorrect')
+ expect(page).to have_selector('.ace_content', text: yaml_content)
end
end
diff --git a/spec/features/profiles/user_visits_notifications_tab_spec.rb b/spec/features/profiles/user_visits_notifications_tab_spec.rb
index 1952fdae798..95953fbcfac 100644
--- a/spec/features/profiles/user_visits_notifications_tab_spec.rb
+++ b/spec/features/profiles/user_visits_notifications_tab_spec.rb
@@ -16,6 +16,6 @@ feature 'User visits the notifications tab', :js do
first('#notifications-button').click
click_link('On mention')
- expect(page).to have_content('On mention')
+ expect(page).to have_selector('#notifications-button', text: 'On mention')
end
end
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 849d85061df..09b6cc4dbf5 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -349,6 +349,18 @@ describe 'Pipelines', :js do
it { expect(page).not_to have_selector('.build-artifacts') }
end
+
+ context 'with trace artifact' do
+ before do
+ create(:ci_build, :success, :trace_artifact, pipeline: pipeline)
+
+ visit_project_pipelines
+ end
+
+ it 'does not show trace artifact as artifacts' do
+ expect(page).not_to have_selector('.build-artifacts')
+ end
+ end
end
context 'mini pipeline graph' do
diff --git a/spec/features/projects/show_project_spec.rb b/spec/features/projects/show_project_spec.rb
index 0a014e9f080..e4f13e6cab7 100644
--- a/spec/features/projects/show_project_spec.rb
+++ b/spec/features/projects/show_project_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe 'Project show page', :feature do
+ include DropzoneHelper
+
context 'when project pending delete' do
let(:project) { create(:project, :empty_repo, pending_delete: true) }
@@ -334,4 +336,24 @@ describe 'Project show page', :feature do
end
end
end
+
+ describe 'dropzone', :js do
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+
+ before do
+ project.add_master(user)
+ sign_in(user)
+
+ visit project_path(project)
+ end
+
+ it 'can upload files' do
+ find('.add-to-tree').click
+ click_link 'Upload file'
+ drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt'))
+
+ expect(find('.dz-filename')).to have_content('doc_sample.txt')
+ end
+ end
end
diff --git a/spec/lib/gitlab/conflict/file_collection_spec.rb b/spec/lib/gitlab/conflict/file_collection_spec.rb
index 5944ce8049a..c93912db411 100644
--- a/spec/lib/gitlab/conflict/file_collection_spec.rb
+++ b/spec/lib/gitlab/conflict/file_collection_spec.rb
@@ -10,6 +10,38 @@ describe Gitlab::Conflict::FileCollection do
end
end
+ describe '#cache' do
+ it 'specifies a custom namespace with the merge request commit ids' do
+ our_commit = merge_request.source_branch_head.raw
+ their_commit = merge_request.target_branch_head.raw
+ custom_namespace = "#{our_commit.id}:#{their_commit.id}"
+
+ expect(file_collection.send(:cache).namespace).to include(custom_namespace)
+ end
+ end
+
+ describe '#can_be_resolved_in_ui?' do
+ it 'returns true if conflicts for this collection can be resolved in the UI' do
+ expect(file_collection.can_be_resolved_in_ui?).to be true
+ end
+
+ it "returns false if conflicts for this collection can't be resolved in the UI" do
+ expect(file_collection).to receive(:files).and_raise(Gitlab::Git::Conflict::Resolver::ConflictSideMissing)
+
+ expect(file_collection.can_be_resolved_in_ui?).to be false
+ end
+
+ it 'caches the result' do
+ expect(file_collection).to receive(:files).twice.and_call_original
+
+ expect(file_collection.can_be_resolved_in_ui?).to be true
+
+ expect(file_collection).not_to receive(:files)
+
+ expect(file_collection.can_be_resolved_in_ui?).to be true
+ end
+ end
+
describe '#default_commit_message' do
it 'matches the format of the git CLI commit message' do
expect(file_collection.default_commit_message).to eq(<<EOM.chomp)
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index b2f13fae73f..1fe1d3926ad 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -287,6 +287,29 @@ describe Gitlab::Database do
end
end
+ describe '.cached_column_exists?' do
+ it 'only retrieves data once' do
+ expect(ActiveRecord::Base.connection).to receive(:columns).once.and_call_original
+
+ 2.times do
+ expect(described_class.cached_column_exists?(:projects, :id)).to be_truthy
+ expect(described_class.cached_column_exists?(:projects, :bogus_column)).to be_falsey
+ end
+ end
+ end
+
+ describe '.cached_table_exists?' do
+ it 'only retrieves data once per table' do
+ expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:projects).once.and_call_original
+ expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:bogus_table_name).once.and_call_original
+
+ 2.times do
+ expect(described_class.cached_table_exists?(:projects)).to be_truthy
+ expect(described_class.cached_table_exists?(:bogus_table_name)).to be_falsey
+ end
+ end
+ end
+
describe '#true_value' do
it 'returns correct value for PostgreSQL' do
expect(described_class).to receive(:postgresql?).and_return(true)
diff --git a/spec/lib/gitlab/project_transfer_spec.rb b/spec/lib/gitlab/project_transfer_spec.rb
index 10c5fb148cd..0b9b1f537b5 100644
--- a/spec/lib/gitlab/project_transfer_spec.rb
+++ b/spec/lib/gitlab/project_transfer_spec.rb
@@ -21,30 +21,77 @@ describe Gitlab::ProjectTransfer do
describe '#move_project' do
it "moves project upload to another namespace" do
- FileUtils.mkdir_p(File.join(@root_dir, @namespace_path_was, @project_path))
+ path_to_be_moved = File.join(@root_dir, @namespace_path_was, @project_path)
+ expected_path = File.join(@root_dir, @namespace_path, @project_path)
+ FileUtils.mkdir_p(path_to_be_moved)
+
@project_transfer.move_project(@project_path, @namespace_path_was, @namespace_path)
- expected_path = File.join(@root_dir, @namespace_path, @project_path)
expect(Dir.exist?(expected_path)).to be_truthy
end
end
+ describe '#move_namespace' do
+ context 'when moving namespace from root into another namespace' do
+ it "moves namespace projects' upload" do
+ child_namespace = 'test_child_namespace'
+ path_to_be_moved = File.join(@root_dir, child_namespace, @project_path)
+ expected_path = File.join(@root_dir, @namespace_path, child_namespace, @project_path)
+ FileUtils.mkdir_p(path_to_be_moved)
+
+ @project_transfer.move_namespace(child_namespace, nil, @namespace_path)
+
+ expect(Dir.exist?(expected_path)).to be_truthy
+ end
+ end
+
+ context 'when moving namespace from one parent to another' do
+ it "moves namespace projects' upload" do
+ child_namespace = 'test_child_namespace'
+ path_to_be_moved = File.join(@root_dir, @namespace_path_was, child_namespace, @project_path)
+ expected_path = File.join(@root_dir, @namespace_path, child_namespace, @project_path)
+ FileUtils.mkdir_p(path_to_be_moved)
+
+ @project_transfer.move_namespace(child_namespace, @namespace_path_was, @namespace_path)
+
+ expect(Dir.exist?(expected_path)).to be_truthy
+ end
+ end
+
+ context 'when moving namespace from having a parent to root' do
+ it "moves namespace projects' upload" do
+ child_namespace = 'test_child_namespace'
+ path_to_be_moved = File.join(@root_dir, @namespace_path_was, child_namespace, @project_path)
+ expected_path = File.join(@root_dir, child_namespace, @project_path)
+ FileUtils.mkdir_p(path_to_be_moved)
+
+ @project_transfer.move_namespace(child_namespace, @namespace_path_was, nil)
+
+ expect(Dir.exist?(expected_path)).to be_truthy
+ end
+ end
+ end
+
describe '#rename_project' do
it "renames project" do
- FileUtils.mkdir_p(File.join(@root_dir, @namespace_path, @project_path_was))
+ path_to_be_moved = File.join(@root_dir, @namespace_path, @project_path_was)
+ expected_path = File.join(@root_dir, @namespace_path, @project_path)
+ FileUtils.mkdir_p(path_to_be_moved)
+
@project_transfer.rename_project(@project_path_was, @project_path, @namespace_path)
- expected_path = File.join(@root_dir, @namespace_path, @project_path)
expect(Dir.exist?(expected_path)).to be_truthy
end
end
describe '#rename_namespace' do
it "renames namespace" do
- FileUtils.mkdir_p(File.join(@root_dir, @namespace_path_was, @project_path))
+ path_to_be_moved = File.join(@root_dir, @namespace_path_was, @project_path)
+ expected_path = File.join(@root_dir, @namespace_path, @project_path)
+ FileUtils.mkdir_p(path_to_be_moved)
+
@project_transfer.rename_namespace(@namespace_path_was, @namespace_path)
- expected_path = File.join(@root_dir, @namespace_path, @project_path)
expect(Dir.exist?(expected_path)).to be_truthy
end
end
diff --git a/spec/lib/gitlab/repository_cache_adapter_spec.rb b/spec/lib/gitlab/repository_cache_adapter_spec.rb
new file mode 100644
index 00000000000..85971f2a7ef
--- /dev/null
+++ b/spec/lib/gitlab/repository_cache_adapter_spec.rb
@@ -0,0 +1,76 @@
+require 'spec_helper'
+
+describe Gitlab::RepositoryCacheAdapter do
+ let(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
+ let(:cache) { repository.send(:cache) }
+
+ describe '#cache_method_output', :use_clean_rails_memory_store_caching do
+ let(:fallback) { 10 }
+
+ context 'with a non-existing repository' do
+ let(:project) { create(:project) } # No repository
+
+ subject do
+ repository.cache_method_output(:cats, fallback: fallback) do
+ repository.cats_call_stub
+ end
+ end
+
+ it 'returns the fallback value' do
+ expect(subject).to eq(fallback)
+ end
+
+ it 'avoids calling the original method' do
+ expect(repository).not_to receive(:cats_call_stub)
+
+ subject
+ end
+ end
+
+ context 'with a method throwing a non-existing-repository error' do
+ subject do
+ repository.cache_method_output(:cats, fallback: fallback) do
+ raise Gitlab::Git::Repository::NoRepository
+ end
+ end
+
+ it 'returns the fallback value' do
+ expect(subject).to eq(fallback)
+ end
+
+ it 'does not cache the data' do
+ subject
+
+ expect(repository.instance_variable_defined?(:@cats)).to eq(false)
+ expect(cache.exist?(:cats)).to eq(false)
+ end
+ end
+
+ context 'with an existing repository' do
+ it 'caches the output' do
+ object = double
+
+ expect(object).to receive(:number).once.and_return(10)
+
+ 2.times do
+ val = repository.cache_method_output(:cats) { object.number }
+
+ expect(val).to eq(10)
+ end
+
+ expect(repository.send(:cache).exist?(:cats)).to eq(true)
+ expect(repository.instance_variable_get(:@cats)).to eq(10)
+ end
+ end
+ end
+
+ describe '#expire_method_caches' do
+ it 'expires the caches of the given methods' do
+ expect(cache).to receive(:expire).with(:readme)
+ expect(cache).to receive(:expire).with(:gitignore)
+
+ repository.expire_method_caches(%i(readme gitignore))
+ end
+ end
+end
diff --git a/spec/lib/gitlab/repository_cache_spec.rb b/spec/lib/gitlab/repository_cache_spec.rb
new file mode 100644
index 00000000000..fc259cf1208
--- /dev/null
+++ b/spec/lib/gitlab/repository_cache_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+
+describe Gitlab::RepositoryCache do
+ let(:backend) { double('backend').as_null_object }
+ let(:project) { create(:project) }
+ let(:repository) { project.repository }
+ let(:namespace) { "#{repository.full_path}:#{project.id}" }
+ let(:cache) { described_class.new(repository, backend: backend) }
+
+ describe '#cache_key' do
+ subject { cache.cache_key(:foo) }
+
+ it 'includes the namespace' do
+ expect(subject).to eq "foo:#{namespace}"
+ end
+
+ context 'with a given namespace' do
+ let(:extra_namespace) { 'my:data' }
+ let(:cache) do
+ described_class.new(repository, extra_namespace: extra_namespace,
+ backend: backend)
+ end
+
+ it 'includes the full namespace' do
+ expect(subject).to eq "foo:#{namespace}:#{extra_namespace}"
+ end
+ end
+ end
+
+ describe '#expire' do
+ it 'expires the given key from the cache' do
+ cache.expire(:foo)
+ expect(backend).to have_received(:delete).with("foo:#{namespace}")
+ end
+ end
+
+ describe '#fetch' do
+ it 'fetches the given key from the cache' do
+ cache.fetch(:bar)
+ expect(backend).to have_received(:fetch).with("bar:#{namespace}")
+ end
+
+ it 'accepts a block' do
+ p = -> {}
+
+ cache.fetch(:baz, &p)
+ expect(backend).to have_received(:fetch).with("baz:#{namespace}", &p)
+ end
+ end
+end
diff --git a/spec/lib/repository_cache_spec.rb b/spec/lib/repository_cache_spec.rb
deleted file mode 100644
index 8b0c7254b5e..00000000000
--- a/spec/lib/repository_cache_spec.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require 'spec_helper'
-
-describe RepositoryCache do
- let(:project) { create(:project) }
- let(:backend) { double('backend').as_null_object }
- let(:cache) { described_class.new('example', project.id, backend) }
-
- describe '#cache_key' do
- it 'includes the namespace' do
- expect(cache.cache_key(:foo)).to eq "foo:example:#{project.id}"
- end
- end
-
- describe '#expire' do
- it 'expires the given key from the cache' do
- cache.expire(:foo)
- expect(backend).to have_received(:delete).with("foo:example:#{project.id}")
- end
- end
-
- describe '#fetch' do
- it 'fetches the given key from the cache' do
- cache.fetch(:bar)
- expect(backend).to have_received(:fetch).with("bar:example:#{project.id}")
- end
-
- it 'accepts a block' do
- p = -> {}
-
- cache.fetch(:baz, &p)
- expect(backend).to have_received(:fetch).with("baz:example:#{project.id}", &p)
- end
- end
-end
diff --git a/spec/migrations/migrate_old_artifacts_spec.rb b/spec/migrations/migrate_old_artifacts_spec.rb
index 92eb1d9ce86..638b2853374 100644
--- a/spec/migrations/migrate_old_artifacts_spec.rb
+++ b/spec/migrations/migrate_old_artifacts_spec.rb
@@ -66,7 +66,7 @@ describe MigrateOldArtifacts do
end
it 'all files do have artifacts' do
- Ci::Build.with_artifacts do |build|
+ Ci::Build.with_artifacts_archive do |build|
expect(build).to have_artifacts
end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index c27313ed88b..59b82bfa093 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -80,6 +80,42 @@ describe Ci::Build do
end
end
+ describe '.with_artifacts_archive' do
+ subject { described_class.with_artifacts_archive }
+
+ context 'when job does not have an archive' do
+ let!(:job) { create(:ci_build) }
+
+ it 'does not return the job' do
+ is_expected.not_to include(job)
+ end
+ end
+
+ context 'when job has a legacy archive' do
+ let!(:job) { create(:ci_build, :legacy_artifacts) }
+
+ it 'returns the job' do
+ is_expected.to include(job)
+ end
+ end
+
+ context 'when job has a job artifact archive' do
+ let!(:job) { create(:ci_build, :artifacts) }
+
+ it 'returns the job' do
+ is_expected.to include(job)
+ end
+ end
+
+ context 'when job has a job artifact trace' do
+ let!(:job) { create(:ci_build, :trace_artifact) }
+
+ it 'does not return the job' do
+ is_expected.not_to include(job)
+ end
+ end
+ end
+
describe '#actionize' do
context 'when build is a created' do
before do
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index e626efd054d..ee142718f7e 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -204,43 +204,67 @@ describe Namespace do
expect(gitlab_shell.exists?(project.repository_storage_path, "#{namespace.path}/#{project.path}.git")).to be_truthy
end
- context 'with subgroups' do
+ context 'with subgroups', :nested_groups do
let(:parent) { create(:group, name: 'parent', path: 'parent') }
+ let(:new_parent) { create(:group, name: 'new_parent', path: 'new_parent') }
let(:child) { create(:group, name: 'child', path: 'child', parent: parent) }
let!(:project) { create(:project_empty_repo, :legacy_storage, path: 'the-project', namespace: child, skip_disk_validation: true) }
let(:uploads_dir) { FileUploader.root }
let(:pages_dir) { File.join(TestEnv.pages_path) }
+ def expect_project_directories_at(namespace_path)
+ expected_repository_path = File.join(TestEnv.repos_path, namespace_path, 'the-project.git')
+ expected_upload_path = File.join(uploads_dir, namespace_path, 'the-project')
+ expected_pages_path = File.join(pages_dir, namespace_path, 'the-project')
+
+ expect(File.directory?(expected_repository_path)).to be_truthy
+ expect(File.directory?(expected_upload_path)).to be_truthy
+ expect(File.directory?(expected_pages_path)).to be_truthy
+ end
+
before do
+ FileUtils.mkdir_p(File.join(TestEnv.repos_path, "#{project.full_path}.git"))
FileUtils.mkdir_p(File.join(uploads_dir, project.full_path))
FileUtils.mkdir_p(File.join(pages_dir, project.full_path))
end
context 'renaming child' do
it 'correctly moves the repository, uploads and pages' do
- expected_repository_path = File.join(TestEnv.repos_path, 'parent', 'renamed', 'the-project.git')
- expected_upload_path = File.join(uploads_dir, 'parent', 'renamed', 'the-project')
- expected_pages_path = File.join(pages_dir, 'parent', 'renamed', 'the-project')
+ child.update!(path: 'renamed')
- child.update_attributes!(path: 'renamed')
-
- expect(File.directory?(expected_repository_path)).to be(true)
- expect(File.directory?(expected_upload_path)).to be(true)
- expect(File.directory?(expected_pages_path)).to be(true)
+ expect_project_directories_at('parent/renamed')
end
end
context 'renaming parent' do
it 'correctly moves the repository, uploads and pages' do
- expected_repository_path = File.join(TestEnv.repos_path, 'renamed', 'child', 'the-project.git')
- expected_upload_path = File.join(uploads_dir, 'renamed', 'child', 'the-project')
- expected_pages_path = File.join(pages_dir, 'renamed', 'child', 'the-project')
+ parent.update!(path: 'renamed')
+
+ expect_project_directories_at('renamed/child')
+ end
+ end
+
+ context 'moving from one parent to another' do
+ it 'correctly moves the repository, uploads and pages' do
+ child.update!(parent: new_parent)
- parent.update_attributes!(path: 'renamed')
+ expect_project_directories_at('new_parent/child')
+ end
+ end
+
+ context 'moving from having a parent to root' do
+ it 'correctly moves the repository, uploads and pages' do
+ child.update!(parent: nil)
+
+ expect_project_directories_at('child')
+ end
+ end
+
+ context 'moving from root to having a parent' do
+ it 'correctly moves the repository, uploads and pages' do
+ parent.update!(parent: new_parent)
- expect(File.directory?(expected_repository_path)).to be(true)
- expect(File.directory?(expected_upload_path)).to be(true)
- expect(File.directory?(expected_pages_path)).to be(true)
+ expect_project_directories_at('new_parent/parent/child')
end
end
end
@@ -525,7 +549,6 @@ describe Namespace do
end
end
- # Note: Group transfers are not yet implemented
context 'when a group is transferred into a root group' do
context 'when the root group "Share with group lock" is enabled' do
let(:root_group) { create(:group, share_with_group_lock: true) }
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 93a61c6ea71..5bc972bca14 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -2169,15 +2169,6 @@ describe Repository do
end
end
- describe '#expire_method_caches' do
- it 'expires the caches of the given methods' do
- expect_any_instance_of(RepositoryCache).to receive(:expire).with(:readme)
- expect_any_instance_of(RepositoryCache).to receive(:expire).with(:gitignore)
-
- repository.expire_method_caches(%i(readme gitignore))
- end
- end
-
describe '#expire_all_method_caches' do
it 'expires the caches of all methods' do
expect(repository).to receive(:expire_method_caches)
@@ -2323,66 +2314,6 @@ describe Repository do
end
end
- describe '#cache_method_output', :use_clean_rails_memory_store_caching do
- let(:fallback) { 10 }
-
- context 'with a non-existing repository' do
- let(:project) { create(:project) } # No repository
-
- subject do
- repository.cache_method_output(:cats, fallback: fallback) do
- repository.cats_call_stub
- end
- end
-
- it 'returns the fallback value' do
- expect(subject).to eq(fallback)
- end
-
- it 'avoids calling the original method' do
- expect(repository).not_to receive(:cats_call_stub)
-
- subject
- end
- end
-
- context 'with a method throwing a non-existing-repository error' do
- subject do
- repository.cache_method_output(:cats, fallback: fallback) do
- raise Gitlab::Git::Repository::NoRepository
- end
- end
-
- it 'returns the fallback value' do
- expect(subject).to eq(fallback)
- end
-
- it 'does not cache the data' do
- subject
-
- expect(repository.instance_variable_defined?(:@cats)).to eq(false)
- expect(repository.send(:cache).exist?(:cats)).to eq(false)
- end
- end
-
- context 'with an existing repository' do
- it 'caches the output' do
- object = double
-
- expect(object).to receive(:number).once.and_return(10)
-
- 2.times do
- val = repository.cache_method_output(:cats) { object.number }
-
- expect(val).to eq(10)
- end
-
- expect(repository.send(:cache).exist?(:cats)).to eq(true)
- expect(repository.instance_variable_get(:@cats)).to eq(10)
- end
- end
- end
-
describe '#refresh_method_caches' do
it 'refreshes the caches of the given types' do
expect(repository).to receive(:expire_method_caches)
diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
index 33f9efc1490..4223dc18933 100644
--- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
@@ -285,8 +285,8 @@ production:
export CI_APPLICATION_TAG=$CI_COMMIT_SHA
export CI_CONTAINER_NAME=ci_job_build_${CI_JOB_ID}
export TILLER_NAMESPACE=$KUBE_NAMESPACE
- # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" for Static Code Analysis
- export SCA_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
+ # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" for Security Products
+ export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
function sast_container() {
if [[ -n "$CI_REGISTRY_USER" ]]; then
@@ -307,11 +307,10 @@ production:
}
function codeclimate() {
- docker run --env CODECLIMATE_CODE="$PWD" \
+ docker run --env SOURCE_CODE="$PWD" \
--volume "$PWD":/code \
--volume /var/run/docker.sock:/var/run/docker.sock \
- --volume /tmp/cc:/tmp/cc \
- "registry.gitlab.com/gitlab-org/security-products/codequality/codeclimate:${SCA_VERSION}" analyze -f json > codeclimate.json
+ "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
}
function sast() {
@@ -328,7 +327,7 @@ production:
--env SAST_DISABLE_REMOTE_CHECKS="${SAST_DISABLE_REMOTE_CHECKS:-false}" \
--volume "$PWD:/code" \
--volume /var/run/docker.sock:/var/run/docker.sock \
- "registry.gitlab.com/gitlab-org/security-products/sast:$SCA_VERSION" /app/bin/run /code
+ "registry.gitlab.com/gitlab-org/security-products/sast:$SP_VERSION" /app/bin/run /code
;;
*)
echo "GitLab EE is required"