summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/releases.gitlab-ci.yml22
-rw-r--r--CHANGELOG.md18
-rw-r--r--VERSION2
-rw-r--r--app/assets/javascripts/repository/components/last_commit.vue28
-rw-r--r--app/assets/stylesheets/framework/memory_graph.scss6
-rw-r--r--app/controllers/clusters/clusters_controller.rb2
-rw-r--r--app/controllers/concerns/internal_redirect.rb2
-rw-r--r--app/controllers/concerns/lfs_request.rb1
-rw-r--r--app/helpers/markup_helper.rb10
-rw-r--r--app/models/clusters/cluster.rb2
-rw-r--r--app/models/clusters/clusters_hierarchy.rb41
-rw-r--r--app/models/concerns/deployment_platform.rb2
-rw-r--r--app/models/discussion.rb1
-rw-r--r--app/models/milestone.rb4
-rw-r--r--app/models/note.rb4
-rw-r--r--app/models/project.rb4
-rw-r--r--app/models/wiki_page.rb6
-rw-r--r--app/policies/note_policy.rb2
-rw-r--r--app/serializers/merge_request_diff_entity.rb2
-rw-r--r--app/services/clusters/update_service.rb50
-rw-r--r--app/services/error_tracking/list_projects_service.rb9
-rw-r--r--app/services/notification_service.rb2
-rw-r--r--app/services/projects/operations/update_service.rb6
-rw-r--r--app/views/projects/compare/_form.html.haml10
-rw-r--r--app/views/projects/settings/operations/_error_tracking.html.haml2
-rw-r--r--changelogs/unreleased/28336-dropdown-icon-missing-on-compare-page.yml5
-rw-r--r--changelogs/unreleased/31390-remove-pointer-cursor-from-memory-usage-chart.yml5
-rw-r--r--changelogs/unreleased/8558-bump-ado-image-for-modsec-secruleengine.yml5
-rw-r--r--changelogs/unreleased/cluster_management_projects_updating.yml5
-rw-r--r--changelogs/unreleased/id-nil-short-commit-sha.yml5
-rw-r--r--changelogs/unreleased/introduce-feature-flag-api-enable-disable.yml5
-rw-r--r--changelogs/unreleased/ph-fixAdminGeoSidebarFlyOut.yml5
-rw-r--r--changelogs/unreleased/security-id-fix-disclosure-of-private-repo-names.yml5
-rw-r--r--changelogs/unreleased/security-mask-sentry-token-ce.yml4
-rw-r--r--changelogs/unreleased/security-open-redirect-internalredirect.yml5
-rw-r--r--changelogs/unreleased/security-wiki-rdoc-content.yml5
-rw-r--r--doc/api/group_clusters.md32
-rw-r--r--doc/api/project_clusters.md32
-rw-r--r--doc/development/documentation/site_architecture/index.md145
-rw-r--r--doc/development/documentation/site_architecture/release_process.md241
-rw-r--r--doc/development/testing_guide/review_apps.md8
-rw-r--r--doc/topics/autodevops/index.md1
-rw-r--r--doc/user/clusters/management_project.md14
-rw-r--r--lib/api/entities.rb1
-rw-r--r--lib/api/group_clusters.rb1
-rw-r--r--lib/api/helpers/internal_helpers.rb3
-rw-r--r--lib/api/project_clusters.rb1
-rw-r--r--lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml2
-rw-r--r--lib/gitlab/experimentation.rb6
-rw-r--r--lib/gitlab/other_markup.rb2
-rw-r--r--lib/quality/kubernetes_client.rb11
-rw-r--r--locale/gitlab.pot3
-rw-r--r--qa/qa/resource/user.rb1
-rwxr-xr-xscripts/review_apps/automated_cleanup.rb27
-rw-r--r--scripts/sync-stable-branch.sh32
-rwxr-xr-xscripts/trigger-build35
-rw-r--r--spec/controllers/concerns/internal_redirect_spec.rb3
-rw-r--r--spec/controllers/concerns/lfs_request_spec.rb43
-rw-r--r--spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap56
-rw-r--r--spec/helpers/markup_helper_spec.rb78
-rw-r--r--spec/lib/gitlab/experimentation_spec.rb18
-rw-r--r--spec/lib/quality/helm_client_spec.rb20
-rw-r--r--spec/lib/quality/kubernetes_client_spec.rb25
-rw-r--r--spec/migrations/schedule_fix_gitlab_com_pages_access_level_spec.rb2
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb50
-rw-r--r--spec/models/clusters/cluster_spec.rb10
-rw-r--r--spec/models/clusters/clusters_hierarchy_spec.rb40
-rw-r--r--spec/models/concerns/deployment_platform_spec.rb12
-rw-r--r--spec/models/milestone_spec.rb8
-rw-r--r--spec/models/note_spec.rb20
-rw-r--r--spec/models/project_spec.rb8
-rw-r--r--spec/models/shard_spec.rb3
-rw-r--r--spec/models/wiki_page_spec.rb17
-rw-r--r--spec/requests/api/group_clusters_spec.rb21
-rw-r--r--spec/requests/api/internal/base_spec.rb3
-rw-r--r--spec/requests/api/project_clusters_spec.rb21
-rw-r--r--spec/serializers/merge_request_diff_entity_spec.rb19
-rw-r--r--spec/services/clusters/update_service_spec.rb110
-rw-r--r--spec/services/error_tracking/list_projects_service_spec.rb13
-rw-r--r--spec/services/projects/operations/update_service_spec.rb21
-rw-r--r--spec/sidekiq/cron/job_gem_dependency_spec.rb2
-rw-r--r--spec/support/helpers/smime_helper.rb2
83 files changed, 1315 insertions, 202 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c1a9d38d5aa..36108d04e9c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,6 +1,7 @@
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.22-chrome-73.0-node-12.x-yarn-1.16-postgresql-9.6-graphicsmagick-1.3.33"
stages:
+ - sync
- prepare
- quick-test
- test
@@ -40,3 +41,4 @@ include:
- local: .gitlab/ci/setup.gitlab-ci.yml
- local: .gitlab/ci/test-metadata.gitlab-ci.yml
- local: .gitlab/ci/yaml.gitlab-ci.yml
+ - local: .gitlab/ci/releases.gitlab-ci.yml
diff --git a/.gitlab/ci/releases.gitlab-ci.yml b/.gitlab/ci/releases.gitlab-ci.yml
new file mode 100644
index 00000000000..1ddc4e90fcf
--- /dev/null
+++ b/.gitlab/ci/releases.gitlab-ci.yml
@@ -0,0 +1,22 @@
+---
+
+# Syncs any changes pushed to a stable branch to the corresponding CE stable
+# branch. We run this prior to any tests so that random failures don't prevent a
+# sync.
+sync-stable-branch:
+ # We don't need/want any global before/after commands, so we overwrite these
+ # settings.
+ image: alpine:edge
+ stage: sync
+ # This job should only run on EE stable branches on the canonical GitLab.com
+ # repository.
+ only:
+ variables:
+ - $CI_SERVER_HOST == "gitlab.com"
+ refs:
+ - /^[\d-]+-stable-ee$/@gitlab-org/gitlab
+ before_script:
+ - apk add --no-cache --update curl bash
+ after_script: []
+ script:
+ - bash scripts/sync-stable-branch.sh
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9411180abff..16a36724b4f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,24 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 12.4.1
+
+### Security (12 changes)
+
+- Standardize error response when route is missing.
+- Do not display project labels that are not visible for user accessing group labels.
+- Show cross-referenced label and milestones in issues' activities only to authorized users.
+- Analyze incoming GraphQL queries and check for recursion.
+- Disallow unprivileged users from commenting on private repository commits.
+- Don't allow maintainers of a target project to delete the source branch of a merge request from a fork.
+- Require Maintainer permission on group where project is transferred to.
+- Don't leak private members in project member autocomplete suggestions.
+- Return 404 on LFS request if project doesn't exist.
+- Mask sentry auth token in Error Tracking dashboard.
+- Fixes a Open Redirect issue in `InternalRedirect`.
+- Sanitize all wiki markup formats with GitLab sanitization pipelines.
+
+
## 12.4.0
### Security (14 changes)
diff --git a/VERSION b/VERSION
index 80212c6e1f0..4dd2ed8f250 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-12.3.0-pre
+12.5.0-pre
diff --git a/app/assets/javascripts/repository/components/last_commit.vue b/app/assets/javascripts/repository/components/last_commit.vue
index 19a2db2db25..aa270a374ae 100644
--- a/app/assets/javascripts/repository/components/last_commit.vue
+++ b/app/assets/javascripts/repository/components/last_commit.vue
@@ -125,19 +125,21 @@ export default {
</div>
<div class="commit-actions flex-row">
<div v-if="commit.signatureHtml" v-html="commit.signatureHtml"></div>
- <gl-link
- v-if="commit.latestPipeline"
- v-gl-tooltip
- :href="commit.latestPipeline.detailedStatus.detailsPath"
- :title="statusTitle"
- class="js-commit-pipeline"
- >
- <ci-icon
- :status="commit.latestPipeline.detailedStatus"
- :size="24"
- :aria-label="statusTitle"
- />
- </gl-link>
+ <div class="ci-status-link">
+ <gl-link
+ v-if="commit.latestPipeline"
+ v-gl-tooltip
+ :href="commit.latestPipeline.detailedStatus.detailsPath"
+ :title="statusTitle"
+ class="js-commit-pipeline"
+ >
+ <ci-icon
+ :status="commit.latestPipeline.detailedStatus"
+ :size="24"
+ :aria-label="statusTitle"
+ />
+ </gl-link>
+ </div>
<div class="commit-sha-group d-flex">
<div class="label label-monospace monospace">
{{ showCommitId }}
diff --git a/app/assets/stylesheets/framework/memory_graph.scss b/app/assets/stylesheets/framework/memory_graph.scss
index 81cdf6b59e4..c84010c6f10 100644
--- a/app/assets/stylesheets/framework/memory_graph.scss
+++ b/app/assets/stylesheets/framework/memory_graph.scss
@@ -1,11 +1,7 @@
.memory-graph-container {
svg {
background: $white-light;
- cursor: pointer;
-
- &:hover {
- box-shadow: 0 0 4px $gray-darkest inset;
- }
+ border: 1px solid $gray-200;
}
path {
diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb
index bb47ccb72b3..abec237dd1d 100644
--- a/app/controllers/clusters/clusters_controller.rb
+++ b/app/controllers/clusters/clusters_controller.rb
@@ -142,6 +142,7 @@ class Clusters::ClustersController < Clusters::BaseController
:environment_scope,
:managed,
:base_domain,
+ :management_project_id,
platform_kubernetes_attributes: [
:api_url,
:token,
@@ -155,6 +156,7 @@ class Clusters::ClustersController < Clusters::BaseController
:environment_scope,
:managed,
:base_domain,
+ :management_project_id,
platform_kubernetes_attributes: [
:namespace
]
diff --git a/app/controllers/concerns/internal_redirect.rb b/app/controllers/concerns/internal_redirect.rb
index 99bbfd56516..a35bc19aa37 100644
--- a/app/controllers/concerns/internal_redirect.rb
+++ b/app/controllers/concerns/internal_redirect.rb
@@ -6,7 +6,7 @@ module InternalRedirect
def safe_redirect_path(path)
return unless path
# Verify that the string starts with a `/` and a known route character.
- return unless path =~ %r{^/[-\w].*$}
+ return unless path =~ %r{\A/[-\w].*\z}
uri = URI(path)
# Ignore anything path of the redirect except for the path, querystring and,
diff --git a/app/controllers/concerns/lfs_request.rb b/app/controllers/concerns/lfs_request.rb
index 733265f4099..417bb169f39 100644
--- a/app/controllers/concerns/lfs_request.rb
+++ b/app/controllers/concerns/lfs_request.rb
@@ -34,6 +34,7 @@ module LfsRequest
end
def lfs_check_access!
+ return render_lfs_not_found unless project
return if download_request? && lfs_download_access?
return if upload_request? && lfs_upload_access?
diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb
index d76a0f3a3b8..e2524938e10 100644
--- a/app/helpers/markup_helper.rb
+++ b/app/helpers/markup_helper.rb
@@ -133,15 +133,7 @@ module MarkupHelper
issuable_state_filter_enabled: true
)
- html =
- case wiki_page.format
- when :markdown
- markdown_unsafe(text, context)
- when :asciidoc
- asciidoc_unsafe(text)
- else
- wiki_page.formatted_content.html_safe
- end
+ html = markup_unsafe(wiki_page.path, text, context)
prepare_for_rendering(html, context)
end
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb
index c600717f5df..e3e2f06c313 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -117,6 +117,8 @@ module Clusters
scope :default_environment, -> { where(environment_scope: DEFAULT_ENVIRONMENT) }
+ scope :for_project_namespace, -> (namespace_id) { joins(:projects).where(projects: { namespace_id: namespace_id }) }
+
def self.ancestor_clusters_for_clusterable(clusterable, hierarchy_order: :asc)
return [] if clusterable.is_a?(Instance)
diff --git a/app/models/clusters/clusters_hierarchy.rb b/app/models/clusters/clusters_hierarchy.rb
index a906eb2888b..c9c18d8c96a 100644
--- a/app/models/clusters/clusters_hierarchy.rb
+++ b/app/models/clusters/clusters_hierarchy.rb
@@ -20,7 +20,7 @@ module Clusters
.with
.recursive(cte.to_arel)
.from(cte_alias)
- .order(DEPTH_COLUMN => :asc)
+ .order(depth_order_clause)
end
private
@@ -40,7 +40,7 @@ module Clusters
end
if clusterable.is_a?(::Project) && include_management_project
- cte << management_clusters_query
+ cte << same_namespace_management_clusters_query
end
cte << base_query
@@ -49,13 +49,42 @@ module Clusters
cte
end
+ # Returns project-level clusters where the project is the management project
+ # for the cluster. The management project has to be in the same namespace /
+ # group as the cluster's project.
+ #
+ # Support for management project in sub-groups is planned in
+ # https://gitlab.com/gitlab-org/gitlab/issues/34650
+ #
+ # NB: group_parent_id is un-used but we still need to match the same number of
+ # columns as other queries in the CTE.
+ def same_namespace_management_clusters_query
+ clusterable.management_clusters
+ .project_type
+ .select([clusters_star, 'NULL AS group_parent_id', "0 AS #{DEPTH_COLUMN}"])
+ .for_project_namespace(clusterable.namespace_id)
+ end
+
# Management clusters should be first in the hierarchy so we use 0 for the
# depth column.
#
- # group_parent_id is un-used but we still need to match the same number of
- # columns as other queries in the CTE.
- def management_clusters_query
- clusterable.management_clusters.select([clusters_star, 'NULL AS group_parent_id', "0 AS #{DEPTH_COLUMN}"])
+ # Only applicable if the clusterable is a project (most especially when
+ # requesting project.deployment_platform).
+ def depth_order_clause
+ return { DEPTH_COLUMN => :asc } unless clusterable.is_a?(::Project) && include_management_project
+
+ order = <<~SQL
+ (CASE clusters.management_project_id
+ WHEN :project_id THEN 0
+ ELSE #{DEPTH_COLUMN}
+ END) ASC
+ SQL
+
+ values = {
+ project_id: clusterable.id
+ }
+
+ model.sanitize_sql_array([Arel.sql(order), values])
end
def group_clusters_base_query
diff --git a/app/models/concerns/deployment_platform.rb b/app/models/concerns/deployment_platform.rb
index fe8e9609820..d0b10fd8671 100644
--- a/app/models/concerns/deployment_platform.rb
+++ b/app/models/concerns/deployment_platform.rb
@@ -12,7 +12,7 @@ module DeploymentPlatform
private
def cluster_management_project_enabled?
- Feature.enabled?(:cluster_management_project, default_enabled: true)
+ Feature.enabled?(:cluster_management_project, self)
end
def find_deployment_platform(environment)
diff --git a/app/models/discussion.rb b/app/models/discussion.rb
index 0d066d0d99f..b8525f7b135 100644
--- a/app/models/discussion.rb
+++ b/app/models/discussion.rb
@@ -16,6 +16,7 @@ class Discussion
:commit_id,
:for_commit?,
:for_merge_request?,
+ :noteable_ability_name,
:to_ability_name,
:editable?,
:visible_for?,
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index 2fa0cfc9b93..a9f4cdec901 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -261,6 +261,10 @@ class Milestone < ApplicationRecord
group || project
end
+ def to_ability_name
+ model_name.singular
+ end
+
def group_milestone?
group_id.present?
end
diff --git a/app/models/note.rb b/app/models/note.rb
index 43f349c6fa2..ce60413b8a0 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -361,6 +361,10 @@ class Note < ApplicationRecord
end
def to_ability_name
+ model_name.singular
+ end
+
+ def noteable_ability_name
for_snippet? ? noteable.class.name.underscore : noteable_type.demodulize.underscore
end
diff --git a/app/models/project.rb b/app/models/project.rb
index b5046bbb6ee..f12adc489a2 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1265,6 +1265,10 @@ class Project < ApplicationRecord
end
end
+ def to_ability_name
+ model_name.singular
+ end
+
# rubocop: disable CodeReuse/ServiceClass
def execute_hooks(data, hooks_scope = :push_hooks)
run_after_commit_or_now do
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index 6b3cb0b39d8..3006753e9e7 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -121,6 +121,12 @@ class WikiPage
@version ||= @page.version
end
+ def path
+ return unless persisted?
+
+ @path ||= @page.path
+ end
+
def versions(options = {})
return [] unless persisted?
diff --git a/app/policies/note_policy.rb b/app/policies/note_policy.rb
index b2af6c874c7..dcde8cefa0d 100644
--- a/app/policies/note_policy.rb
+++ b/app/policies/note_policy.rb
@@ -9,7 +9,7 @@ class NotePolicy < BasePolicy
condition(:editable, scope: :subject) { @subject.editable? }
- condition(:can_read_noteable) { can?(:"read_#{@subject.to_ability_name}") }
+ condition(:can_read_noteable) { can?(:"read_#{@subject.noteable_ability_name}") }
condition(:is_visible) { @subject.visible_for?(@user) }
diff --git a/app/serializers/merge_request_diff_entity.rb b/app/serializers/merge_request_diff_entity.rb
index 7e3053e5881..5c79b165ee9 100644
--- a/app/serializers/merge_request_diff_entity.rb
+++ b/app/serializers/merge_request_diff_entity.rb
@@ -21,6 +21,8 @@ class MergeRequestDiffEntity < Grape::Entity
expose :latest?, as: :latest
expose :short_commit_sha do |merge_request_diff|
+ next unless merge_request_diff.head_commit_sha
+
short_sha(merge_request_diff.head_commit_sha)
end
diff --git a/app/services/clusters/update_service.rb b/app/services/clusters/update_service.rb
index 25d26e761b1..98dd6b26a47 100644
--- a/app/services/clusters/update_service.rb
+++ b/app/services/clusters/update_service.rb
@@ -9,7 +9,55 @@ module Clusters
end
def execute(cluster)
- cluster.update(params)
+ if validate_params(cluster)
+ cluster.update(params)
+ else
+ false
+ end
+ end
+
+ private
+
+ def can_admin_pipeline_for_project?(project)
+ Ability.allowed?(current_user, :admin_pipeline, project)
+ end
+
+ def validate_params(cluster)
+ if params[:management_project_id]
+ management_project = management_project_scope(cluster).find_by_id(params[:management_project_id])
+
+ unless management_project
+ cluster.errors.add(:management_project_id, _('Project does not exist or you don\'t have permission to perform this action'))
+
+ return false
+ end
+
+ unless can_admin_pipeline_for_project?(management_project)
+ # Use same message as not found to prevent enumeration
+ cluster.errors.add(:management_project_id, _('Project does not exist or you don\'t have permission to perform this action'))
+
+ return false
+ end
+ end
+
+ true
+ end
+
+ def management_project_scope(cluster)
+ return ::Project.all if cluster.instance_type?
+
+ group =
+ if cluster.group_type?
+ cluster.first_group
+ elsif cluster.project_type?
+ cluster.first_project&.namespace
+ end
+
+ # Prevent users from selecting nested projects until
+ # https://gitlab.com/gitlab-org/gitlab/issues/34650 is resolved
+ include_subgroups = cluster.group_type?
+
+ ::GroupProjectsFinder.new(group: group, current_user: current_user, options: { only_owned: true, include_subgroups: include_subgroups }).execute
end
end
end
diff --git a/app/services/error_tracking/list_projects_service.rb b/app/services/error_tracking/list_projects_service.rb
index 8d08f0cda94..92d4ef85ecf 100644
--- a/app/services/error_tracking/list_projects_service.rb
+++ b/app/services/error_tracking/list_projects_service.rb
@@ -32,7 +32,7 @@ module ErrorTracking
project_slug: 'proj'
)
- setting.token = params[:token]
+ setting.token = token(setting)
setting.enabled = true
end
end
@@ -40,5 +40,12 @@ module ErrorTracking
def can_read?
can?(current_user, :read_sentry_issue, project)
end
+
+ def token(setting)
+ # Use param token if not masked, otherwise use database token
+ return params[:token] unless /\A\*+\z/.match?(params[:token])
+
+ setting.token
+ end
end
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index b56b2cf14e3..1709474a6c7 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -281,7 +281,7 @@ class NotificationService
end
def send_new_note_notifications(note)
- notify_method = "note_#{note.to_ability_name}_email".to_sym
+ notify_method = "note_#{note.noteable_ability_name}_email".to_sym
recipients = NotificationRecipientService.build_new_note_recipients(note)
recipients.each do |recipient|
diff --git a/app/services/projects/operations/update_service.rb b/app/services/projects/operations/update_service.rb
index 64519501ff4..0ca89664304 100644
--- a/app/services/projects/operations/update_service.rb
+++ b/app/services/projects/operations/update_service.rb
@@ -36,15 +36,17 @@ module Projects
organization_slug: settings.dig(:project, :organization_slug)
)
- {
+ params = {
error_tracking_setting_attributes: {
api_url: api_url,
- token: settings[:token],
enabled: settings[:enabled],
project_name: settings.dig(:project, :name),
organization_name: settings.dig(:project, :organization_name)
}
}
+ params[:error_tracking_setting_attributes][:token] = settings[:token] unless /\A\*+\z/.match?(settings[:token]) # Don't update token if we receive masked value
+
+ params
end
def grafana_integration_params
diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml
index 9744d293c8b..c8c96297672 100644
--- a/app/views/projects/compare/_form.html.haml
+++ b/app/views/projects/compare/_form.html.haml
@@ -8,8 +8,9 @@
.input-group-text
= s_("CompareBranches|Source")
= hidden_field_tag :to, params[:to]
- = button_tag type: 'button', title: params[:to], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip monospace", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-to-dropdown", selected: params[:to], field_name: :to } do
- .dropdown-toggle-text.str-truncated= params[:to] || _("Select branch/tag")
+ = button_tag type: 'button', title: params[:to], class: "btn form-control compare-dropdown-toggle js-compare-dropdown has-tooltip", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-to-dropdown", selected: params[:to], field_name: :to } do
+ .dropdown-toggle-text.str-truncated.monospace.float-left= params[:to] || _("Select branch/tag")
+ = sprite_icon('arrow-down', size: 16, css_class: 'float-right')
= render 'shared/ref_dropdown'
.compare-ellipsis.inline ...
.form-group.dropdown.compare-form-group.from.js-compare-from-dropdown
@@ -18,8 +19,9 @@
.input-group-text
= s_("CompareBranches|Target")
= hidden_field_tag :from, params[:from]
- = button_tag type: 'button', title: params[:from], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip monospace", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from], field_name: :from } do
- .dropdown-toggle-text.str-truncated= params[:from] || _("Select branch/tag")
+ = button_tag type: 'button', title: params[:from], class: "btn form-control compare-dropdown-toggle js-compare-dropdown has-tooltip", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from], field_name: :from } do
+ .dropdown-toggle-text.str-truncated.monospace.float-left= params[:from] || _("Select branch/tag")
+ = sprite_icon('arrow-down', size: 16, css_class: 'float-right')
= render 'shared/ref_dropdown'
&nbsp;
= button_tag s_("CompareBranches|Compare"), class: "btn btn-success commits-compare-btn"
diff --git a/app/views/projects/settings/operations/_error_tracking.html.haml b/app/views/projects/settings/operations/_error_tracking.html.haml
index 583fc08f375..589d3037eba 100644
--- a/app/views/projects/settings/operations/_error_tracking.html.haml
+++ b/app/views/projects/settings/operations/_error_tracking.html.haml
@@ -17,4 +17,4 @@
project: error_tracking_setting_project_json,
api_host: setting.api_host,
enabled: setting.enabled.to_json,
- token: setting.token } }
+ token: setting.token.present? ? '*' * 12 : nil } }
diff --git a/changelogs/unreleased/28336-dropdown-icon-missing-on-compare-page.yml b/changelogs/unreleased/28336-dropdown-icon-missing-on-compare-page.yml
new file mode 100644
index 00000000000..75f40d1ac8a
--- /dev/null
+++ b/changelogs/unreleased/28336-dropdown-icon-missing-on-compare-page.yml
@@ -0,0 +1,5 @@
+---
+title: Adding dropdown arrow icon and updated text alignment
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/31390-remove-pointer-cursor-from-memory-usage-chart.yml b/changelogs/unreleased/31390-remove-pointer-cursor-from-memory-usage-chart.yml
new file mode 100644
index 00000000000..0bbbcb11523
--- /dev/null
+++ b/changelogs/unreleased/31390-remove-pointer-cursor-from-memory-usage-chart.yml
@@ -0,0 +1,5 @@
+---
+title: Remove pointer cursor from MemoryUsage chart on MR widget deployment
+merge_request: 18599
+author:
+type: fixed
diff --git a/changelogs/unreleased/8558-bump-ado-image-for-modsec-secruleengine.yml b/changelogs/unreleased/8558-bump-ado-image-for-modsec-secruleengine.yml
new file mode 100644
index 00000000000..615ae1452d0
--- /dev/null
+++ b/changelogs/unreleased/8558-bump-ado-image-for-modsec-secruleengine.yml
@@ -0,0 +1,5 @@
+---
+title: Bump Auto-Deploy image to v0.3.0
+merge_request: 18809
+author:
+type: added
diff --git a/changelogs/unreleased/cluster_management_projects_updating.yml b/changelogs/unreleased/cluster_management_projects_updating.yml
new file mode 100644
index 00000000000..8f1876fc5a1
--- /dev/null
+++ b/changelogs/unreleased/cluster_management_projects_updating.yml
@@ -0,0 +1,5 @@
+---
+title: Adds ability to set management project for cluster via API
+merge_request: 18429
+author:
+type: added
diff --git a/changelogs/unreleased/id-nil-short-commit-sha.yml b/changelogs/unreleased/id-nil-short-commit-sha.yml
new file mode 100644
index 00000000000..3d925e10616
--- /dev/null
+++ b/changelogs/unreleased/id-nil-short-commit-sha.yml
@@ -0,0 +1,5 @@
+---
+title: Serialize short sha as nil if head commit is blank
+merge_request: 19014
+author:
+type: fixed
diff --git a/changelogs/unreleased/introduce-feature-flag-api-enable-disable.yml b/changelogs/unreleased/introduce-feature-flag-api-enable-disable.yml
new file mode 100644
index 00000000000..1c60b87d7b2
--- /dev/null
+++ b/changelogs/unreleased/introduce-feature-flag-api-enable-disable.yml
@@ -0,0 +1,5 @@
+---
+title: Support Enable/Disable operations in Feature Flag API
+merge_request: 18368
+author:
+type: added
diff --git a/changelogs/unreleased/ph-fixAdminGeoSidebarFlyOut.yml b/changelogs/unreleased/ph-fixAdminGeoSidebarFlyOut.yml
new file mode 100644
index 00000000000..daafd2539fd
--- /dev/null
+++ b/changelogs/unreleased/ph-fixAdminGeoSidebarFlyOut.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed admin geo collapsed sidebar fly out not showing
+merge_request: 19012
+author:
+type: fixed
diff --git a/changelogs/unreleased/security-id-fix-disclosure-of-private-repo-names.yml b/changelogs/unreleased/security-id-fix-disclosure-of-private-repo-names.yml
new file mode 100644
index 00000000000..dfd7a2d11f9
--- /dev/null
+++ b/changelogs/unreleased/security-id-fix-disclosure-of-private-repo-names.yml
@@ -0,0 +1,5 @@
+---
+title: Return 404 on LFS request if project doesn't exist
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/security-mask-sentry-token-ce.yml b/changelogs/unreleased/security-mask-sentry-token-ce.yml
new file mode 100644
index 00000000000..e9fe780a488
--- /dev/null
+++ b/changelogs/unreleased/security-mask-sentry-token-ce.yml
@@ -0,0 +1,4 @@
+---
+title: Mask sentry auth token in Error Tracking dashboard
+author:
+type: security
diff --git a/changelogs/unreleased/security-open-redirect-internalredirect.yml b/changelogs/unreleased/security-open-redirect-internalredirect.yml
new file mode 100644
index 00000000000..5ac65a4b355
--- /dev/null
+++ b/changelogs/unreleased/security-open-redirect-internalredirect.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes a Open Redirect issue in `InternalRedirect`.
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/security-wiki-rdoc-content.yml b/changelogs/unreleased/security-wiki-rdoc-content.yml
new file mode 100644
index 00000000000..f40f1abcd94
--- /dev/null
+++ b/changelogs/unreleased/security-wiki-rdoc-content.yml
@@ -0,0 +1,5 @@
+---
+title: Sanitize all wiki markup formats with GitLab sanitization pipelines
+merge_request:
+author:
+type: security
diff --git a/doc/api/group_clusters.md b/doc/api/group_clusters.md
index e878bb5fa4d..6eb2cef4097 100644
--- a/doc/api/group_clusters.md
+++ b/doc/api/group_clusters.md
@@ -53,6 +53,16 @@ Example response:
"api_url":"https://104.197.68.152",
"authorization_type":"rbac",
"ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----"
+ },
+ "management_project":
+ {
+ "id":2,
+ "description":null,
+ "name":"project2",
+ "name_with_namespace":"John Doe8 / project2",
+ "path":"project2",
+ "path_with_namespace":"namespace2/project2",
+ "created_at":"2019-10-11T02:55:54.138Z"
}
},
{
@@ -111,6 +121,16 @@ Example response:
"authorization_type":"rbac",
"ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----"
},
+ "management_project":
+ {
+ "id":2,
+ "description":null,
+ "name":"project2",
+ "name_with_namespace":"John Doe8 / project2",
+ "path":"project2",
+ "path_with_namespace":"namespace2/project2",
+ "created_at":"2019-10-11T02:55:54.138Z"
+ },
"group":
{
"id":26,
@@ -135,6 +155,7 @@ Parameters:
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
| `name` | String | yes | The name of the cluster |
| `domain` | String | no | The [base domain](../user/group/clusters/index.md#base-domain) of the cluster |
+| `management_project_id` | integer | no | The ID of the [management project](../user/clusters/management_project.md) for the cluster |
| `enabled` | Boolean | no | Determines if cluster is active or not, defaults to true |
| `managed` | Boolean | no | Determines if GitLab will manage namespaces and service accounts for this cluster, defaults to true |
| `platform_kubernetes_attributes[api_url]` | String | yes | The URL to access the Kubernetes API |
@@ -178,6 +199,7 @@ Example response:
"authorization_type":"rbac",
"ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----"
},
+ "management_project":null,
"group":
{
"id":26,
@@ -248,6 +270,16 @@ Example response:
"authorization_type":"rbac",
"ca_cert":null
},
+ "management_project":
+ {
+ "id":2,
+ "description":null,
+ "name":"project2",
+ "name_with_namespace":"John Doe8 / project2",
+ "path":"project2",
+ "path_with_namespace":"namespace2/project2",
+ "created_at":"2019-10-11T02:55:54.138Z"
+ },
"group":
{
"id":26,
diff --git a/doc/api/project_clusters.md b/doc/api/project_clusters.md
index 633ef20deb4..e733cd7ea7b 100644
--- a/doc/api/project_clusters.md
+++ b/doc/api/project_clusters.md
@@ -54,6 +54,16 @@ Example response:
"namespace":"cluster-1-namespace",
"authorization_type":"rbac",
"ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----"
+ },
+ "management_project":
+ {
+ "id":2,
+ "description":null,
+ "name":"project2",
+ "name_with_namespace":"John Doe8 / project2",
+ "path":"project2",
+ "path_with_namespace":"namespace2/project2",
+ "created_at":"2019-10-11T02:55:54.138Z"
}
},
{
@@ -113,6 +123,16 @@ Example response:
"authorization_type":"rbac",
"ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----"
},
+ "management_project":
+ {
+ "id":2,
+ "description":null,
+ "name":"project2",
+ "name_with_namespace":"John Doe8 / project2",
+ "path":"project2",
+ "path_with_namespace":"namespace2/project2",
+ "created_at":"2019-10-11T02:55:54.138Z"
+ },
"project":
{
"id":26,
@@ -205,6 +225,7 @@ Example response:
"authorization_type":"rbac",
"ca_cert":"-----BEGIN CERTIFICATE-----\r\nhFiK1L61owwDQYJKoZIhvcNAQELBQAw\r\nLzEtMCsGA1UEAxMkZDA1YzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM4ZDBj\r\nMB4XDTE4MTIyNzIwMDM1MVoXDTIzMTIyNjIxMDM1MVowLzEtMCsGA1UEAxMkZDA1\r\nYzQ1YjctNzdiMS00NDY0LThjNmEtMTQ0ZDJkZjM.......-----END CERTIFICATE-----"
},
+ "management_project":null,
"project":
{
"id":26,
@@ -253,6 +274,7 @@ Parameters:
| `cluster_id` | integer | yes | The ID of the cluster |
| `name` | String | no | The name of the cluster |
| `domain` | String | no | The [base domain](../user/project/clusters/index.md#base-domain) of the cluster |
+| `management_project_id` | integer | no | The ID of the [management project](../user/clusters/management_project.md) for the cluster |
| `platform_kubernetes_attributes[api_url]` | String | no | The URL to access the Kubernetes API |
| `platform_kubernetes_attributes[token]` | String | no | The token to authenticate against Kubernetes |
| `platform_kubernetes_attributes[ca_cert]` | String | no | TLS certificate (needed if API is using a self-signed TLS certificate |
@@ -300,6 +322,16 @@ Example response:
"authorization_type":"rbac",
"ca_cert":null
},
+ "management_project":
+ {
+ "id":2,
+ "description":null,
+ "name":"project2",
+ "name_with_namespace":"John Doe8 / project2",
+ "path":"project2",
+ "path_with_namespace":"namespace2/project2",
+ "created_at":"2019-10-11T02:55:54.138Z"
+ },
"project":
{
"id":26,
diff --git a/doc/development/documentation/site_architecture/index.md b/doc/development/documentation/site_architecture/index.md
index f5a12e9c216..bf873995e54 100644
--- a/doc/development/documentation/site_architecture/index.md
+++ b/doc/development/documentation/site_architecture/index.md
@@ -4,14 +4,16 @@ description: "Learn how GitLab's documentation website is architectured."
# Documentation site architecture
-Learn how we build and architecture [`gitlab-docs`](https://gitlab.com/gitlab-org/gitlab-docs)
-and deploy it to <https://docs.gitlab.com>.
+The [`gitlab-docs`](https://gitlab.com/gitlab-org/gitlab-docs) project hosts
+the repository which is used to generate the GitLab documentation website and
+is deployed to <https://docs.gitlab.com>. It uses the [Nanoc](http://nanoc.ws)
+static site generator.
-## Repository
+## Architecture
While the source of the documentation content is stored in GitLab's respective product
-repositories, the source that is used to build the documentation site _from that content_
-is located at <https://gitlab.com/gitlab-org/gitlab-docs>.
+repositories, the source that is used to build the documentation
+site _from that content_ is located at <https://gitlab.com/gitlab-org/gitlab-docs>.
The following diagram illustrates the relationship between the repositories
from where content is sourced, the `gitlab-docs` project, and the published output.
@@ -43,8 +45,23 @@ from where content is sourced, the `gitlab-docs` project, and the published outp
G --> L
```
-See the [README there](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/README.md)
-for detailed information.
+You will not find any GitLab docs content in the `gitlab-docs` repository.
+All documentation files are hosted in the respective repository of each
+product, and all together are pulled to generate the docs website:
+
+- [GitLab](https://gitlab.com/gitlab-org/gitlab/tree/master/doc)
+- [Omnibus GitLab](https://gitlab.com/gitlab-org/omnibus-gitlab/tree/master/doc)
+- [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-runner/tree/master/docs)
+- [GitLab Chart](https://gitlab.com/charts/gitlab/tree/master/doc)
+
+NOTE: **Note:**
+In September 2019, we [moved towards a single codebase](https://gitlab.com/gitlab-org/gitlab-ee/issues/2952),
+as such the docs for CE and EE are now identical. For historical reasons and
+in order not to break any existing links throughout the internet, we still
+maintain the CE docs (`https://docs.gitlab.com/ce/`), although it is hidden
+from the website, and is now a symlink to the EE docs. When
+[Pages supports redirects](https://gitlab.com/gitlab-org/gitlab-pages/issues/24),
+we will be able to remove this completely.
## Assets
@@ -73,28 +90,112 @@ Read through [the global navigation documentation](global_nav.md) to understand:
- How the global navigation is built.
- How to add new navigation items.
-## Deployment
+<!--
+## Helpers
-The docs site is deployed to production with GitLab Pages, and previewed in
-merge requests with Review Apps.
+TBA
+-->
-The deployment aspects will be soon transferred from the [original document](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/README.md)
-to this page.
+## Using YAML data files
-<!--
-## Repositories
+The easiest way to achieve something similar to
+[Jekyll's data files](https://jekyllrb.com/docs/datafiles/) in Nanoc is by
+using the [`@items`](https://nanoc.ws/doc/reference/variables/#items-and-layouts)
+variable.
-TBA
+The data file must be placed inside the `content/` directory and then it can
+be referenced in an ERB template.
-## Search engine
+Suppose we have the `content/_data/versions.yaml` file with the content:
-TBA
+```yaml
+versions:
+- 10.6
+- 10.5
+- 10.4
+```
-## Versions
+We can then loop over the `versions` array with something like:
-TBA
+```erb
+<% @items['/_data/versions.yaml'][:versions].each do | version | %>
-## Helpers
+<h3><%= version %></h3>
-TBA
--->
+<% end &>
+```
+
+Note that the data file must have the `yaml` extension (not `yml`) and that
+we reference the array with a symbol (`:versions`).
+
+## Bumping versions of CSS and Javascript
+
+Whenever the custom CSS and Javascript files under `content/assets/` change,
+make sure to bump their version in the frontmatter. This method guarantees that
+your changes will take effect by clearing the cache of previous files.
+
+Always use Nanoc's way of including those files, do not hardcode them in the
+layouts. For example use:
+
+```erb
+<script async type="application/javascript" src="<%= @items['/assets/javascripts/badges.*'].path %>"></script>
+
+<link rel="stylesheet" href="<%= @items['/assets/stylesheets/toc.*'].path %>">
+```
+
+The links pointing to the files should be similar to:
+
+```erb
+<%= @items['/path/to/assets/file.*'].path %>
+```
+
+Nanoc will then build and render those links correctly according with what's
+defined in [`Rules`](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/Rules).
+
+## Linking to source files
+
+A helper called [`edit_on_gitlab`](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/lib/helpers/edit_on_gitlab.rb) can be used
+to link to a page's source file. We can link to both the simple editor and the
+web IDE. Here's how you can use it in a Nanoc layout:
+
+- Default editor: `<a href="<%= edit_on_gitlab(@item, editor: :simple) %>">Simple editor</a>`
+- Web IDE: `<a href="<%= edit_on_gitlab(@item, editor: :webide) %>">Web IDE</a>`
+
+If you don't specify `editor:`, the simple one is used by default.
+
+## Algolia search engine
+
+The docs site uses [Algolia docsearch](https://community.algolia.com/docsearch/)
+for its search function. This is how it works:
+
+1. GitLab is a member of the [docsearch program](https://community.algolia.com/docsearch/#join-docsearch-program),
+ which is the free tier of [Algolia](https://www.algolia.com/).
+1. Algolia hosts a [doscsearch config](https://github.com/algolia/docsearch-configs/blob/master/configs/gitlab.json)
+ for the GitLab docs site, and we've worked together to refine it.
+1. That [config](https://community.algolia.com/docsearch/config-file.html) is
+ parsed by their [crawler](https://community.algolia.com/docsearch/crawler-overview.html)
+ every 24h and [stores](https://community.algolia.com/docsearch/inside-the-engine.html)
+ the [docsearch index](https://community.algolia.com/docsearch/how-do-we-build-an-index.html)
+ on [Algolia's servers](https://community.algolia.com/docsearch/faq.html#where-is-my-data-hosted%3F).
+1. On the docs side, we use a [docsearch layout](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/layouts/docsearch.html) which
+ is present on pretty much every page except <https://docs.gitlab.com/search/>,
+ which uses its [own layout](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/layouts/instantsearch.html). In those layouts,
+ there's a javascript snippet which initiates docsearch by using an API key
+ and an index name (`gitlab`) that are needed for Algolia to show the results.
+
+NOTE: **For GitLab employees:**
+The credentials to access the Algolia dashboard are stored in 1Password. If you
+want to receive weekly reports of the search usage, search the Google doc with
+title "Email, Slack, and GitLab Groups and Aliases", search for `docsearch`,
+and add a comment with your email to be added to the alias that gets the weekly
+reports.
+
+## Monthly release process (versions)
+
+The docs website supports versions and each month we add the latest one to the list.
+For more information, read about the [monthly release process](release_process.md).
+
+## Review Apps for documentation merge requests
+
+If you are contributing to GitLab docs read how to [create a Review App with each
+merge request](../index.md#previewing-the-changes-live).
diff --git a/doc/development/documentation/site_architecture/release_process.md b/doc/development/documentation/site_architecture/release_process.md
new file mode 100644
index 00000000000..6f723531f4c
--- /dev/null
+++ b/doc/development/documentation/site_architecture/release_process.md
@@ -0,0 +1,241 @@
+# GitLab Docs monthly release process
+
+The [`dockerfiles` directory](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/dockerfiles/)
+contains all needed Dockerfiles to build and deploy the versioned website. It
+is heavily inspired by Docker's
+[Dockerfile](https://github.com/docker/docker.github.io/blob/06ed03db13895bfe867761b6fc2ad40acf6026dd/Dockerfile).
+
+The following Dockerfiles are used.
+
+| Dockerfile | Docker image | Description |
+| ---------- | ------------ | ----------- |
+| [`Dockerfile.bootstrap`](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/dockerfiles/Dockerfile.bootstrap) | `gitlab-docs:bootstrap` | Contains all the dependencies that are needed to build the website. If the gems are updated and `Gemfile{,.lock}` changes, the image must be rebuilt. |
+| [`Dockerfile.builder.onbuild`](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/dockerfiles/Dockerfile.builder.onbuild) | `gitlab-docs:builder-onbuild` | Base image to build the docs website. It uses `ONBUILD` to perform all steps and depends on `gitlab-docs:bootstrap`. |
+| [`Dockerfile.nginx.onbuild`](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/dockerfiles/Dockerfile.nginx.onbuild) | `gitlab-docs:nginx-onbuild` | Base image to use for building documentation archives. It uses `ONBUILD` to perform all required steps to copy the archive, and relies upon its parent `Dockerfile.builder.onbuild` that is invoked when building single documentation achives (see the `Dockerfile` of each branch. |
+| [`Dockerfile.archives`](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/dockerfiles/Dockerfile.archives) | `gitlab-docs:archives` | Contains all the versions of the website in one archive. It copies all generated HTML files from every version in one location. |
+
+## How to build the images
+
+Although build images are built automatically via GitLab CI/CD, you can build
+and tag all tooling images locally:
+
+1. Make sure you have [Docker installed](https://docs.docker.com/install/).
+1. Make sure you're on the `dockerfiles/` directory of the `gitlab-docs` repo.
+1. Build the images:
+
+ ```sh
+ docker build -t registry.gitlab.com/gitlab-org/gitlab-docs:bootstrap -f Dockerfile.bootstrap ../
+ docker build -t registry.gitlab.com/gitlab-org/gitlab-docs:builder-onbuild -f Dockerfile.builder.onbuild ../
+ docker build -t registry.gitlab.com/gitlab-org/gitlab-docs:nginx-onbuild -f Dockerfile.nginx.onbuild ../
+ ```
+
+For each image, there's a manual job under the `images` stage in
+[`.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab-docs/blob/master/.gitlab-ci.yml) which can be invoked at will.
+
+## Monthly release process
+
+When a new GitLab version is released on the 22nd, we need to create the respective
+single Docker image, and update some files so that the dropdown works correctly.
+
+### 1. Add the chart version
+
+Since the charts use a different version number than all the other GitLab
+products, we need to add a
+[version mapping](https://docs.gitlab.com/charts/installation/version_mappings.html):
+
+1. Check that there is a [stable branch created](https://gitlab.com/gitlab-org/charts/gitlab/-/branches)
+ for the new chart version. If you're unsure or can't find it, drop a line in
+ the `#g_delivery` channel.
+1. Make sure you're on the root path of the `gitlab-docs` repo.
+1. Open `content/_data/chart_versions.yaml` and add the new stable branch version using the
+ version mapping. Note that only the `major.minor` version is needed.
+1. Create a new merge request and merge it.
+
+TIP: **Tip:**
+It can be handy to create the future mappings since they are pretty much known.
+In that case, when a new GitLab version is released, you don't have to repeat
+this first step.
+
+### 2. Create an image for a single version
+
+The single docs version must be created before the release merge request, but
+this needs to happen when the stable branches for all products have been created.
+
+1. Make sure you're on the root path of the `gitlab-docs` repo.
+1. Run the raketask to create the single version:
+
+ ```sh
+ ./bin/rake "release:single[12.0]"
+ ```
+
+ A new `Dockerfile.12.0` should have been created and committed to a new branch.
+
+1. Push the newly created branch, but **don't create a merge request**.
+ Once you push, the `image:docker-singe` job will create a new Docker image
+ tagged with the branch name you created in the first step. In the end, the
+ image will be uploaded in the [Container Registry](https://gitlab.com/gitlab-org/gitlab-docs/container_registry)
+ and it will be listed under the
+ [`registry` environment folder](https://gitlab.com/gitlab-org/gitlab-docs/environments/folders/registry).
+
+Optionally, you can test locally by building the image and running it:
+
+```sh
+docker build -t docs:12.0 -f Dockerfile.12.0 .
+docker run -it --rm -p 4000:4000 docs:12.0
+```
+
+Visit `http://localhost:4000/12.0/` to see if everything works correctly.
+
+### 3. Create the release merge request
+
+Now it's time to create the monthly release merge request that adds the new
+version and rotates the old one:
+
+1. Make sure you're on the root path of the `gitlab-docs` repo.
+1. Create a branch `release-X-Y`:
+
+ ```sh
+ git checkout -b release-12-0
+ ```
+
+1. **Rotate the online and offline versions:**
+
+ At any given time, there are 4 browsable online versions: one pulled from
+ the upstream master branches (docs for GitLab.com) and the three latest
+ stable versions.
+
+ Edit `content/_data/versions.yaml` and rotate the versions to reflect the
+ new changes:
+
+ - `online`: The 3 latest stable versions.
+ - `offline`: All the previous versions offered as an offline archive.
+
+1. **Add the new offline version in the 404 page redirect script:**
+
+ Since we're deprecating the oldest version each month, we need to redirect
+ those URLs in order not to create [404 entries](https://gitlab.com/gitlab-org/gitlab-docs/issues/221).
+ There's a temporary hack for now:
+
+ 1. Edit `content/404.html`, making sure all offline versions under
+ `content/_data/versions.yaml` are in the Javascript snippet at the end of
+ the document.
+
+1. **Update the `:latest` and `:archives` Docker images:**
+
+ The following two Dockerfiles need to be updated:
+
+ 1. `dockerfiles/Dockerfile.archives` - Add the latest version at the top of
+ the list.
+ 1. `Dockerfile.master` - Rotate the versions (oldest gets removed and latest
+ is added at the top of the list).
+
+1. In the end, there should be four files in total that have changed.
+ Commit and push to create the merge request using the "Release" template:
+
+ ```sh
+ git add content/ Dockerfile.master dockerfiles/Dockerfile.archives
+ git commit -m "Release 12.0"
+ git push origin release-12-0
+ ```
+
+### 4. Update the dropdown for all online versions
+
+The versions dropdown is in a way "hardcoded". When the site is built, it looks
+at the contents of `content/_data/versions.yaml` and based on that, the dropdown
+is populated. So, older branches will have different content, which means the
+dropdown will be one or more releases behind. Remember that the new changes of
+the dropdown are included in the unmerged `release-X-Y` branch.
+
+The content of `content/_data/versions.yaml` needs to change for all online
+versions:
+
+1. Before creating the merge request, [disable the scheduled pipeline](https://gitlab.com/gitlab-org/gitlab-docs/pipeline_schedules/228/edit)
+ by unchecking the "Active" option. Since all steps must run in sequence, we need
+ to do this to avoid race conditions in the event some previous versions are
+ updated before the release merge request is merged.
+1. Run the raketask that will create all the respective merge requests needed to
+ update the dropdowns and will be set to automatically be merged when their
+ pipelines succeed. The `release-X-Y` branch needs to be present locally,
+ otherwise the raketask will fail:
+
+ ```sh
+ ./bin/rake release:dropdowns
+ ```
+
+Once all are merged, proceed to the following and final step.
+
+TIP: **Tip:**
+In case a pipeline fails, see [troubleshooting](#troubleshooting).
+
+### 5. Merge the release merge request
+
+The dropdown merge requests should have now been merged into their respective
+version (stable branch), which will trigger another pipeline. At this point,
+you need to only babysit the pipelines and make sure they don't fail:
+
+1. Check the [pipelines page](https://gitlab.com/gitlab-org/gitlab-docs/pipelines)
+ and make sure all stable branches have green pipelines.
+1. After all the pipelines of the online versions succeed, merge the release merge request.
+1. Finally, re-activate the [scheduled pipeline](https://gitlab.com/gitlab-org/gitlab-docs/pipeline_schedules/228/edit),
+ save it, and hit the play button to get it started.
+
+Once the scheduled pipeline succeeds, the docs site will be deployed with all
+new versions online.
+
+## Update an old Docker image with new upstream docs content
+
+If there are any changes to any of the stable branches of the products that are
+not included in the single Docker image, just
+[rerun the pipeline](https://gitlab.com/gitlab-org/gitlab-docs/pipelines/new)
+for the version in question.
+
+## Porting new website changes to old versions
+
+CAUTION: **Warning:**
+Porting changes to older branches can have unintended effects as we're constantly
+changing the backend of the website. Use only when you know what you're doing
+and make sure to test locally.
+
+The website will keep changing and being improved. In order to consolidate
+those changes to the stable branches, we'd need to pick certain changes
+from time to time.
+
+If this is not possible or there are many changes, merge master into them:
+
+```sh
+git branch 12.0
+git fetch origin master
+git merge origin/master
+```
+
+## Troubleshooting
+
+Releasing a new version is a long process that involves many moving parts.
+
+### `test_internal_links_and_anchors` failing on dropdown merge requests
+
+When [updating the dropdown for the stable versions](#4-update-the-dropdown-for-all-online-versions),
+there may be cases where some links might fail. The process of how the
+dropdown MRs are created have a caveat, and that is that the tests run by
+pulling the master branches of all products, instead of the respective stable
+ones.
+
+In a real world scenario, the [Update 12.2 dropdown to match that of 12.4](https://gitlab.com/gitlab-org/gitlab-docs/merge_requests/604)
+merge request failed because of the [`test_internal_links_and_anchors` test](https://gitlab.com/gitlab-org/gitlab-docs/-/jobs/328042431).
+
+This happened because there has been a rename of a product (`gitlab-monitor` to `gitlab-exporter`)
+and the old name was still referenced in the 12.2 docs. If the respective stable
+branches for 12.2 were used, this wouldn't have failed, but as we can see from
+the [`compile_dev` job](https://gitlab.com/gitlab-org/gitlab-docs/-/jobs/328042427),
+the `master` branches were pulled.
+
+To fix this, you need to [re-run the pipeline](https://gitlab.com/gitlab-org/gitlab-docs/pipelines/new)
+for the `update-12-2-for-release-12-4` branch, by including the following environment variables:
+
+- `BRANCH_CE` set to `12-2-stable`
+- `BRANCH_EE` set to `12-2-stable-ee`
+- `BRANCH_OMNIBUS` set to `12-2-stable`
+- `BRANCH_RUNNER` set to `12-2-stable`
+- `BRANCH_CHARTS` set to `2-2-stable`
+
+This should make the MR pass.
diff --git a/doc/development/testing_guide/review_apps.md b/doc/development/testing_guide/review_apps.md
index 3dd403f148e..ecfcbc731e1 100644
--- a/doc/development/testing_guide/review_apps.md
+++ b/doc/development/testing_guide/review_apps.md
@@ -189,10 +189,10 @@ that the `review-apps-ce/ee` cluster is unhealthy. Leading indicators may be hea
The following items may help diagnose this:
-- [Instance group CPU Utilization in GCP](https://console.cloud.google.com/compute/instanceGroups/details/us-central1-b/gke-review-apps-ee-preemp-n1-standard-8affc0f5-grp?project=gitlab-review-apps&tab=monitoring&graph=GCE_CPU&duration=P30D) - helpful to identify if nodes are problematic or the entire cluster is trending towards unhealthy
-- [Instance Group size in GCP](https://console.cloud.google.com/compute/instanceGroups/details/us-central1-b/gke-review-apps-ee-preemp-n1-standard-8affc0f5-grp?project=gitlab-review-apps&tab=monitoring&graph=GCE_SIZE&duration=P30D) - aids in identifying load spikes on the cluster. Kubernetes will add nodes up to 220 based on total resource requests.
-- `kubectl top nodes --sort-by=cpu` - can identify if node spikes are common or load on specific nodes which may get rebalanced by the Kubernetes scheduler.
-- `kubectl top pods --sort-by=cpu` -
+- [Review Apps Health dashboard](https://app.google.stackdriver.com/dashboards/6798952013815386466?project=gitlab-review-apps&timeDomain=1d)
+ - Aids in identifying load spikes on the cluster, and if nodes are problematic or the entire cluster is trending towards unhealthy.
+- `kubectl top nodes | sort --key 3 --numeric` - can identify if node spikes are common or load on specific nodes which may get rebalanced by the Kubernetes scheduler.
+- `kubectl top pods | sort --key 2 --numeric` -
- [K9s] - K9s is a powerful command line dashboard which allows you to filter by labels. This can help identify trends with apps exceeding the [review-app resource requests](https://gitlab.com/gitlab-org/gitlab/blob/master/scripts/review_apps/base-config.yaml). Kubernetes will schedule pods to nodes based on resource requests and allow for CPU usage up to the limits.
- In K9s you can sort or add filters by typing the `/` character
- `-lrelease=<review-app-slug>` - filters down to all pods for a release. This aids in determining what is having issues in a single deployment
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index a1373639a87..270c6255350 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -924,6 +924,7 @@ applications.
| `AUTO_DEVOPS_CHART_REPOSITORY_NAME` | From GitLab 11.11, used to set the name of the Helm repository. Defaults to `gitlab`. |
| `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME` | From GitLab 11.11, used to set a username to connect to the Helm repository. Defaults to no credentials. Also set `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD`. |
| `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD` | From GitLab 11.11, used to set a password to connect to the Helm repository. Defaults to no credentials. Also set `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME`. |
+| `AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE` | From GitLab 12.5, used in combination with [Modsecurity feature flag](../../user/clusters/applications.md#web-application-firewall-modsecurity) to toggle [Modsecurity's `SecRuleEngine`](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual-(v2.x)#SecRuleEngine) behavior. Defaults to `DetectionOnly`. |
| `BUILDPACK_URL` | Buildpack's full URL. Can point to either Git repositories or a tarball URL. For Git repositories, it is possible to point to a specific `ref`. For example `https://github.com/heroku/heroku-buildpack-ruby.git#v142`. |
| `CANARY_ENABLED` | From GitLab 11.0, used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments-premium). |
| `CANARY_PRODUCTION_REPLICAS` | Number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md) in the production environment. Takes precedence over `CANARY_REPLICAS`. Defaults to 1. |
diff --git a/doc/user/clusters/management_project.md b/doc/user/clusters/management_project.md
index 37308ad7175..2c3c6850ea5 100644
--- a/doc/user/clusters/management_project.md
+++ b/doc/user/clusters/management_project.md
@@ -4,7 +4,7 @@ CAUTION: **Warning:**
This is an _alpha_ feature, and it is subject to change at any time without
prior notice.
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/merge_requests/17866) in GitLab 12.4
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/32810) in GitLab 12.5
A project can be designated as the management project for a cluster.
A management project can be used to run deployment jobs with
@@ -22,6 +22,14 @@ This can be useful for:
Only the management project will receive `cluster-admin` privileges. All
other projects will continue to receive [namespace scoped `edit` level privileges](../project/clusters/index.md#rbac-cluster-resources).
+Management projects are restricted to the following:
+
+- For project-level clusters, the management project must in the same
+ namespace (or descendants) as the cluster's project.
+- For group-level clusters, the management project must in the same
+ group (or descendants) as as the cluster's group.
+- For instance-level clusters, there are no such restrictions.
+
## Usage
### Selecting a cluster management project
@@ -87,9 +95,9 @@ configure production cluster:
name: production
```
-## Disabling this feature
+## Enabling this feature
-This feature is enabled by default. To disable this feature, disable the
+This feature is disabled by default. To enable this feature, enable the
feature flag `:cluster_management_project`.
To check if the feature flag is enabled on your GitLab instance,
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 933a571c17d..de12695af37 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -1791,6 +1791,7 @@ module API
expose :user, using: Entities::UserBasic
expose :platform_kubernetes, using: Entities::Platform::Kubernetes
expose :provider_gcp, using: Entities::Provider::Gcp
+ expose :management_project, using: Entities::ProjectIdentity
end
class ClusterProject < Cluster
diff --git a/lib/api/group_clusters.rb b/lib/api/group_clusters.rb
index a70ac63cc6e..abfe10b7fa1 100644
--- a/lib/api/group_clusters.rb
+++ b/lib/api/group_clusters.rb
@@ -84,6 +84,7 @@ module API
requires :cluster_id, type: Integer, desc: 'The cluster ID'
optional :name, type: String, desc: 'Cluster name'
optional :domain, type: String, desc: 'Cluster base domain'
+ optional :management_project_id, type: Integer, desc: 'The ID of the management project'
optional :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do
optional :api_url, type: String, desc: 'URL to access the Kubernetes API'
optional :token, type: String, desc: 'Token to authenticate against Kubernetes'
diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb
index 4c575381d30..dfac777e4a1 100644
--- a/lib/api/helpers/internal_helpers.rb
+++ b/lib/api/helpers/internal_helpers.rb
@@ -140,7 +140,8 @@ module API
{
repository: repository.gitaly_repository,
address: Gitlab::GitalyClient.address(project.repository_storage),
- token: Gitlab::GitalyClient.token(project.repository_storage)
+ token: Gitlab::GitalyClient.token(project.repository_storage),
+ features: Feature::Gitaly.server_feature_flags
}
end
end
diff --git a/lib/api/project_clusters.rb b/lib/api/project_clusters.rb
index 45c800d7d1e..8e35914f48a 100644
--- a/lib/api/project_clusters.rb
+++ b/lib/api/project_clusters.rb
@@ -88,6 +88,7 @@ module API
requires :cluster_id, type: Integer, desc: 'The cluster ID'
optional :name, type: String, desc: 'Cluster name'
optional :domain, type: String, desc: 'Cluster base domain'
+ optional :management_project_id, type: Integer, desc: 'The ID of the management project'
optional :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do
optional :api_url, type: String, desc: 'URL to access the Kubernetes API'
optional :token, type: String, desc: 'Token to authenticate against Kubernetes'
diff --git a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
index a8ec2d4781d..6de7aace8db 100644
--- a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
@@ -1,5 +1,5 @@
.auto-deploy:
- image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v0.1.0"
+ image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v0.3.0"
review:
extends: .auto-deploy
diff --git a/lib/gitlab/experimentation.rb b/lib/gitlab/experimentation.rb
index 8b315ae5606..2ccc8a367aa 100644
--- a/lib/gitlab/experimentation.rb
+++ b/lib/gitlab/experimentation.rb
@@ -43,7 +43,7 @@ module Gitlab
end
def experiment_enabled?(experiment_key)
- Experimentation.enabled_for_user?(experiment_key, experimentation_subject_index)
+ Experimentation.enabled_for_user?(experiment_key, experimentation_subject_index) || forced_enabled?(experiment_key)
end
def track_experiment_event(experiment_key, action)
@@ -94,6 +94,10 @@ module Gitlab
experiment_enabled?(experiment_key) ? 'experimental_group' : 'control_group'
end
+
+ def forced_enabled?(experiment_key)
+ params.has_key?(:force_experiment) && params[:force_experiment] == experiment_key.to_s
+ end
end
class << self
diff --git a/lib/gitlab/other_markup.rb b/lib/gitlab/other_markup.rb
index bc467486eee..0dd6b8a809c 100644
--- a/lib/gitlab/other_markup.rb
+++ b/lib/gitlab/other_markup.rb
@@ -10,7 +10,7 @@ module Gitlab
def self.render(file_name, input, context)
html = GitHub::Markup.render(file_name, input)
.force_encoding(input.encoding)
- context[:pipeline] = :markup
+ context[:pipeline] ||= :markup
html = Banzai.render(html, context)
diff --git a/lib/quality/kubernetes_client.rb b/lib/quality/kubernetes_client.rb
index 190b48ba7cb..0bd16935045 100644
--- a/lib/quality/kubernetes_client.rb
+++ b/lib/quality/kubernetes_client.rb
@@ -13,6 +13,15 @@ module Quality
end
def cleanup(release_name:)
+ selector = case release_name
+ when String
+ %(-l release="#{release_name}")
+ when Array
+ %(-l 'release in (#{release_name.join(', ')})')
+ else
+ raise ArgumentError, 'release_name must be a string or an array'
+ end
+
command = [
%(--namespace "#{namespace}"),
'delete',
@@ -20,7 +29,7 @@ module Quality
'--now',
'--ignore-not-found',
'--include-uninitialized',
- %(-l release="#{release_name}")
+ selector
]
run_command(command)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 1c8669deef3..323efe2e6eb 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -12701,6 +12701,9 @@ msgstr ""
msgid "Project details"
msgstr ""
+msgid "Project does not exist or you don't have permission to perform this action"
+msgstr ""
+
msgid "Project export could not be deleted."
msgstr ""
diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb
index dcf145c9882..095233b54f0 100644
--- a/qa/qa/resource/user.rb
+++ b/qa/qa/resource/user.rb
@@ -93,6 +93,7 @@ module QA
if Runtime::Env.signup_disabled?
self.fabricate_via_api! do |user|
user.username = username
+ user.password = password
end
else
self.fabricate!
diff --git a/scripts/review_apps/automated_cleanup.rb b/scripts/review_apps/automated_cleanup.rb
index 2b4f1b9fe0b..731903f823b 100755
--- a/scripts/review_apps/automated_cleanup.rb
+++ b/scripts/review_apps/automated_cleanup.rb
@@ -60,6 +60,8 @@ class AutomatedCleanup
stop_threshold = threshold_time(days: days_for_stop)
deployments_look_back_threshold = threshold_time(days: days_for_delete * 5)
+ releases_to_delete = []
+
gitlab.deployments(project_path, per_page: DEPLOYMENTS_PER_PAGE, sort: 'desc').auto_paginate do |deployment|
break if Time.parse(deployment.created_at) < deployments_look_back_threshold
@@ -75,7 +77,7 @@ class AutomatedCleanup
if deployed_at < delete_threshold
delete_environment(environment, deployment)
release = Quality::HelmClient::Release.new(environment.slug, 1, deployed_at.to_s, nil, nil, review_apps_namespace)
- delete_helm_release(release)
+ releases_to_delete << release
elsif deployed_at < stop_threshold
stop_environment(environment, deployment)
else
@@ -84,6 +86,8 @@ class AutomatedCleanup
checked_environments << environment.slug
end
+
+ delete_helm_releases(releases_to_delete)
end
def perform_helm_releases_cleanup!(days:)
@@ -91,16 +95,20 @@ class AutomatedCleanup
threshold_day = threshold_time(days: days)
+ releases_to_delete = []
+
helm_releases.each do |release|
# Prevents deleting `dns-gitlab-review-app` releases or other unrelated releases
next unless release.name.start_with?('review-')
if release.status == 'FAILED' || release.last_update < threshold_day
- delete_helm_release(release)
+ releases_to_delete << release
else
print_release_state(subject: 'Release', release_name: release.name, release_date: release.last_update, action: 'leaving')
end
end
+
+ delete_helm_releases(releases_to_delete)
end
private
@@ -121,10 +129,17 @@ class AutomatedCleanup
helm.releases(args: args)
end
- def delete_helm_release(release)
- print_release_state(subject: 'Release', release_name: release.name, release_status: release.status, release_date: release.last_update, action: 'cleaning')
- helm.delete(release_name: release.name)
- kubernetes.cleanup(release_name: release.name)
+ def delete_helm_releases(releases)
+ return if releases.empty?
+
+ releases.each do |release|
+ print_release_state(subject: 'Release', release_name: release.name, release_status: release.status, release_date: release.last_update, action: 'cleaning')
+ end
+
+ releases_names = releases.map(&:name)
+ helm.delete(release_name: releases_names)
+ kubernetes.cleanup(release_name: releases_names)
+
rescue Quality::HelmClient::CommandFailedError => ex
raise ex unless ignore_exception?(ex.message, IGNORED_HELM_ERRORS)
diff --git a/scripts/sync-stable-branch.sh b/scripts/sync-stable-branch.sh
new file mode 100644
index 00000000000..fc62453d743
--- /dev/null
+++ b/scripts/sync-stable-branch.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+
+# This script triggers a merge train job to sync an EE stable branch to its
+# corresponding CE stable branch.
+
+set -e
+
+if [[ "$MERGE_TRAIN_TRIGGER_TOKEN" == '' ]]
+then
+ echo 'The variable MERGE_TRAIN_TRIGGER_TOKEN must be set to a non-empy value'
+ exit 1
+fi
+
+if [[ "$MERGE_TRAIN_TRIGGER_URL" == '' ]]
+then
+ echo 'The variable MERGE_TRAIN_TRIGGER_URL must be set to a non-empy value'
+ exit 1
+fi
+
+if [[ "$CI_COMMIT_REF_NAME" == '' ]]
+then
+ echo 'The variable CI_COMMIT_REF_NAME must be set to a non-empy value'
+ exit 1
+fi
+
+curl -X POST \
+ -F token="$MERGE_TRAIN_TRIGGER_TOKEN" \
+ -F ref=master \
+ -F "variables[MERGE_FOSS]=1" \
+ -F "variables[SOURCE_BRANCH]=$CI_COMMIT_REF_NAME" \
+ -F "variables[TARGET_BRANCH]=${CI_COMMIT_REF_NAME/-ee/}" \
+ "$MERGE_TRAIN_TRIGGER_URL"
diff --git a/scripts/trigger-build b/scripts/trigger-build
index badbb562021..74c1df258c0 100755
--- a/scripts/trigger-build
+++ b/scripts/trigger-build
@@ -17,7 +17,7 @@ module Trigger
end
class Base
- def invoke!(post_comment: false)
+ def invoke!(post_comment: false, downstream_job_name: nil)
pipeline = Gitlab.run_trigger(
downstream_project_path,
trigger_token,
@@ -28,7 +28,18 @@ module Trigger
puts "Waiting for downstream pipeline status"
Trigger::CommitComment.post!(pipeline, access_token) if post_comment
- Trigger::Pipeline.new(downstream_project_path, pipeline.id, access_token)
+ downstream_job =
+ if downstream_job_name
+ Gitlab.pipeline_jobs(downstream_project_path, pipeline.id).auto_paginate.find do |potential_job|
+ potential_job.name == downstream_job_name
+ end
+ end
+
+ if downstream_job
+ Trigger::Job.new(downstream_project_path, downstream_job.id, access_token)
+ else
+ Trigger::Pipeline.new(downstream_project_path, pipeline.id, access_token)
+ end
end
private
@@ -187,6 +198,14 @@ module Trigger
attr_reader :project, :id, :api_token
+ def self.unscoped_class_name
+ name.split('::').last
+ end
+
+ def self.gitlab_api_method_name
+ unscoped_class_name.downcase
+ end
+
def initialize(project, id, api_token)
@project = project
@id = id
@@ -199,17 +218,17 @@ module Trigger
def wait!
loop do
- raise "Pipeline timed out after waiting for #{duration} minutes!" if timeout?
+ raise "#{self.class.unscoped_class_name} timed out after waiting for #{duration} minutes!" if timeout?
case status
when :created, :pending, :running
print "."
sleep INTERVAL
when :success
- puts "Pipeline succeeded in #{duration} minutes!"
+ puts "#{self.class.unscoped_class_name} succeeded in #{duration} minutes!"
break
else
- raise "Pipeline did not succeed!"
+ raise "#{self.class.unscoped_class_name} did not succeed!"
end
STDOUT.flush
@@ -225,7 +244,7 @@ module Trigger
end
def status
- Gitlab.pipeline(project, id).status.to_sym
+ Gitlab.public_send(self.class.gitlab_api_method_name, project, id).status.to_sym # rubocop:disable GitlabSecurity/PublicSend
rescue Gitlab::Error::Error => error
puts "Ignoring the following error: #{error}"
# Ignore GitLab API hiccups. If GitLab is really down, we'll hit the job
@@ -233,11 +252,13 @@ module Trigger
:running
end
end
+
+ Job = Class.new(Pipeline)
end
case ARGV[0]
when 'omnibus'
- Trigger::Omnibus.new.invoke!(post_comment: true).wait!
+ Trigger::Omnibus.new.invoke!(post_comment: true, downstream_job_name: 'Trigger:qa-test').wait!
when 'cng'
Trigger::CNG.new.invoke!.wait!
else
diff --git a/spec/controllers/concerns/internal_redirect_spec.rb b/spec/controllers/concerns/internal_redirect_spec.rb
index da68c8c8697..e5e50cfd55e 100644
--- a/spec/controllers/concerns/internal_redirect_spec.rb
+++ b/spec/controllers/concerns/internal_redirect_spec.rb
@@ -19,7 +19,8 @@ describe InternalRedirect do
[
'Hello world',
'//example.com/hello/world',
- 'https://example.com/hello/world'
+ 'https://example.com/hello/world',
+ "not-starting-with-a-slash\n/starting/with/slash"
]
end
diff --git a/spec/controllers/concerns/lfs_request_spec.rb b/spec/controllers/concerns/lfs_request_spec.rb
index cb8c0b8f71c..823b9a50434 100644
--- a/spec/controllers/concerns/lfs_request_spec.rb
+++ b/spec/controllers/concerns/lfs_request_spec.rb
@@ -16,13 +16,17 @@ describe LfsRequest do
end
def project
- @project ||= Project.find(params[:id])
+ @project ||= Project.find_by(id: params[:id])
end
def download_request?
true
end
+ def upload_request?
+ false
+ end
+
def ci?
false
end
@@ -49,4 +53,41 @@ describe LfsRequest do
expect(assigns(:storage_project)).to eq(project)
end
end
+
+ context 'user is authenticated without access to lfs' do
+ before do
+ allow(controller).to receive(:authenticate_user)
+ allow(controller).to receive(:authentication_result) do
+ Gitlab::Auth::Result.new
+ end
+ end
+
+ context 'with access to the project' do
+ it 'returns 403' do
+ get :show, params: { id: project.id }
+
+ expect(response.status).to eq(403)
+ end
+ end
+
+ context 'without access to the project' do
+ context 'project does not exist' do
+ it 'returns 404' do
+ get :show, params: { id: 'does not exist' }
+
+ expect(response.status).to eq(404)
+ end
+ end
+
+ context 'project is private' do
+ let(:project) { create(:project, :private) }
+
+ it 'returns 404' do
+ get :show, params: { id: project.id }
+
+ expect(response.status).to eq(404)
+ end
+ end
+ end
+ end
end
diff --git a/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap b/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
index 08173f4f0c4..706c26403c0 100644
--- a/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
+++ b/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
@@ -62,19 +62,23 @@ exports[`Repository last commit component renders commit widget 1`] = `
>
<!---->
- <gllink-stub
- class="js-commit-pipeline"
- data-original-title="Commit: failed"
- href="https://test.com/pipeline"
- title=""
+ <div
+ class="ci-status-link"
>
- <ciicon-stub
- aria-label="Commit: failed"
- cssclasses=""
- size="24"
- status="[object Object]"
- />
- </gllink-stub>
+ <gllink-stub
+ class="js-commit-pipeline"
+ data-original-title="Commit: failed"
+ href="https://test.com/pipeline"
+ title=""
+ >
+ <ciicon-stub
+ aria-label="Commit: failed"
+ cssclasses=""
+ size="24"
+ status="[object Object]"
+ />
+ </gllink-stub>
+ </div>
<div
class="commit-sha-group d-flex"
@@ -165,19 +169,23 @@ exports[`Repository last commit component renders the signature HTML as returned
</button>
</div>
- <gllink-stub
- class="js-commit-pipeline"
- data-original-title="Commit: failed"
- href="https://test.com/pipeline"
- title=""
+ <div
+ class="ci-status-link"
>
- <ciicon-stub
- aria-label="Commit: failed"
- cssclasses=""
- size="24"
- status="[object Object]"
- />
- </gllink-stub>
+ <gllink-stub
+ class="js-commit-pipeline"
+ data-original-title="Commit: failed"
+ href="https://test.com/pipeline"
+ title=""
+ >
+ <ciicon-stub
+ aria-label="Commit: failed"
+ cssclasses=""
+ size="24"
+ status="[object Object]"
+ />
+ </gllink-stub>
+ </div>
<div
class="commit-sha-group d-flex"
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index 364f215420a..32851249b2e 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -3,18 +3,18 @@
require 'spec_helper'
describe MarkupHelper do
- let!(:project) { create(:project, :repository) }
-
- let(:user) { create(:user, username: 'gfm') }
- let(:commit) { project.commit }
- let(:issue) { create(:issue, project: project) }
- let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
- let(:snippet) { create(:project_snippet, project: project) }
-
- before do
- # Ensure the generated reference links aren't redacted
+ set(:project) { create(:project, :repository) }
+ set(:user) do
+ user = create(:user, username: 'gfm')
project.add_maintainer(user)
+ user
+ end
+ set(:issue) { create(:issue, project: project) }
+ set(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
+ set(:snippet) { create(:project_snippet, project: project) }
+ let(:commit) { project.commit }
+ before do
# Helper expects a @project instance variable
helper.instance_variable_set(:@project, project)
@@ -44,8 +44,8 @@ describe MarkupHelper do
describe "override default project" do
let(:actual) { issue.to_reference }
- let(:second_project) { create(:project, :public) }
- let(:second_issue) { create(:issue, project: second_project) }
+ set(:second_project) { create(:project, :public) }
+ set(:second_issue) { create(:issue, project: second_project) }
it 'links to the issue' do
expected = urls.project_issue_path(second_project, second_issue)
@@ -55,7 +55,7 @@ describe MarkupHelper do
describe 'uploads' do
let(:text) { "![ImageTest](/uploads/test.png)" }
- let(:group) { create(:group) }
+ set(:group) { create(:group) }
subject { helper.markdown(text) }
@@ -77,7 +77,7 @@ describe MarkupHelper do
end
describe "with a group in the context" do
- let(:project_in_group) { create(:project, group: group) }
+ set(:project_in_group) { create(:project, group: group) }
before do
helper.instance_variable_set(:@group, group)
@@ -235,38 +235,48 @@ describe MarkupHelper do
end
describe '#render_wiki_content' do
+ let(:wiki) { double('WikiPage', path: "file.#{extension}") }
+ let(:context) do
+ {
+ pipeline: :wiki, project: project, project_wiki: wiki,
+ page_slug: 'nested/page', issuable_state_filter_enabled: true
+ }
+ end
+
before do
- @wiki = double('WikiPage')
- allow(@wiki).to receive(:content).and_return('wiki content')
- allow(@wiki).to receive(:slug).and_return('nested/page')
- helper.instance_variable_set(:@project_wiki, @wiki)
+ expect(wiki).to receive(:content).and_return('wiki content')
+ expect(wiki).to receive(:slug).and_return('nested/page')
+ helper.instance_variable_set(:@project_wiki, wiki)
end
- it "uses Wiki pipeline for markdown files" do
- allow(@wiki).to receive(:format).and_return(:markdown)
+ context 'when file is Markdown' do
+ let(:extension) { 'md' }
- expect(helper).to receive(:markdown_unsafe).with('wiki content',
- pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page",
- issuable_state_filter_enabled: true)
+ it 'renders using #markdown_unsafe helper method' do
+ expect(helper).to receive(:markdown_unsafe).with('wiki content', context)
- helper.render_wiki_content(@wiki)
+ helper.render_wiki_content(wiki)
+ end
end
- it "uses Asciidoctor for asciidoc files" do
- allow(@wiki).to receive(:format).and_return(:asciidoc)
+ context 'when file is Asciidoc' do
+ let(:extension) { 'adoc' }
- expect(helper).to receive(:asciidoc_unsafe).with('wiki content')
+ it 'renders using Gitlab::Asciidoc' do
+ expect(Gitlab::Asciidoc).to receive(:render)
- helper.render_wiki_content(@wiki)
+ helper.render_wiki_content(wiki)
+ end
end
- it "uses the Gollum renderer for all other file types" do
- allow(@wiki).to receive(:format).and_return(:rdoc)
- formatted_content_stub = double('formatted_content')
- expect(formatted_content_stub).to receive(:html_safe)
- allow(@wiki).to receive(:formatted_content).and_return(formatted_content_stub)
+ context 'any other format' do
+ let(:extension) { 'foo' }
- helper.render_wiki_content(@wiki)
+ it 'renders all other formats using Gitlab::OtherMarkup' do
+ expect(Gitlab::OtherMarkup).to receive(:render)
+
+ helper.render_wiki_content(wiki)
+ end
end
end
diff --git a/spec/lib/gitlab/experimentation_spec.rb b/spec/lib/gitlab/experimentation_spec.rb
index a7d3f628741..725c23e913c 100644
--- a/spec/lib/gitlab/experimentation_spec.rb
+++ b/spec/lib/gitlab/experimentation_spec.rb
@@ -71,6 +71,24 @@ describe Gitlab::Experimentation do
controller.experiment_enabled?(:test_experiment)
end
end
+
+ describe 'URL parameter to force enable experiment' do
+ context 'is not present' do
+ it 'returns false' do
+ get :index, params: { force_experiment: :test_experiment2 }
+
+ expect(controller.experiment_enabled?(:test_experiment)).to be_falsey
+ end
+ end
+
+ context 'is present' do
+ it 'returns true' do
+ get :index, params: { force_experiment: :test_experiment }
+
+ expect(controller.experiment_enabled?(:test_experiment)).to be_truthy
+ end
+ end
+ end
end
describe '#track_experiment_event' do
diff --git a/spec/lib/quality/helm_client_spec.rb b/spec/lib/quality/helm_client_spec.rb
index 7abb9688d5a..da5ba4c4d99 100644
--- a/spec/lib/quality/helm_client_spec.rb
+++ b/spec/lib/quality/helm_client_spec.rb
@@ -107,5 +107,25 @@ RSpec.describe Quality::HelmClient do
expect(subject.delete(release_name: release_name)).to eq('')
end
+
+ context 'with multiple release names' do
+ let(:release_name) { ['my-release', 'my-release-2'] }
+
+ it 'raises an error if the Helm command fails' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(helm delete --tiller-namespace "#{namespace}" --purge #{release_name.join(' ')})])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
+
+ expect { subject.delete(release_name: release_name) }.to raise_error(described_class::CommandFailedError)
+ end
+
+ it 'calls helm delete with multiple release names' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(helm delete --tiller-namespace "#{namespace}" --purge #{release_name.join(' ')})])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
+
+ expect(subject.delete(release_name: release_name)).to eq('')
+ end
+ end
end
end
diff --git a/spec/lib/quality/kubernetes_client_spec.rb b/spec/lib/quality/kubernetes_client_spec.rb
index 4e77dcc97e6..a42f6151a5e 100644
--- a/spec/lib/quality/kubernetes_client_spec.rb
+++ b/spec/lib/quality/kubernetes_client_spec.rb
@@ -29,5 +29,30 @@ RSpec.describe Quality::KubernetesClient do
# We're not verifying the output here, just silencing it
expect { subject.cleanup(release_name: release_name) }.to output.to_stdout
end
+
+ context 'with multiple releases' do
+ let(:release_name) { ['my-release', 'my-release-2'] }
+
+ it 'raises an error if the Kubernetes command fails' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(kubectl --namespace "#{namespace}" delete ) \
+ 'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa ' \
+ "--now --ignore-not-found --include-uninitialized -l 'release in (#{release_name.join(', ')})'"])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false)))
+
+ expect { subject.cleanup(release_name: release_name) }.to raise_error(described_class::CommandFailedError)
+ end
+
+ it 'calls kubectl with the correct arguments' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(kubectl --namespace "#{namespace}" delete ) \
+ 'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa ' \
+ "--now --ignore-not-found --include-uninitialized -l 'release in (#{release_name.join(', ')})'"])
+ .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true)))
+
+ # We're not verifying the output here, just silencing it
+ expect { subject.cleanup(release_name: release_name) }.to output.to_stdout
+ end
+ end
end
end
diff --git a/spec/migrations/schedule_fix_gitlab_com_pages_access_level_spec.rb b/spec/migrations/schedule_fix_gitlab_com_pages_access_level_spec.rb
index 88e5c101d32..1ddb468f6b7 100644
--- a/spec/migrations/schedule_fix_gitlab_com_pages_access_level_spec.rb
+++ b/spec/migrations/schedule_fix_gitlab_com_pages_access_level_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20191017045817_schedule_fix_gitlab_com_pages_access_level.rb')
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index 66b65d8b6d8..96d81f4cc49 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
@@ -63,7 +65,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let(:data_store) { :redis }
before do
- build_trace_chunk.send(:unsafe_set_data!, 'Sample data in redis')
+ build_trace_chunk.send(:unsafe_set_data!, +'Sample data in redis')
end
it { is_expected.to eq('Sample data in redis') }
@@ -71,7 +73,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
context 'when data_store is database' do
let(:data_store) { :database }
- let(:raw_data) { 'Sample data in database' }
+ let(:raw_data) { +'Sample data in database' }
it { is_expected.to eq('Sample data in database') }
end
@@ -80,7 +82,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let(:data_store) { :fog }
before do
- build_trace_chunk.send(:unsafe_set_data!, 'Sample data in fog')
+ build_trace_chunk.send(:unsafe_set_data!, +'Sample data in fog')
end
it { is_expected.to eq('Sample data in fog') }
@@ -90,7 +92,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
describe '#append' do
subject { build_trace_chunk.append(new_data, offset) }
- let(:new_data) { 'Sample new data' }
+ let(:new_data) { +'Sample new data' }
let(:offset) { 0 }
let(:merged_data) { data + new_data.to_s }
@@ -143,7 +145,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
context 'when new_data is empty' do
- let(:new_data) { '' }
+ let(:new_data) { +'' }
it 'does not append' do
subject
@@ -172,7 +174,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
shared_examples_for 'Scheduling sidekiq worker to flush data to persist store' do
context 'when new data fulfilled chunk size' do
- let(:new_data) { 'a' * described_class::CHUNK_SIZE }
+ let(:new_data) { +'a' * described_class::CHUNK_SIZE }
it 'schedules trace chunk flush worker' do
expect(Ci::BuildTraceChunkFlushWorker).to receive(:perform_async).once
@@ -194,7 +196,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
shared_examples_for 'Scheduling no sidekiq worker' do
context 'when new data fulfilled chunk size' do
- let(:new_data) { 'a' * described_class::CHUNK_SIZE }
+ let(:new_data) { +'a' * described_class::CHUNK_SIZE }
it 'does not schedule trace chunk flush worker' do
expect(Ci::BuildTraceChunkFlushWorker).not_to receive(:perform_async)
@@ -219,7 +221,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let(:data_store) { :redis }
context 'when there are no data' do
- let(:data) { '' }
+ let(:data) { +'' }
it 'has no data' do
expect(build_trace_chunk.data).to be_empty
@@ -230,7 +232,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
context 'when there are some data' do
- let(:data) { 'Sample data in redis' }
+ let(:data) { +'Sample data in redis' }
before do
build_trace_chunk.send(:unsafe_set_data!, data)
@@ -249,7 +251,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let(:data_store) { :database }
context 'when there are no data' do
- let(:data) { '' }
+ let(:data) { +'' }
it 'has no data' do
expect(build_trace_chunk.data).to be_empty
@@ -260,7 +262,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
context 'when there are some data' do
- let(:raw_data) { 'Sample data in database' }
+ let(:raw_data) { +'Sample data in database' }
let(:data) { raw_data }
it 'has data' do
@@ -276,7 +278,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let(:data_store) { :fog }
context 'when there are no data' do
- let(:data) { '' }
+ let(:data) { +'' }
it 'has no data' do
expect(build_trace_chunk.data).to be_empty
@@ -287,7 +289,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
context 'when there are some data' do
- let(:data) { 'Sample data in fog' }
+ let(:data) { +'Sample data in fog' }
before do
build_trace_chunk.send(:unsafe_set_data!, data)
@@ -332,7 +334,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
context 'when data_store is redis' do
let(:data_store) { :redis }
- let(:data) { 'Sample data in redis' }
+ let(:data) { +'Sample data in redis' }
before do
build_trace_chunk.send(:unsafe_set_data!, data)
@@ -343,7 +345,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
context 'when data_store is database' do
let(:data_store) { :database }
- let(:raw_data) { 'Sample data in database' }
+ let(:raw_data) { +'Sample data in database' }
let(:data) { raw_data }
it_behaves_like 'truncates'
@@ -351,7 +353,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
context 'when data_store is fog' do
let(:data_store) { :fog }
- let(:data) { 'Sample data in fog' }
+ let(:data) { +'Sample data in fog' }
before do
build_trace_chunk.send(:unsafe_set_data!, data)
@@ -368,7 +370,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let(:data_store) { :redis }
context 'when data exists' do
- let(:data) { 'Sample data in redis' }
+ let(:data) { +'Sample data in redis' }
before do
build_trace_chunk.send(:unsafe_set_data!, data)
@@ -386,7 +388,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let(:data_store) { :database }
context 'when data exists' do
- let(:raw_data) { 'Sample data in database' }
+ let(:raw_data) { +'Sample data in database' }
let(:data) { raw_data }
it { is_expected.to eq(data.bytesize) }
@@ -401,7 +403,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
let(:data_store) { :fog }
context 'when data exists' do
- let(:data) { 'Sample data in fog' }
+ let(:data) { +'Sample data in fog' }
let(:key) { "tmp/builds/#{build.id}/chunks/#{chunk_index}.log" }
before do
@@ -443,7 +445,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
context 'when data size reached CHUNK_SIZE' do
- let(:data) { 'a' * described_class::CHUNK_SIZE }
+ let(:data) { +'a' * described_class::CHUNK_SIZE }
it 'persists the data' do
expect(build_trace_chunk.redis?).to be_truthy
@@ -463,7 +465,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
context 'when data size has not reached CHUNK_SIZE' do
- let(:data) { 'Sample data in redis' }
+ let(:data) { +'Sample data in redis' }
it 'does not persist the data and the orignal data is intact' do
expect { subject }.to raise_error(described_class::FailedToPersistDataError)
@@ -492,7 +494,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
context 'when data size reached CHUNK_SIZE' do
- let(:data) { 'a' * described_class::CHUNK_SIZE }
+ let(:data) { +'a' * described_class::CHUNK_SIZE }
it 'persists the data' do
expect(build_trace_chunk.database?).to be_truthy
@@ -512,7 +514,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
context 'when data size has not reached CHUNK_SIZE' do
- let(:data) { 'Sample data in database' }
+ let(:data) { +'Sample data in database' }
it 'does not persist the data and the orignal data is intact' do
expect { subject }.to raise_error(described_class::FailedToPersistDataError)
@@ -561,7 +563,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
context 'when data size has not reached CHUNK_SIZE' do
- let(:data) { 'Sample data in fog' }
+ let(:data) { +'Sample data in fog' }
it 'does not raise error' do
expect { subject }.not_to raise_error
diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb
index c2c57379461..8a3a7eee25d 100644
--- a/spec/models/clusters/cluster_spec.rb
+++ b/spec/models/clusters/cluster_spec.rb
@@ -152,6 +152,16 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
end
end
+ describe '.for_project_namespace' do
+ subject { described_class.for_project_namespace(namespace_id) }
+
+ let!(:cluster) { create(:cluster, :project) }
+ let!(:another_cluster) { create(:cluster, :project) }
+ let(:namespace_id) { cluster.first_project.namespace_id }
+
+ it { is_expected.to contain_exactly(cluster) }
+ end
+
describe 'validations' do
subject { cluster.valid? }
diff --git a/spec/models/clusters/clusters_hierarchy_spec.rb b/spec/models/clusters/clusters_hierarchy_spec.rb
index fc35b8257e9..1957e1fc5ee 100644
--- a/spec/models/clusters/clusters_hierarchy_spec.rb
+++ b/spec/models/clusters/clusters_hierarchy_spec.rb
@@ -42,6 +42,28 @@ describe Clusters::ClustersHierarchy do
it 'returns clusters for project' do
expect(base_and_ancestors(cluster.project)).to eq([cluster])
end
+
+ context 'cluster has management project' do
+ let(:management_project) { create(:project, namespace: cluster.first_project.namespace) }
+
+ before do
+ cluster.update!(management_project: management_project)
+ end
+
+ context 'management_project is in same namespace as cluster' do
+ it 'returns cluster for management_project' do
+ expect(base_and_ancestors(management_project)).to eq([cluster])
+ end
+ end
+
+ context 'management_project is in a different namespace from cluster' do
+ let(:management_project) { create(:project) }
+
+ it 'returns nothing' do
+ expect(base_and_ancestors(management_project)).to be_empty
+ end
+ end
+ end
end
context 'cluster has management project' do
@@ -50,16 +72,12 @@ describe Clusters::ClustersHierarchy do
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
- let(:management_project) { create(:project) }
+ let(:management_project) { create(:project, group: group) }
it 'returns clusters for management_project' do
expect(base_and_ancestors(management_project)).to eq([group_cluster])
end
- it 'returns nothing if include_management_project is false' do
- expect(base_and_ancestors(management_project, include_management_project: false)).to be_empty
- end
-
it 'returns clusters for project' do
expect(base_and_ancestors(project)).to eq([project_cluster, group_cluster])
end
@@ -70,17 +88,21 @@ describe Clusters::ClustersHierarchy do
end
context 'project in nested group with clusters at some levels' do
- let!(:child) { create(:cluster, :group, groups: [child_group], management_project: management_project) }
- let!(:ancestor) { create(:cluster, :group, groups: [ancestor_group]) }
+ let!(:child) { create(:cluster, :group, groups: [child_group]) }
+ let!(:ancestor) { create(:cluster, :group, groups: [ancestor_group], management_project: management_project) }
let(:ancestor_group) { create(:group) }
let(:parent_group) { create(:group, parent: ancestor_group) }
let(:child_group) { create(:group, parent: parent_group) }
let(:project) { create(:project, group: child_group) }
- let(:management_project) { create(:project) }
+ let(:management_project) { create(:project, group: child_group) }
+
+ it 'returns clusters for management_project' do
+ expect(base_and_ancestors(management_project)).to eq([ancestor, child])
+ end
it 'returns clusters for management_project' do
- expect(base_and_ancestors(management_project)).to eq([child])
+ expect(base_and_ancestors(management_project, include_management_project: false)).to eq([child, ancestor])
end
it 'returns clusters for project' do
diff --git a/spec/models/concerns/deployment_platform_spec.rb b/spec/models/concerns/deployment_platform_spec.rb
index 2cac42f56ff..9164c3a75c5 100644
--- a/spec/models/concerns/deployment_platform_spec.rb
+++ b/spec/models/concerns/deployment_platform_spec.rb
@@ -13,7 +13,11 @@ describe DeploymentPlatform do
end
context 'when project is the cluster\'s management project ' do
- let!(:cluster_with_management_project) { create(:cluster, :provided_by_user, management_project: project) }
+ let(:another_project) { create(:project, namespace: project.namespace) }
+
+ let!(:cluster_with_management_project) do
+ create(:cluster, :provided_by_user, projects: [another_project], management_project: project)
+ end
context 'cluster_management_project feature is enabled' do
it 'returns the cluster with management project' do
@@ -66,7 +70,11 @@ describe DeploymentPlatform do
end
context 'when project is the cluster\'s management project ' do
- let!(:cluster_with_management_project) { create(:cluster, :provided_by_user, management_project: project) }
+ let(:another_project) { create(:project, namespace: project.namespace) }
+
+ let!(:cluster_with_management_project) do
+ create(:cluster, :provided_by_user, projects: [another_project], management_project: project)
+ end
context 'cluster_management_project feature is enabled' do
it 'returns the cluster with management project' do
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index 2ecbe548520..120ba67f328 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -227,6 +227,14 @@ describe Milestone do
end
end
+ describe '#to_ability_name' do
+ it 'returns milestone' do
+ milestone = build(:milestone)
+
+ expect(milestone.to_ability_name).to eq('milestone')
+ end
+ end
+
describe '.search' do
let(:milestone) { create(:milestone, title: 'foo', description: 'bar') }
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 4c320b4b145..1c895f084b0 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -578,24 +578,30 @@ describe Note do
end
describe '#to_ability_name' do
- it 'returns snippet for a project snippet note' do
- expect(build(:note_on_project_snippet).to_ability_name).to eq('project_snippet')
+ it 'returns note' do
+ expect(build(:note).to_ability_name).to eq('note')
+ end
+ end
+
+ describe '#noteable_ability_name' do
+ it 'returns project_snippet for a project snippet note' do
+ expect(build(:note_on_project_snippet).noteable_ability_name).to eq('project_snippet')
end
it 'returns personal_snippet for a personal snippet note' do
- expect(build(:note_on_personal_snippet).to_ability_name).to eq('personal_snippet')
+ expect(build(:note_on_personal_snippet).noteable_ability_name).to eq('personal_snippet')
end
it 'returns merge_request for an MR note' do
- expect(build(:note_on_merge_request).to_ability_name).to eq('merge_request')
+ expect(build(:note_on_merge_request).noteable_ability_name).to eq('merge_request')
end
it 'returns issue for an issue note' do
- expect(build(:note_on_issue).to_ability_name).to eq('issue')
+ expect(build(:note_on_issue).noteable_ability_name).to eq('issue')
end
- it 'returns issue for a commit note' do
- expect(build(:note_on_commit).to_ability_name).to eq('commit')
+ it 'returns commit for a commit note' do
+ expect(build(:note_on_commit).noteable_ability_name).to eq('commit')
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 5922a6f36f5..b4d9ce28829 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -4444,6 +4444,14 @@ describe Project do
end
end
+ describe '#to_ability_name' do
+ it 'returns project' do
+ project = build(:project_empty_repo)
+
+ expect(project.to_ability_name).to eq('project')
+ end
+ end
+
describe '#execute_hooks' do
let(:data) { { ref: 'refs/heads/master', data: 'data' } }
it 'executes active projects hooks with the specified scope' do
diff --git a/spec/models/shard_spec.rb b/spec/models/shard_spec.rb
index 83104711b55..4da86858b54 100644
--- a/spec/models/shard_spec.rb
+++ b/spec/models/shard_spec.rb
@@ -1,4 +1,5 @@
-# frozen_string_literals: true
+# frozen_string_literal: true
+
require 'spec_helper'
describe Shard do
diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
index 8f3730f97ac..38415613b9e 100644
--- a/spec/models/wiki_page_spec.rb
+++ b/spec/models/wiki_page_spec.rb
@@ -358,6 +358,23 @@ describe WikiPage do
end
end
+ describe '#path' do
+ let(:path) { 'mypath.md' }
+ let(:wiki_page) { instance_double('Gitlab::Git::WikiPage', path: path).as_null_object }
+
+ it 'returns the path when persisted' do
+ page = described_class.new(wiki, wiki_page, true)
+
+ expect(page.path).to eq(path)
+ end
+
+ it 'returns nil when not persisted' do
+ page = described_class.new(wiki, wiki_page, false)
+
+ expect(page.path).to be_nil
+ end
+ end
+
describe '#directory' do
context 'when the page is at the root directory' do
it 'returns an empty string' do
diff --git a/spec/requests/api/group_clusters_spec.rb b/spec/requests/api/group_clusters_spec.rb
index 46e3dd650cc..97465647a87 100644
--- a/spec/requests/api/group_clusters_spec.rb
+++ b/spec/requests/api/group_clusters_spec.rb
@@ -286,12 +286,15 @@ describe API::GroupClusters do
let(:update_params) do
{
domain: domain,
- platform_kubernetes_attributes: platform_kubernetes_attributes
+ platform_kubernetes_attributes: platform_kubernetes_attributes,
+ management_project_id: management_project_id
}
end
let(:domain) { 'new-domain.com' }
let(:platform_kubernetes_attributes) { {} }
+ let(:management_project) { create(:project, group: group) }
+ let(:management_project_id) { management_project.id }
let(:cluster) do
create(:cluster, :group, :provided_by_gcp,
@@ -308,6 +311,8 @@ describe API::GroupClusters do
context 'authorized user' do
before do
+ management_project.add_maintainer(current_user)
+
put api("/groups/#{group.id}/clusters/#{cluster.id}", current_user), params: update_params
cluster.reload
@@ -320,6 +325,7 @@ describe API::GroupClusters do
it 'updates cluster attributes' do
expect(cluster.domain).to eq('new-domain.com')
+ expect(cluster.management_project).to eq(management_project)
end
end
@@ -332,6 +338,7 @@ describe API::GroupClusters do
it 'does not update cluster attributes' do
expect(cluster.domain).to eq('old-domain.com')
+ expect(cluster.management_project).to be_nil
end
it 'returns validation errors' do
@@ -339,6 +346,18 @@ describe API::GroupClusters do
end
end
+ context 'current user does not have access to management_project_id' do
+ let(:management_project_id) { create(:project).id }
+
+ it 'responds with 400' do
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it 'returns validation errors' do
+ expect(json_response['message']['management_project_id'].first).to match('don\'t have permission')
+ end
+ end
+
context 'with a GCP cluster' do
context 'when user tries to change GCP specific fields' do
let(:platform_kubernetes_attributes) do
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index 63b75b810dc..02d1e4bf2f1 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -316,6 +316,7 @@ describe API::Internal::Base do
expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
+ expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-get-all-lfs-pointers-go' => 'true', 'gitaly-feature-inforef-uploadpack-cache' => 'true')
expect(user.reload.last_activity_on).to eql(Date.today)
end
end
@@ -335,6 +336,7 @@ describe API::Internal::Base do
expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
+ expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-get-all-lfs-pointers-go' => 'true', 'gitaly-feature-inforef-uploadpack-cache' => 'true')
expect(user.reload.last_activity_on).to be_nil
end
end
@@ -576,6 +578,7 @@ describe API::Internal::Base do
expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path)
expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage))
expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage))
+ expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-get-all-lfs-pointers-go' => 'true', 'gitaly-feature-inforef-uploadpack-cache' => 'true')
end
end
diff --git a/spec/requests/api/project_clusters_spec.rb b/spec/requests/api/project_clusters_spec.rb
index a7b919de2ef..04e59238877 100644
--- a/spec/requests/api/project_clusters_spec.rb
+++ b/spec/requests/api/project_clusters_spec.rb
@@ -281,11 +281,14 @@ describe API::ProjectClusters do
let(:api_url) { 'https://kubernetes.example.com' }
let(:namespace) { 'new-namespace' }
let(:platform_kubernetes_attributes) { { namespace: namespace } }
+ let(:management_project) { create(:project, namespace: project.namespace) }
+ let(:management_project_id) { management_project.id }
let(:update_params) do
{
domain: 'new-domain.com',
- platform_kubernetes_attributes: platform_kubernetes_attributes
+ platform_kubernetes_attributes: platform_kubernetes_attributes,
+ management_project_id: management_project_id
}
end
@@ -310,6 +313,8 @@ describe API::ProjectClusters do
context 'authorized user' do
before do
+ management_project.add_maintainer(current_user)
+
put api("/projects/#{project.id}/clusters/#{cluster.id}", current_user), params: update_params
cluster.reload
@@ -323,6 +328,7 @@ describe API::ProjectClusters do
it 'updates cluster attributes' do
expect(cluster.domain).to eq('new-domain.com')
expect(cluster.platform_kubernetes.namespace).to eq('new-namespace')
+ expect(cluster.management_project).to eq(management_project)
end
end
@@ -336,6 +342,7 @@ describe API::ProjectClusters do
it 'does not update cluster attributes' do
expect(cluster.domain).not_to eq('new_domain.com')
expect(cluster.platform_kubernetes.namespace).not_to eq('invalid_namespace')
+ expect(cluster.management_project).not_to eq(management_project)
end
it 'returns validation errors' do
@@ -343,6 +350,18 @@ describe API::ProjectClusters do
end
end
+ context 'current user does not have access to management_project_id' do
+ let(:management_project_id) { create(:project).id }
+
+ it 'responds with 400' do
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it 'returns validation errors' do
+ expect(json_response['message']['management_project_id'].first).to match('don\'t have permission')
+ end
+ end
+
context 'with a GCP cluster' do
context 'when user tries to change GCP specific fields' do
let(:platform_kubernetes_attributes) do
diff --git a/spec/serializers/merge_request_diff_entity_spec.rb b/spec/serializers/merge_request_diff_entity_spec.rb
index 062f17963c0..59ec0b22158 100644
--- a/spec/serializers/merge_request_diff_entity_spec.rb
+++ b/spec/serializers/merge_request_diff_entity_spec.rb
@@ -7,14 +7,15 @@ describe MergeRequestDiffEntity do
let(:request) { EntityRequest.new(project: project) }
let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) }
let(:merge_request_diffs) { merge_request.merge_request_diffs }
+ let(:merge_request_diff) { merge_request_diffs.first }
let(:entity) do
- described_class.new(merge_request_diffs.first, request: request, merge_request: merge_request, merge_request_diffs: merge_request_diffs)
+ described_class.new(merge_request_diff, request: request, merge_request: merge_request, merge_request_diffs: merge_request_diffs)
end
- context 'as json' do
- subject { entity.as_json }
+ subject { entity.as_json }
+ context 'as json' do
it 'exposes needed attributes' do
expect(subject).to include(
:version_index, :created_at, :commits_count,
@@ -23,4 +24,16 @@ describe MergeRequestDiffEntity do
)
end
end
+
+ describe '#short_commit_sha' do
+ it 'returns short sha' do
+ expect(subject[:short_commit_sha]).to eq('b83d6e39')
+ end
+
+ it 'returns nil if head_commit_sha does not exist' do
+ allow(merge_request_diff).to receive(:head_commit_sha).and_return(nil)
+
+ expect(subject[:short_commit_sha]).to eq(nil)
+ end
+ end
end
diff --git a/spec/services/clusters/update_service_spec.rb b/spec/services/clusters/update_service_spec.rb
index 3ee45375dca..8c2d8c9246e 100644
--- a/spec/services/clusters/update_service_spec.rb
+++ b/spec/services/clusters/update_service_spec.rb
@@ -90,5 +90,115 @@ describe Clusters::UpdateService do
end
end
end
+
+ context 'when params includes :management_project_id' do
+ context 'management_project is non-existent' do
+ let(:params) do
+ { management_project_id: 0 }
+ end
+
+ it 'does not update management_project_id' do
+ is_expected.to eq(false)
+
+ expect(cluster.errors[:management_project_id]).to include('Project does not exist or you don\'t have permission to perform this action')
+
+ cluster.reload
+ expect(cluster.management_project_id).to be_nil
+ end
+ end
+
+ shared_examples 'setting a management project' do
+ context 'user is authorized to adminster manangement_project' do
+ before do
+ management_project.add_maintainer(cluster.user)
+ end
+
+ let(:params) do
+ { management_project_id: management_project.id }
+ end
+
+ it 'updates management_project_id' do
+ is_expected.to eq(true)
+
+ expect(cluster.management_project).to eq(management_project)
+ end
+ end
+
+ context 'user is not authorized to adminster manangement_project' do
+ let(:params) do
+ { management_project_id: management_project.id }
+ end
+
+ it 'does not update management_project_id' do
+ is_expected.to eq(false)
+
+ expect(cluster.errors[:management_project_id]).to include('Project does not exist or you don\'t have permission to perform this action')
+
+ cluster.reload
+ expect(cluster.management_project_id).to be_nil
+ end
+ end
+ end
+
+ context 'project cluster' do
+ include_examples 'setting a management project' do
+ let(:management_project) { create(:project, namespace: cluster.first_project.namespace) }
+ end
+
+ context 'manangement_project is outside of the namespace scope' do
+ before do
+ management_project.update(group: create(:group))
+ end
+
+ let(:params) do
+ { management_project_id: management_project.id }
+ end
+
+ it 'does not update management_project_id' do
+ is_expected.to eq(false)
+
+ expect(cluster.errors[:management_project_id]).to include('Project does not exist or you don\'t have permission to perform this action')
+
+ cluster.reload
+ expect(cluster.management_project_id).to be_nil
+ end
+ end
+ end
+
+ context 'group cluster' do
+ let(:cluster) { create(:cluster, :group) }
+
+ include_examples 'setting a management project' do
+ let(:management_project) { create(:project, group: cluster.first_group) }
+ end
+
+ context 'manangement_project is outside of the namespace scope' do
+ before do
+ management_project.update(group: create(:group))
+ end
+
+ let(:params) do
+ { management_project_id: management_project.id }
+ end
+
+ it 'does not update management_project_id' do
+ is_expected.to eq(false)
+
+ expect(cluster.errors[:management_project_id]).to include('Project does not exist or you don\'t have permission to perform this action')
+
+ cluster.reload
+ expect(cluster.management_project_id).to be_nil
+ end
+ end
+ end
+
+ context 'instance cluster' do
+ let(:cluster) { create(:cluster, :instance) }
+
+ include_examples 'setting a management project' do
+ let(:management_project) { create(:project) }
+ end
+ end
+ end
end
end
diff --git a/spec/services/error_tracking/list_projects_service_spec.rb b/spec/services/error_tracking/list_projects_service_spec.rb
index 730fccc599e..a272a604184 100644
--- a/spec/services/error_tracking/list_projects_service_spec.rb
+++ b/spec/services/error_tracking/list_projects_service_spec.rb
@@ -50,6 +50,19 @@ describe ErrorTracking::ListProjectsService do
end
end
+ context 'masked param token' do
+ let(:params) { ActionController::Parameters.new(token: "*********", api_host: new_api_host) }
+
+ before do
+ expect(error_tracking_setting).to receive(:list_sentry_projects)
+ .and_return({ projects: [] })
+ end
+
+ it 'uses database token' do
+ expect { subject.execute }.not_to change { error_tracking_setting.token }
+ end
+ end
+
context 'sentry client raises exception' do
context 'Sentry::Client::Error' do
before do
diff --git a/spec/services/projects/operations/update_service_spec.rb b/spec/services/projects/operations/update_service_spec.rb
index b2f9fd6df79..81d59a98b9b 100644
--- a/spec/services/projects/operations/update_service_spec.rb
+++ b/spec/services/projects/operations/update_service_spec.rb
@@ -145,6 +145,27 @@ describe Projects::Operations::UpdateService do
end
end
+ context 'with masked param token' do
+ let(:params) do
+ {
+ error_tracking_setting_attributes: {
+ enabled: false,
+ token: '*' * 8
+ }
+ }
+ end
+
+ before do
+ create(:project_error_tracking_setting, project: project, token: 'token')
+ end
+
+ it 'does not update token' do
+ expect(result[:status]).to eq(:success)
+
+ expect(project.error_tracking_setting.token).to eq('token')
+ end
+ end
+
context 'with invalid parameters' do
let(:params) { {} }
diff --git a/spec/sidekiq/cron/job_gem_dependency_spec.rb b/spec/sidekiq/cron/job_gem_dependency_spec.rb
index 2e7de75fd08..20347b4d306 100644
--- a/spec/sidekiq/cron/job_gem_dependency_spec.rb
+++ b/spec/sidekiq/cron/job_gem_dependency_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Sidekiq::Cron::Job do
diff --git a/spec/support/helpers/smime_helper.rb b/spec/support/helpers/smime_helper.rb
index 656b3e196ba..3ad19cd3da0 100644
--- a/spec/support/helpers/smime_helper.rb
+++ b/spec/support/helpers/smime_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SmimeHelper
include OpenSSL