summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/ci/releases.gitlab-ci.yml36
-rw-r--r--app/assets/javascripts/error_tracking/components/error_tracking_list.vue47
-rw-r--r--app/assets/javascripts/error_tracking/store/list/actions.js1
-rw-r--r--app/assets/javascripts/error_tracking/store/list/mutation_types.js1
-rw-r--r--app/assets/javascripts/error_tracking/store/list/mutations.js3
-rw-r--r--app/assets/javascripts/error_tracking/store/list/state.js1
-rw-r--r--app/assets/javascripts/ide/stores/utils.js2
-rw-r--r--app/assets/javascripts/merge_request_tabs.js18
-rw-r--r--app/controllers/concerns/uploads_actions.rb19
-rw-r--r--app/graphql/types/permission_types/snippet.rb1
-rw-r--r--app/helpers/projects_helper.rb1
-rw-r--r--app/models/ci/build.rb11
-rw-r--r--app/models/ci/resource.rb13
-rw-r--r--app/models/ci/resource_group.rb36
-rw-r--r--app/models/project.rb3
-rw-r--r--app/models/project_services/chat_message/merge_message.rb2
-rw-r--r--app/models/project_services/hipchat_service.rb2
-rw-r--r--app/policies/user_policy.rb5
-rw-r--r--app/presenters/snippet_presenter.rb4
-rw-r--r--app/services/ci/retry_build_service.rb2
-rw-r--r--app/views/users/_profile_basic_info.html.haml6
-rw-r--r--app/views/users/show.html.haml94
-rw-r--r--changelogs/unreleased/35122-sentry-error-search-pagination-implement-pagination-in-sentry-error.yml5
-rw-r--r--changelogs/unreleased/ci-resource-group-model.yml5
-rw-r--r--changelogs/unreleased/dz-move-project-routes.yml5
-rw-r--r--changelogs/unreleased/fix-padding-design-comments.yml5
-rw-r--r--changelogs/unreleased/fj-add-report-permission-to-graphql-snippet-endpoint.yml5
-rw-r--r--changelogs/unreleased/issue_7105.yml5
-rw-r--r--changelogs/unreleased/nicolasdular-allow-broadcast-message-styling.yml5
-rw-r--r--changelogs/unreleased/xanf-blocked-profile-page.yml5
-rw-r--r--config/routes/project.rb154
-rw-r--r--db/migrate/20191128145231_add_ci_resource_groups.rb24
-rw-r--r--db/migrate/20191129144631_add_index_to_resource_group_id.rb20
-rw-r--r--db/schema.rb24
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql51
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json175
-rw-r--r--doc/api/graphql/reference/index.md9
-rw-r--r--doc/user/project/clusters/serverless/index.md18
-rw-r--r--lib/banzai/filter/base_sanitization_filter.rb1
-rw-r--r--lib/banzai/filter/broadcast_message_sanitization_filter.rb20
-rw-r--r--lib/banzai/pipeline/broadcast_message_pipeline.rb2
-rw-r--r--lib/gitlab/ci/config/entry/job.rb9
-rw-r--r--lib/gitlab/ci/pipeline/chain/validate/external.rb2
-rw-r--r--lib/gitlab/ci/pipeline/seed/build.rb2
-rw-r--r--lib/gitlab/ci/pipeline/seed/build/resource_group.rb40
-rw-r--r--lib/gitlab/ci/yaml_processor.rb1
-rw-r--r--lib/gitlab/file_type_detection.rb5
-rw-r--r--lib/gitlab/import_export/import_export.yml2
-rw-r--r--lib/gitlab/import_export/relation_factory.rb4
-rw-r--r--locale/gitlab.pot6
-rwxr-xr-xscripts/frontend/check_no_partial_karma_jest.sh38
-rwxr-xr-xscripts/static-analysis3
-rw-r--r--scripts/sync-stable-branch.sh14
-rw-r--r--spec/controllers/uploads_controller_spec.rb35
-rw-r--r--spec/factories/ci/builds.rb8
-rw-r--r--spec/factories/ci/resource.rb11
-rw-r--r--spec/factories/ci/resource_group.rb8
-rw-r--r--spec/factories/notes.rb4
-rw-r--r--spec/features/users/show_spec.rb36
-rw-r--r--spec/fixtures/lib/gitlab/import_export/complex/project.json11
-rw-r--r--spec/frontend/error_tracking/components/error_tracking_list_spec.js82
-rw-r--r--spec/frontend/error_tracking/store/list/actions_spec.js1
-rw-r--r--spec/frontend/notes/mock_data.js26
-rw-r--r--spec/graphql/types/permission_types/snippet_spec.rb2
-rw-r--r--spec/helpers/award_emoji_helper_spec.rb2
-rw-r--r--spec/helpers/events_helper_spec.rb2
-rw-r--r--spec/helpers/labels_helper_spec.rb10
-rw-r--r--spec/javascripts/ide/stores/modules/commit/actions_spec.js2
-rw-r--r--spec/javascripts/jobs/components/stages_dropdown_spec.js2
-rw-r--r--spec/javascripts/merge_request_spec.js8
-rw-r--r--spec/javascripts/merge_request_tabs_spec.js34
-rw-r--r--spec/javascripts/notes/components/note_actions_spec.js2
-rw-r--r--spec/lib/banzai/filter/broadcast_message_sanitization_filter_spec.rb51
-rw-r--r--spec/lib/banzai/filter/relative_link_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/sanitization_filter_spec.rb180
-rw-r--r--spec/lib/banzai/pipeline/broadcast_message_pipeline_spec.rb23
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build/resource_group_spec.rb46
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build_spec.rb9
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb15
-rw-r--r--spec/lib/gitlab/data_builder/pipeline_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb10
-rw-r--r--spec/lib/gitlab/url_builder_spec.rb6
-rw-r--r--spec/models/ci/build_spec.rb62
-rw-r--r--spec/models/ci/resource_group_spec.rb88
-rw-r--r--spec/models/ci/resource_spec.rb28
-rw-r--r--spec/models/project_services/chat_message/merge_message_spec.rb12
-rw-r--r--spec/presenters/merge_request_presenter_spec.rb14
-rw-r--r--spec/presenters/snippet_presenter_spec.rb16
-rw-r--r--spec/requests/api/internal/base_spec.rb4
-rw-r--r--spec/requests/api/releases_spec.rb2
-rw-r--r--spec/requests/projects/merge_requests_discussions_spec.rb2
-rw-r--r--spec/requests/user_activity_spec.rb4
-rw-r--r--spec/routing/project_routing_spec.rb42
-rw-r--r--spec/serializers/merge_request_widget_entity_spec.rb4
-rw-r--r--spec/serializers/paginated_diff_entity_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb38
-rw-r--r--spec/services/ci/expire_pipeline_cache_service_spec.rb4
-rw-r--r--spec/services/ci/retry_build_service_spec.rb6
-rw-r--r--spec/services/merge_requests/get_urls_service_spec.rb6
-rw-r--r--spec/services/system_note_service_spec.rb2
-rw-r--r--spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb182
102 files changed, 1178 insertions, 943 deletions
diff --git a/.gitlab/ci/releases.gitlab-ci.yml b/.gitlab/ci/releases.gitlab-ci.yml
index 1ddc4e90fcf..d4e0236f3a8 100644
--- a/.gitlab/ci/releases.gitlab-ci.yml
+++ b/.gitlab/ci/releases.gitlab-ci.yml
@@ -1,22 +1,36 @@
---
-# 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:
+# Syncs any changes pushed to a stable branch to the corresponding
+# gitlab-foss/CE stable branch. We run this prior to any tests so that random
+# failures don't prevent a sync.
+.merge-train-sync:
# 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
+ only:
+ variables:
+ - $CI_SERVER_HOST == "gitlab.com"
+
+sync-stable-branch:
+ extends: .merge-train-sync
+ variables:
+ SOURCE_PROJECT: gitlab-org/gitlab
+ TARGET_PROJECT: gitlab-org/gitlab-foss
+ only:
+ refs:
+ - /^[\d-]+-stable-ee$/@gitlab-org/gitlab
+
+sync-security-branch:
+ extends: .merge-train-sync
+ variables:
+ SOURCE_PROJECT: gitlab-org/security/gitlab
+ TARGET_PROJECT: gitlab-org/security/gitlab-foss
+ only:
+ refs:
+ - /^[\d-]+-stable-ee$/@gitlab-org/security/gitlab
diff --git a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
index 23e251e4201..8e2128ac713 100644
--- a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
+++ b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
@@ -12,13 +12,18 @@ import {
GlDropdownItem,
GlDropdownDivider,
GlTooltipDirective,
+ GlPagination,
} from '@gitlab/ui';
import AccessorUtils from '~/lib/utils/accessor';
import Icon from '~/vue_shared/components/icon.vue';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import { __ } from '~/locale';
+import _ from 'underscore';
export default {
+ FIRST_PAGE: 1,
+ PREV_PAGE: 1,
+ NEXT_PAGE: 2,
fields: [
{ key: 'error', label: __('Open errors'), thClass: 'w-70p' },
{ key: 'events', label: __('Events') },
@@ -42,6 +47,7 @@ export default {
GlTable,
GlFormInput,
Icon,
+ GlPagination,
TimeAgo,
},
directives: {
@@ -73,10 +79,28 @@ export default {
data() {
return {
errorSearchQuery: '',
+ pageValue: this.$options.FIRST_PAGE,
};
},
computed: {
- ...mapState('list', ['errors', 'loading', 'searchQuery', 'sortField', 'recentSearches']),
+ ...mapState('list', [
+ 'errors',
+ 'loading',
+ 'searchQuery',
+ 'sortField',
+ 'recentSearches',
+ 'pagination',
+ ]),
+ paginationRequired() {
+ return !_.isEmpty(this.pagination);
+ },
+ },
+ watch: {
+ pagination() {
+ if (typeof this.pagination.previous === 'undefined') {
+ this.pageValue = this.$options.FIRST_PAGE;
+ }
+ },
},
created() {
if (this.errorTrackingEnabled) {
@@ -103,6 +127,17 @@ export default {
getDetailsLink(errorId) {
return `error_tracking/${errorId}/details`;
},
+ goToNextPage() {
+ this.pageValue = this.$options.NEXT_PAGE;
+ this.startPolling(`${this.indexPath}?cursor=${this.pagination.next.cursor}`);
+ },
+ goToPrevPage() {
+ this.startPolling(`${this.indexPath}?cursor=${this.pagination.previous.cursor}`);
+ },
+ goToPage(page) {
+ window.scrollTo(0, 0);
+ return page === this.$options.PREV_PAGE ? this.goToPrevPage() : this.goToNextPage();
+ },
isCurrentSortField(field) {
return field === this.sortField;
},
@@ -217,7 +252,6 @@ export default {
</span>
</div>
</template>
-
<template slot="events" slot-scope="errors">
<div class="text-md-right">{{ errors.item.count }}</div>
</template>
@@ -240,6 +274,15 @@ export default {
</div>
</template>
</gl-table>
+ <gl-pagination
+ v-show="!loading"
+ v-if="paginationRequired"
+ :prev-page="$options.PREV_PAGE"
+ :next-page="$options.NEXT_PAGE"
+ :value="pageValue"
+ align="center"
+ @input="goToPage"
+ />
</div>
<div v-else-if="userCanEnableErrorTracking">
<gl-empty-state
diff --git a/app/assets/javascripts/error_tracking/store/list/actions.js b/app/assets/javascripts/error_tracking/store/list/actions.js
index b1c81b55e58..c9e882c4ed2 100644
--- a/app/assets/javascripts/error_tracking/store/list/actions.js
+++ b/app/assets/javascripts/error_tracking/store/list/actions.js
@@ -23,6 +23,7 @@ export function startPolling({ state, commit, dispatch }) {
if (!data) {
return;
}
+ commit(types.SET_PAGINATION, data.pagination);
commit(types.SET_ERRORS, data.errors);
commit(types.SET_LOADING, false);
dispatch('stopPolling');
diff --git a/app/assets/javascripts/error_tracking/store/list/mutation_types.js b/app/assets/javascripts/error_tracking/store/list/mutation_types.js
index 3ebfef76324..301984a1ee0 100644
--- a/app/assets/javascripts/error_tracking/store/list/mutation_types.js
+++ b/app/assets/javascripts/error_tracking/store/list/mutation_types.js
@@ -4,6 +4,7 @@ export const SET_LOADING = 'SET_LOADING';
export const ADD_RECENT_SEARCH = 'ADD_RECENT_SEARCH';
export const CLEAR_RECENT_SEARCHES = 'CLEAR_RECENT_SEARCHES';
export const LOAD_RECENT_SEARCHES = 'LOAD_RECENT_SEARCHES';
+export const SET_PAGINATION = 'SET_PAGINATION';
export const SET_ENDPOINT = 'SET_ENDPOINT';
export const SET_SORT_FIELD = 'SET_SORT_FIELD';
export const SET_SEARCH_QUERY = 'SET_SEARCH_QUERY';
diff --git a/app/assets/javascripts/error_tracking/store/list/mutations.js b/app/assets/javascripts/error_tracking/store/list/mutations.js
index 048660eaeeb..5648013bb89 100644
--- a/app/assets/javascripts/error_tracking/store/list/mutations.js
+++ b/app/assets/javascripts/error_tracking/store/list/mutations.js
@@ -44,6 +44,9 @@ export default {
throw e;
}
},
+ [types.SET_PAGINATION](state, pagination) {
+ state.pagination = pagination;
+ },
[types.SET_SORT_FIELD](state, field) {
state.sortField = field;
},
diff --git a/app/assets/javascripts/error_tracking/store/list/state.js b/app/assets/javascripts/error_tracking/store/list/state.js
index f20b707142e..93dc1040fde 100644
--- a/app/assets/javascripts/error_tracking/store/list/state.js
+++ b/app/assets/javascripts/error_tracking/store/list/state.js
@@ -6,4 +6,5 @@ export default () => ({
searchQuery: null,
indexPath: '',
recentSearches: [],
+ pagination: {},
});
diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js
index be7ee80656f..a29d9bf3b40 100644
--- a/app/assets/javascripts/ide/stores/utils.js
+++ b/app/assets/javascripts/ide/stores/utils.js
@@ -162,7 +162,7 @@ export const createCommitPayload = ({
});
export const createNewMergeRequestUrl = (projectUrl, source, target) =>
- `${projectUrl}/merge_requests/new?merge_request[source_branch]=${source}&merge_request[target_branch]=${target}&nav_source=webide`;
+ `${projectUrl}/-/merge_requests/new?merge_request[source_branch]=${source}&merge_request[target_branch]=${target}&nav_source=webide`;
const sortTreesByTypeAndName = (a, b) => {
if (a.type === 'tree' && b.type === 'blob') {
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 52674107df2..8d09e88e772 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -32,17 +32,17 @@ import { __ } from './locale';
//
// <ul class="nav-links merge-request-tabs">
// <li class="notes-tab active">
-// <a data-action="notes" data-target="#notes" data-toggle="tab" href="/foo/bar/merge_requests/1">
+// <a data-action="notes" data-target="#notes" data-toggle="tab" href="/foo/bar/-/merge_requests/1">
// Discussion
// </a>
// </li>
// <li class="commits-tab">
-// <a data-action="commits" data-target="#commits" data-toggle="tab" href="/foo/bar/merge_requests/1/commits">
+// <a data-action="commits" data-target="#commits" data-toggle="tab" href="/foo/bar/-/merge_requests/1/commits">
// Commits
// </a>
// </li>
// <li class="diffs-tab">
-// <a data-action="diffs" data-target="#diffs" data-toggle="tab" href="/foo/bar/merge_requests/1/diffs">
+// <a data-action="diffs" data-target="#diffs" data-toggle="tab" href="/foo/bar/-/merge_requests/1/diffs">
// Diffs
// </a>
// </li>
@@ -260,17 +260,17 @@ export default class MergeRequestTabs {
//
// Examples:
//
- // location.pathname # => "/namespace/project/merge_requests/1"
+ // location.pathname # => "/namespace/project/-/merge_requests/1"
// setCurrentAction('diffs')
- // location.pathname # => "/namespace/project/merge_requests/1/diffs"
+ // location.pathname # => "/namespace/project/-/merge_requests/1/diffs"
//
- // location.pathname # => "/namespace/project/merge_requests/1/diffs"
+ // location.pathname # => "/namespace/project/-/merge_requests/1/diffs"
// setCurrentAction('show')
- // location.pathname # => "/namespace/project/merge_requests/1"
+ // location.pathname # => "/namespace/project/-/merge_requests/1"
//
- // location.pathname # => "/namespace/project/merge_requests/1/diffs"
+ // location.pathname # => "/namespace/project/-/merge_requests/1/diffs"
// setCurrentAction('commits')
- // location.pathname # => "/namespace/project/merge_requests/1/commits"
+ // location.pathname # => "/namespace/project/-/merge_requests/1/commits"
//
// Returns the new URL String
setCurrentAction(action) {
diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb
index 9b3b2c4a482..655575e0944 100644
--- a/app/controllers/concerns/uploads_actions.rb
+++ b/app/controllers/concerns/uploads_actions.rb
@@ -44,15 +44,14 @@ module UploadsActions
expires_in ttl, directives
- disposition = uploader.embeddable? ? 'inline' : 'attachment'
-
- uploaders = [uploader, *uploader.versions.values]
- uploader = uploaders.find { |version| version.filename == params[:filename] }
+ file_uploader = [uploader, *uploader.versions.values].find do |version|
+ version.filename == params[:filename]
+ end
- return render_404 unless uploader
+ return render_404 unless file_uploader
workhorse_set_content_type!
- send_upload(uploader, attachment: uploader.filename, disposition: disposition)
+ send_upload(file_uploader, attachment: file_uploader.filename, disposition: content_disposition)
end
def authorize
@@ -83,6 +82,14 @@ module UploadsActions
end
end
+ def content_disposition
+ if uploader.embeddable? || uploader.pdf?
+ 'inline'
+ else
+ 'attachment'
+ end
+ end
+
def uploader_class
raise NotImplementedError
end
diff --git a/app/graphql/types/permission_types/snippet.rb b/app/graphql/types/permission_types/snippet.rb
index 1e21efe790a..0fc13c60983 100644
--- a/app/graphql/types/permission_types/snippet.rb
+++ b/app/graphql/types/permission_types/snippet.rb
@@ -10,6 +10,7 @@ module Types
permission_field :read_snippet, method: :can_read_snippet?
permission_field :update_snippet, method: :can_update_snippet?
permission_field :admin_snippet, method: :can_admin_snippet?
+ permission_field :report_snippet, method: :can_report_as_spam?
end
end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index c7fdd009edb..d683faf6a20 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -686,6 +686,7 @@ module ProjectsHelper
error_tracking
user
gcp
+ logs
]
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 4077a868373..b6c71f81a49 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -23,7 +23,6 @@ module Ci
belongs_to :runner
belongs_to :trigger_request
belongs_to :erased_by, class_name: 'User'
- belongs_to :resource_group, class_name: 'Ci::ResourceGroup', inverse_of: :builds
RUNNER_FEATURES = {
upload_multiple_artifacts: -> (build) { build.publishes_artifacts_reports? },
@@ -35,7 +34,6 @@ module Ci
}.freeze
has_one :deployment, as: :deployable, class_name: 'Deployment'
- has_one :resource, class_name: 'Ci::Resource', inverse_of: :build
has_many :trace_sections, class_name: 'Ci::BuildTraceSection'
has_many :trace_chunks, class_name: 'Ci::BuildTraceChunk', foreign_key: :build_id
@@ -443,15 +441,6 @@ module Ci
environment.present?
end
- def requires_resource?
- Feature.enabled?(:ci_resource_group, project) &&
- self.resource_group_id.present? && resource.nil?
- end
-
- def retains_resource?
- self.resource_group_id.present? && resource.present?
- end
-
def starts_environment?
has_environment? && self.environment_action == 'start'
end
diff --git a/app/models/ci/resource.rb b/app/models/ci/resource.rb
deleted file mode 100644
index ee5b6546165..00000000000
--- a/app/models/ci/resource.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-
-module Ci
- class Resource < ApplicationRecord
- extend Gitlab::Ci::Model
-
- belongs_to :resource_group, class_name: 'Ci::ResourceGroup', inverse_of: :resources
- belongs_to :build, class_name: 'Ci::Build', inverse_of: :resource
-
- scope :free, -> { where(build: nil) }
- scope :retained_by, -> (build) { where(build: build) }
- end
-end
diff --git a/app/models/ci/resource_group.rb b/app/models/ci/resource_group.rb
deleted file mode 100644
index fb562783d3d..00000000000
--- a/app/models/ci/resource_group.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-module Ci
- class ResourceGroup < ApplicationRecord
- extend Gitlab::Ci::Model
-
- belongs_to :project, inverse_of: :resource_groups
-
- has_many :resources, class_name: 'Ci::Resource', inverse_of: :resource_group
- has_many :builds, class_name: 'Ci::Build', inverse_of: :resource_group
-
- validates :key,
- length: { maximum: 255 },
- format: { with: Gitlab::Regex.environment_name_regex,
- message: Gitlab::Regex.environment_name_regex_message }
-
- before_create :ensure_resource
-
- def retain_resource_for(build)
- resources.free.limit(1).update_all(build_id: build.id) > 0
- end
-
- def release_resource_from(build)
- resources.retained_by(build).update_all(build_id: nil) > 0
- end
-
- private
-
- def ensure_resource
- # Currently we only support one resource per group, which means
- # maximum one build can be set to the resource group, thus builds
- # belong to the same resource group are executed once at time.
- self.resources.build if self.resources.empty?
- end
- end
-end
diff --git a/app/models/project.rb b/app/models/project.rb
index bc7aebee9e6..5ed47032dab 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -285,7 +285,6 @@ class Project < ApplicationRecord
has_many :pipeline_schedules, class_name: 'Ci::PipelineSchedule'
has_many :project_deploy_tokens
has_many :deploy_tokens, through: :project_deploy_tokens
- has_many :resource_groups, class_name: 'Ci::ResourceGroup', inverse_of: :project
has_one :auto_devops, class_name: 'ProjectAutoDevops', inverse_of: :project, autosave: true
has_many :custom_attributes, class_name: 'ProjectCustomAttribute'
@@ -741,7 +740,7 @@ class Project < ApplicationRecord
end
def unlink_forks_upon_visibility_decrease_enabled?
- Feature.enabled?(:unlink_fork_network_upon_visibility_decrease, self)
+ Feature.enabled?(:unlink_fork_network_upon_visibility_decrease, self, default_enabled: true)
end
def empty_repo?
diff --git a/app/models/project_services/chat_message/merge_message.rb b/app/models/project_services/chat_message/merge_message.rb
index 46313ba7bec..dc62a4c8908 100644
--- a/app/models/project_services/chat_message/merge_message.rb
+++ b/app/models/project_services/chat_message/merge_message.rb
@@ -62,7 +62,7 @@ module ChatMessage
end
def merge_request_url
- "#{project_url}/merge_requests/#{merge_request_iid}"
+ "#{project_url}/-/merge_requests/#{merge_request_iid}"
end
# overridden in EE
diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
index 019bd54f48c..c92e8ecb31c 100644
--- a/app/models/project_services/hipchat_service.rb
+++ b/app/models/project_services/hipchat_service.rb
@@ -184,7 +184,7 @@ class HipchatService < Service
description = obj_attr[:description]
title = render_line(obj_attr[:title])
- merge_request_url = "#{project_url}/merge_requests/#{merge_request_id}"
+ merge_request_url = "#{project_url}/-/merge_requests/#{merge_request_id}"
merge_request_link = "<a href=\"#{merge_request_url}\">merge request !#{merge_request_id}</a>"
message = ["#{user_name} #{state} #{merge_request_link} in " \
"#{project_link}: <b>#{title}</b>"]
diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb
index e1efd84e510..d092a2de882 100644
--- a/app/policies/user_policy.rb
+++ b/app/policies/user_policy.rb
@@ -10,6 +10,9 @@ class UserPolicy < BasePolicy
desc "The profile is private"
condition(:private_profile, scope: :subject, score: 0) { @subject.private_profile? }
+ desc "The user is blocked"
+ condition(:blocked_user, scope: :subject, score: 0) { @subject.blocked? }
+
rule { ~restricted_public_level }.enable :read_user
rule { ~anonymous }.enable :read_user
@@ -20,5 +23,5 @@ class UserPolicy < BasePolicy
end
rule { default }.enable :read_user_profile
- rule { private_profile & ~(user_is_self | admin) }.prevent :read_user_profile
+ rule { (private_profile | blocked_user) & ~(user_is_self | admin) }.prevent :read_user_profile
end
diff --git a/app/presenters/snippet_presenter.rb b/app/presenters/snippet_presenter.rb
index 37c9ebd3305..a453be18b95 100644
--- a/app/presenters/snippet_presenter.rb
+++ b/app/presenters/snippet_presenter.rb
@@ -23,6 +23,10 @@ class SnippetPresenter < Gitlab::View::Presenter::Delegated
can_access_resource?("admin")
end
+ def can_report_as_spam?
+ snippet.submittable_as_spam_by?(current_user)
+ end
+
private
def can_access_resource?(ability_prefix)
diff --git a/app/services/ci/retry_build_service.rb b/app/services/ci/retry_build_service.rb
index 5abfbd26641..7a5e33c61ba 100644
--- a/app/services/ci/retry_build_service.rb
+++ b/app/services/ci/retry_build_service.rb
@@ -5,7 +5,7 @@ module Ci
CLONE_ACCESSORS = %i[pipeline project ref tag options name
allow_failure stage stage_id stage_idx trigger_request
yaml_variables when environment coverage_regex
- description tag_list protected needs resource_group].freeze
+ description tag_list protected needs].freeze
def execute(build)
reprocess!(build).tap do |new_build|
diff --git a/app/views/users/_profile_basic_info.html.haml b/app/views/users/_profile_basic_info.html.haml
new file mode 100644
index 00000000000..af0a766bab0
--- /dev/null
+++ b/app/views/users/_profile_basic_info.html.haml
@@ -0,0 +1,6 @@
+%p
+ %span.middle-dot-divider
+ @#{@user.username}
+ - if can?(current_user, :read_user_profile, @user)
+ %span.middle-dot-divider
+ = s_('Member since %{date}') % { date: @user.created_at.to_date.to_s(:long) }
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index e1c75d5d0f4..e10dad8aa8d 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,7 +1,7 @@
- @hide_top_links = true
- @hide_breadcrumbs = true
- @no_container = true
-- page_title @user.name
+- page_title @user.blocked? ? s_('UserProfile|Blocked user') : @user.name
- page_description @user.bio
- header_title @user.name, user_path(@user)
@@ -36,50 +36,48 @@
= link_to avatar_icon_for_user(@user, 400), target: '_blank', rel: 'noopener noreferrer' do
= image_tag avatar_icon_for_user(@user, 90), class: "avatar s90", alt: ''
- .user-info
- .cover-title
- = @user.name
-
- - if @user.status
- .cover-status
- = emoji_icon(@user.status.emoji)
- = markdown_field(@user.status, :message)
-
- .cover-desc.member-date.cgray
- %p
- %span.middle-dot-divider
- @#{@user.username}
- - if can?(current_user, :read_user_profile, @user)
- %span.middle-dot-divider
- = s_('Member since %{date}') % { date: @user.created_at.to_date.to_s(:long) }
-
- .cover-desc.cgray
- - unless @user.public_email.blank?
- .profile-link-holder.middle-dot-divider
- = link_to @user.public_email, "mailto:#{@user.public_email}", class: 'text-link'
- - unless @user.skype.blank?
- .profile-link-holder.middle-dot-divider
- = link_to "skype:#{@user.skype}", title: "Skype" do
- = icon('skype')
- - unless @user.linkedin.blank?
- .profile-link-holder.middle-dot-divider
- = link_to linkedin_url(@user), title: "LinkedIn", target: '_blank', rel: 'noopener noreferrer nofollow' do
- = icon('linkedin-square')
- - unless @user.twitter.blank?
- .profile-link-holder.middle-dot-divider
- = link_to twitter_url(@user), title: "Twitter", target: '_blank', rel: 'noopener noreferrer nofollow' do
- = icon('twitter-square')
- - unless @user.website_url.blank?
- .profile-link-holder.middle-dot-divider
- = link_to @user.short_website_url, @user.full_website_url, class: 'text-link', target: '_blank', rel: 'me noopener noreferrer nofollow'
- - unless @user.location.blank?
- .profile-link-holder.middle-dot-divider
- = sprite_icon('location', size: 16, css_class: 'vertical-align-sub')
- = @user.location
- - unless @user.organization.blank?
- .profile-link-holder.middle-dot-divider
- = sprite_icon('work', size: 16, css_class: 'vertical-align-sub')
- = @user.organization
+ - if @user.blocked?
+ .user-info
+ .cover-title
+ = s_('UserProfile|Blocked user')
+ = render "users/profile_basic_info"
+ - else
+ .user-info
+ .cover-title
+ = @user.name
+
+ - if @user.status
+ .cover-status
+ = emoji_icon(@user.status.emoji)
+ = markdown_field(@user.status, :message)
+ = render "users/profile_basic_info"
+ .cover-desc.cgray
+ - unless @user.public_email.blank?
+ .profile-link-holder.middle-dot-divider
+ = link_to @user.public_email, "mailto:#{@user.public_email}", class: 'text-link'
+ - unless @user.skype.blank?
+ .profile-link-holder.middle-dot-divider
+ = link_to "skype:#{@user.skype}", title: "Skype" do
+ = icon('skype')
+ - unless @user.linkedin.blank?
+ .profile-link-holder.middle-dot-divider
+ = link_to linkedin_url(@user), title: "LinkedIn", target: '_blank', rel: 'noopener noreferrer nofollow' do
+ = icon('linkedin-square')
+ - unless @user.twitter.blank?
+ .profile-link-holder.middle-dot-divider
+ = link_to twitter_url(@user), title: "Twitter", target: '_blank', rel: 'noopener noreferrer nofollow' do
+ = icon('twitter-square')
+ - unless @user.website_url.blank?
+ .profile-link-holder.middle-dot-divider
+ = link_to @user.short_website_url, @user.full_website_url, class: 'text-link', target: '_blank', rel: 'me noopener noreferrer nofollow'
+ - unless @user.location.blank?
+ .profile-link-holder.middle-dot-divider
+ = sprite_icon('location', size: 16, css_class: 'vertical-align-sub')
+ = @user.location
+ - unless @user.organization.blank?
+ .profile-link-holder.middle-dot-divider
+ = sprite_icon('work', size: 16, css_class: 'vertical-align-sub')
+ = @user.organization
- if @user.bio.present?
.cover-desc.cgray
@@ -165,4 +163,8 @@
.col-12.text-center
.text-content
%h4
- = s_('UserProfile|This user has a private profile')
+ - if @user.blocked?
+ = s_('UserProfile|This user is blocked')
+ - else
+ = s_('UserProfile|This user has a private profile')
+
diff --git a/changelogs/unreleased/35122-sentry-error-search-pagination-implement-pagination-in-sentry-error.yml b/changelogs/unreleased/35122-sentry-error-search-pagination-implement-pagination-in-sentry-error.yml
new file mode 100644
index 00000000000..326fd3bf66b
--- /dev/null
+++ b/changelogs/unreleased/35122-sentry-error-search-pagination-implement-pagination-in-sentry-error.yml
@@ -0,0 +1,5 @@
+---
+title: Implement pagination for sentry errors
+merge_request: 21136
+author:
+type: added
diff --git a/changelogs/unreleased/ci-resource-group-model.yml b/changelogs/unreleased/ci-resource-group-model.yml
deleted file mode 100644
index 98bc0159626..00000000000
--- a/changelogs/unreleased/ci-resource-group-model.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add Ci Resource Group models
-merge_request: 20950
-author:
-type: other
diff --git a/changelogs/unreleased/dz-move-project-routes.yml b/changelogs/unreleased/dz-move-project-routes.yml
new file mode 100644
index 00000000000..97029f23907
--- /dev/null
+++ b/changelogs/unreleased/dz-move-project-routes.yml
@@ -0,0 +1,5 @@
+---
+title: Move merge request routes under /-/ scope
+merge_request: 21126
+author:
+type: deprecated
diff --git a/changelogs/unreleased/fix-padding-design-comments.yml b/changelogs/unreleased/fix-padding-design-comments.yml
new file mode 100644
index 00000000000..e8422c7598a
--- /dev/null
+++ b/changelogs/unreleased/fix-padding-design-comments.yml
@@ -0,0 +1,5 @@
+---
+title: Fix padding on the design comments
+merge_request: 21839
+author:
+type: fixed
diff --git a/changelogs/unreleased/fj-add-report-permission-to-graphql-snippet-endpoint.yml b/changelogs/unreleased/fj-add-report-permission-to-graphql-snippet-endpoint.yml
new file mode 100644
index 00000000000..434ed0bb437
--- /dev/null
+++ b/changelogs/unreleased/fj-add-report-permission-to-graphql-snippet-endpoint.yml
@@ -0,0 +1,5 @@
+---
+title: Add reportSnippet permission to Snippet GraphQL
+merge_request: 21836
+author:
+type: other
diff --git a/changelogs/unreleased/issue_7105.yml b/changelogs/unreleased/issue_7105.yml
new file mode 100644
index 00000000000..b85ee761725
--- /dev/null
+++ b/changelogs/unreleased/issue_7105.yml
@@ -0,0 +1,5 @@
+---
+title: Allow PDF attachments to be opened on browser
+merge_request: 21272
+author:
+type: added
diff --git a/changelogs/unreleased/nicolasdular-allow-broadcast-message-styling.yml b/changelogs/unreleased/nicolasdular-allow-broadcast-message-styling.yml
new file mode 100644
index 00000000000..d9e683673fc
--- /dev/null
+++ b/changelogs/unreleased/nicolasdular-allow-broadcast-message-styling.yml
@@ -0,0 +1,5 @@
+---
+title: Allow styling broadcast messages
+merge_request: 21522
+author:
+type: added
diff --git a/changelogs/unreleased/xanf-blocked-profile-page.yml b/changelogs/unreleased/xanf-blocked-profile-page.yml
new file mode 100644
index 00000000000..0b2e54469ec
--- /dev/null
+++ b/changelogs/unreleased/xanf-blocked-profile-page.yml
@@ -0,0 +1,5 @@
+---
+title: Hide profile information when user is blocked
+merge_request: 21706
+author:
+type: added
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 1a5c58105b2..c29d673f315 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -260,6 +260,82 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
+ resources :merge_requests, concerns: :awardable, except: [:new, :create, :show], constraints: { id: /\d+/ } do
+ member do
+ get :show # Insert this first to ensure redirections using merge_requests#show match this route
+ get :commit_change_content
+ post :merge
+ post :cancel_auto_merge
+ get :pipeline_status
+ get :ci_environments_status
+ post :toggle_subscription
+ post :remove_wip
+ post :assign_related_issues
+ get :discussions, format: :json
+ post :rebase
+ get :test_reports
+ get :exposed_artifacts
+
+ scope constraints: ->(req) { req.format == :json }, as: :json do
+ get :commits
+ get :pipelines
+ get :diffs, to: 'merge_requests/diffs#show'
+ get :diffs_batch, to: 'merge_requests/diffs#diffs_batch'
+ get :diffs_metadata, to: 'merge_requests/diffs#diffs_metadata'
+ get :widget, to: 'merge_requests/content#widget'
+ get :cached_widget, to: 'merge_requests/content#cached_widget'
+ end
+
+ scope action: :show do
+ get :commits, defaults: { tab: 'commits' }
+ get :pipelines, defaults: { tab: 'pipelines' }
+ get :diffs, defaults: { tab: 'diffs' }
+ end
+
+ get :diff_for_path, controller: 'merge_requests/diffs'
+
+ scope controller: 'merge_requests/conflicts' do
+ get :conflicts, action: :show
+ get :conflict_for_path
+ post :resolve_conflicts
+ end
+ end
+
+ collection do
+ get :diff_for_path
+ post :bulk_update
+ end
+
+ resources :discussions, only: [:show], constraints: { id: /\h{40}/ } do
+ member do
+ post :resolve
+ delete :resolve, action: :unresolve
+ end
+ end
+ end
+
+ scope path: 'merge_requests', controller: 'merge_requests/creations' do
+ post '', action: :create, as: nil
+
+ scope path: 'new', as: :new_merge_request do
+ get '', action: :new
+
+ scope constraints: ->(req) { req.format == :json }, as: :json do
+ get :diffs
+ get :pipelines
+ end
+
+ scope action: :new do
+ get :diffs, defaults: { tab: 'diffs' }
+ get :pipelines, defaults: { tab: 'pipelines' }
+ end
+
+ get :diff_for_path
+ get :branch_from
+ get :branch_to
+ end
+ end
+
# The wiki routing contains wildcard characters so
# its preferable to keep it below all other project routes
draw :wiki
@@ -317,82 +393,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
- resources :merge_requests, concerns: :awardable, except: [:new, :create, :show], constraints: { id: /\d+/ } do
- member do
- get :show # Insert this first to ensure redirections using merge_requests#show match this route
- get :commit_change_content
- post :merge
- post :cancel_auto_merge
- get :pipeline_status
- get :ci_environments_status
- post :toggle_subscription
- post :remove_wip
- post :assign_related_issues
- get :discussions, format: :json
- post :rebase
- get :test_reports
- get :exposed_artifacts
-
- scope constraints: ->(req) { req.format == :json }, as: :json do
- get :commits
- get :pipelines
- get :diffs, to: 'merge_requests/diffs#show'
- get :diffs_batch, to: 'merge_requests/diffs#diffs_batch'
- get :diffs_metadata, to: 'merge_requests/diffs#diffs_metadata'
- get :widget, to: 'merge_requests/content#widget'
- get :cached_widget, to: 'merge_requests/content#cached_widget'
- end
-
- scope action: :show do
- get :commits, defaults: { tab: 'commits' }
- get :pipelines, defaults: { tab: 'pipelines' }
- get :diffs, defaults: { tab: 'diffs' }
- end
-
- get :diff_for_path, controller: 'merge_requests/diffs'
-
- scope controller: 'merge_requests/conflicts' do
- get :conflicts, action: :show
- get :conflict_for_path
- post :resolve_conflicts
- end
- end
-
- collection do
- get :diff_for_path
- post :bulk_update
- end
-
- resources :discussions, only: [:show], constraints: { id: /\h{40}/ } do
- member do
- post :resolve
- delete :resolve, action: :unresolve
- end
- end
- end
-
- scope path: 'merge_requests', controller: 'merge_requests/creations' do
- post '', action: :create, as: nil
-
- scope path: 'new', as: :new_merge_request do
- get '', action: :new
-
- scope constraints: ->(req) { req.format == :json }, as: :json do
- get :diffs
- get :pipelines
- end
-
- scope action: :new do
- get :diffs, defaults: { tab: 'diffs' }
- get :pipelines, defaults: { tab: 'pipelines' }
- end
-
- get :diff_for_path
- get :branch_from
- get :branch_to
- end
- end
-
resources :pipelines, only: [:index, :new, :create, :show] do
collection do
resource :pipelines_settings, path: 'settings', only: [:show, :update]
@@ -546,7 +546,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
:forks, :group_links, :import, :avatar, :mirror,
:cycle_analytics, :mattermost, :variables, :triggers,
:environments, :protected_environments, :error_tracking,
- :serverless, :clusters, :audit_events, :wikis)
+ :serverless, :clusters, :audit_events, :wikis, :merge_requests)
end
# rubocop: disable Cop/PutProjectRoutesUnderScope
diff --git a/db/migrate/20191128145231_add_ci_resource_groups.rb b/db/migrate/20191128145231_add_ci_resource_groups.rb
deleted file mode 100644
index 0b730e47dda..00000000000
--- a/db/migrate/20191128145231_add_ci_resource_groups.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# frozen_string_literal: true
-
-class AddCiResourceGroups < ActiveRecord::Migration[5.2]
- DOWNTIME = false
-
- def change
- create_table :ci_resource_groups do |t|
- t.timestamps_with_timezone
- t.references :project, null: false, index: false, foreign_key: { on_delete: :cascade }
- t.string :key, null: false, limit: 255
- t.index %i[project_id key], unique: true
- end
-
- create_table :ci_resources do |t|
- t.timestamps_with_timezone
- t.references :resource_group, null: false, index: false, foreign_key: { to_table: :ci_resource_groups, on_delete: :cascade }
- t.references :build, null: true, index: true, foreign_key: { to_table: :ci_builds, on_delete: :nullify }
- t.index %i[resource_group_id build_id], unique: true
- end
-
- add_column :ci_builds, :resource_group_id, :bigint
- add_column :ci_builds, :waiting_for_resource_at, :datetime_with_timezone
- end
-end
diff --git a/db/migrate/20191129144631_add_index_to_resource_group_id.rb b/db/migrate/20191129144631_add_index_to_resource_group_id.rb
deleted file mode 100644
index 0e5a84f094d..00000000000
--- a/db/migrate/20191129144631_add_index_to_resource_group_id.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-class AddIndexToResourceGroupId < ActiveRecord::Migration[5.2]
- include Gitlab::Database::MigrationHelpers
-
- DOWNTIME = false
- INDEX_NAME = 'index_for_resource_group'.freeze
-
- disable_ddl_transaction!
-
- def up
- add_concurrent_index :ci_builds, %i[resource_group_id id], where: 'resource_group_id IS NOT NULL', name: INDEX_NAME
- add_concurrent_foreign_key :ci_builds, :ci_resource_groups, column: :resource_group_id, on_delete: :nullify
- end
-
- def down
- remove_foreign_key_if_exists :ci_builds, column: :resource_group_id
- remove_concurrent_index_by_name :ci_builds, INDEX_NAME
- end
-end
diff --git a/db/schema.rb b/db/schema.rb
index 88c824cb708..ede50e7ed06 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -684,8 +684,6 @@ ActiveRecord::Schema.define(version: 2019_12_08_071112) do
t.datetime_with_timezone "scheduled_at"
t.string "token_encrypted"
t.integer "upstream_pipeline_id"
- t.bigint "resource_group_id"
- t.datetime_with_timezone "waiting_for_resource_at"
t.index ["artifacts_expire_at"], name: "index_ci_builds_on_artifacts_expire_at", where: "(artifacts_file <> ''::text)"
t.index ["auto_canceled_by_id"], name: "index_ci_builds_on_auto_canceled_by_id"
t.index ["commit_id", "artifacts_expire_at", "id"], name: "index_ci_builds_on_commit_id_and_artifacts_expireatandidpartial", where: "(((type)::text = 'Ci::Build'::text) AND ((retried = false) OR (retried IS NULL)) AND ((name)::text = ANY (ARRAY[('sast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('sast:container'::character varying)::text, ('container_scanning'::character varying)::text, ('dast'::character varying)::text])))"
@@ -699,7 +697,6 @@ ActiveRecord::Schema.define(version: 2019_12_08_071112) do
t.index ["project_id"], name: "index_ci_builds_on_project_id_for_successfull_pages_deploy", where: "(((type)::text = 'GenericCommitStatus'::text) AND ((stage)::text = 'deploy'::text) AND ((name)::text = 'pages:deploy'::text) AND ((status)::text = 'success'::text))"
t.index ["protected"], name: "index_ci_builds_on_protected"
t.index ["queued_at"], name: "index_ci_builds_on_queued_at"
- t.index ["resource_group_id", "id"], name: "index_for_resource_group", where: "(resource_group_id IS NOT NULL)"
t.index ["runner_id"], name: "index_ci_builds_on_runner_id"
t.index ["scheduled_at"], name: "partial_index_ci_builds_on_scheduled_at_with_scheduled_jobs", where: "((scheduled_at IS NOT NULL) AND ((type)::text = 'Ci::Build'::text) AND ((status)::text = 'scheduled'::text))"
t.index ["stage_id", "stage_idx"], name: "tmp_build_stage_position_index", where: "(stage_idx IS NOT NULL)"
@@ -874,23 +871,6 @@ ActiveRecord::Schema.define(version: 2019_12_08_071112) do
t.index ["user_id"], name: "index_ci_pipelines_on_user_id"
end
- create_table "ci_resource_groups", force: :cascade do |t|
- t.datetime_with_timezone "created_at", null: false
- t.datetime_with_timezone "updated_at", null: false
- t.bigint "project_id", null: false
- t.string "key", limit: 255, null: false
- t.index ["project_id", "key"], name: "index_ci_resource_groups_on_project_id_and_key", unique: true
- end
-
- create_table "ci_resources", force: :cascade do |t|
- t.datetime_with_timezone "created_at", null: false
- t.datetime_with_timezone "updated_at", null: false
- t.bigint "resource_group_id", null: false
- t.bigint "build_id"
- t.index ["build_id"], name: "index_ci_resources_on_build_id"
- t.index ["resource_group_id", "build_id"], name: "index_ci_resources_on_resource_group_id_and_build_id", unique: true
- end
-
create_table "ci_runner_namespaces", id: :serial, force: :cascade do |t|
t.integer "runner_id"
t.integer "namespace_id"
@@ -4409,7 +4389,6 @@ ActiveRecord::Schema.define(version: 2019_12_08_071112) do
add_foreign_key "ci_builds", "ci_pipelines", column: "auto_canceled_by_id", name: "fk_a2141b1522", on_delete: :nullify
add_foreign_key "ci_builds", "ci_pipelines", column: "commit_id", name: "fk_d3130c9a7f", on_delete: :cascade
add_foreign_key "ci_builds", "ci_pipelines", column: "upstream_pipeline_id", name: "fk_87f4cefcda", on_delete: :cascade
- add_foreign_key "ci_builds", "ci_resource_groups", column: "resource_group_id", name: "fk_6661f4f0e8", on_delete: :nullify
add_foreign_key "ci_builds", "ci_stages", column: "stage_id", name: "fk_3a9eaa254d", on_delete: :cascade
add_foreign_key "ci_builds", "projects", name: "fk_befce0568a", on_delete: :cascade
add_foreign_key "ci_builds_metadata", "ci_builds", column: "build_id", on_delete: :cascade
@@ -4430,9 +4409,6 @@ ActiveRecord::Schema.define(version: 2019_12_08_071112) do
add_foreign_key "ci_pipelines", "external_pull_requests", name: "fk_190998ef09", on_delete: :nullify
add_foreign_key "ci_pipelines", "merge_requests", name: "fk_a23be95014", on_delete: :cascade
add_foreign_key "ci_pipelines", "projects", name: "fk_86635dbd80", on_delete: :cascade
- add_foreign_key "ci_resource_groups", "projects", on_delete: :cascade
- add_foreign_key "ci_resources", "ci_builds", column: "build_id", on_delete: :nullify
- add_foreign_key "ci_resources", "ci_resource_groups", column: "resource_group_id", on_delete: :cascade
add_foreign_key "ci_runner_namespaces", "ci_runners", column: "runner_id", on_delete: :cascade
add_foreign_key "ci_runner_namespaces", "namespaces", on_delete: :cascade
add_foreign_key "ci_runner_projects", "projects", name: "fk_4478a6f1e4", on_delete: :cascade
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index 3cbc489f464..484841fd712 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -2899,6 +2899,51 @@ type IssueSetDueDatePayload {
}
"""
+Autogenerated input type of IssueSetWeight
+"""
+input IssueSetWeightInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The iid of the issue to mutate
+ """
+ iid: String!
+
+ """
+ The project the issue to mutate is in
+ """
+ projectPath: ID!
+
+ """
+ The desired weight for the issue
+ """
+ weight: Int!
+}
+
+"""
+Autogenerated return type of IssueSetWeight
+"""
+type IssueSetWeightPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Reasons why the mutation failed.
+ """
+ errors: [String!]!
+
+ """
+ The issue after mutation
+ """
+ issue: Issue
+}
+
+"""
Values for sorting issues
"""
enum IssueSort {
@@ -3895,6 +3940,7 @@ type Mutation {
epicTreeReorder(input: EpicTreeReorderInput!): EpicTreeReorderPayload
issueSetConfidential(input: IssueSetConfidentialInput!): IssueSetConfidentialPayload
issueSetDueDate(input: IssueSetDueDateInput!): IssueSetDueDatePayload
+ issueSetWeight(input: IssueSetWeightInput!): IssueSetWeightPayload
mergeRequestSetAssignees(input: MergeRequestSetAssigneesInput!): MergeRequestSetAssigneesPayload
mergeRequestSetLabels(input: MergeRequestSetLabelsInput!): MergeRequestSetLabelsPayload
mergeRequestSetLocked(input: MergeRequestSetLockedInput!): MergeRequestSetLockedPayload
@@ -5644,6 +5690,11 @@ type SnippetPermissions {
readSnippet: Boolean!
"""
+ Whether or not a user can perform `report_snippet` on this resource
+ """
+ reportSnippet: Boolean!
+
+ """
Whether or not a user can perform `update_snippet` on this resource
"""
updateSnippet: Boolean!
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index e47e2c8056c..f3437a26f42 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -6803,6 +6803,24 @@
"deprecationReason": null
},
{
+ "name": "reportSnippet",
+ "description": "Whether or not a user can perform `report_snippet` on this resource",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "updateSnippet",
"description": "Whether or not a user can perform `update_snippet` on this resource",
"args": [
@@ -16077,6 +16095,33 @@
"deprecationReason": null
},
{
+ "name": "issueSetWeight",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "IssueSetWeightInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "IssueSetWeightPayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "mergeRequestSetAssignees",
"description": null,
"args": [
@@ -19943,6 +19988,136 @@
},
{
"kind": "OBJECT",
+ "name": "IssueSetWeightPayload",
+ "description": "Autogenerated return type of IssueSetWeight",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Reasons why the mutation failed.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "issue",
+ "description": "The issue after mutation",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "Issue",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
+ "name": "IssueSetWeightInput",
+ "description": "Autogenerated input type of IssueSetWeight",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "projectPath",
+ "description": "The project the issue to mutate is in",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "iid",
+ "description": "The iid of the issue to mutate",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "weight",
+ "description": "The desired weight for the issue",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
"name": "EpicTreeReorderPayload",
"description": "Autogenerated return type of EpicTreeReorder",
"fields": [
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 9ddbd08f32b..1371daa6453 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -410,6 +410,14 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph
| `errors` | String! => Array | Reasons why the mutation failed. |
| `issue` | Issue | The issue after mutation |
+### IssueSetWeightPayload
+
+| Name | Type | Description |
+| --- | ---- | ---------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `errors` | String! => Array | Reasons why the mutation failed. |
+| `issue` | Issue | The issue after mutation |
+
### Label
| Name | Type | Description |
@@ -834,6 +842,7 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph
| `readSnippet` | Boolean! | Whether or not a user can perform `read_snippet` on this resource |
| `updateSnippet` | Boolean! | Whether or not a user can perform `update_snippet` on this resource |
| `adminSnippet` | Boolean! | Whether or not a user can perform `admin_snippet` on this resource |
+| `reportSnippet` | Boolean! | Whether or not a user can perform `report_snippet` on this resource |
### Submodule
diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md
index 81aff05a32a..cfb12acb83b 100644
--- a/doc/user/project/clusters/serverless/index.md
+++ b/doc/user/project/clusters/serverless/index.md
@@ -863,3 +863,21 @@ The instructions below relate to installing and running Certbot on a Linux serve
After your changes are running on your Knative cluster, you can begin using the HTTPS protocol for secure access your deployed Knative services.
In the event a mistake is made during this process and you need to update the cert, you will need to edit the gateway `knative-ingress-gateway`
to switch back to `PASSTHROUGH` mode. Once corrections are made, edit the file again so the gateway will use the new certificates.
+
+## Using an older version of `gitlabktl`
+
+There may be situations where you want to run an older version of `gitlabktl`. This
+requires setting an older version of the `gitlabktl` image in the `.gitlab-ci.yml file.`
+
+To set an older version, add `image:` to the `functions:deploy` block. For example:
+
+```yaml
+functions:deploy:
+ extends: .serverless:deploy:functions
+ environment: production
+ image: registry.gitlab.com/gitlab-org/gitlabktl:0.5.0
+```
+
+Different versions are available by changing the version tag at the end of the registry URL: `registry.gitlab.com/gitlab-org/gitlabktl:VERSION`
+
+For a full inventory of available `gitlabktl` versions please see [this list](https://gitlab.com/gitlab-org/gitlabktl/container_registry)
diff --git a/lib/banzai/filter/base_sanitization_filter.rb b/lib/banzai/filter/base_sanitization_filter.rb
index 2dabca3552d..1b7af8aee45 100644
--- a/lib/banzai/filter/base_sanitization_filter.rb
+++ b/lib/banzai/filter/base_sanitization_filter.rb
@@ -7,6 +7,7 @@ module Banzai
#
# - Banzai::Filter::SanitizationFilter (Markdown)
# - Banzai::Filter::AsciiDocSanitizationFilter (AsciiDoc/Asciidoctor)
+ # - Banzai::Filter::BroadcastMessageSanitizationFilter (Markdown with styled links and line breaks)
#
# Extends HTML::Pipeline::SanitizationFilter with common rules.
class BaseSanitizationFilter < HTML::Pipeline::SanitizationFilter
diff --git a/lib/banzai/filter/broadcast_message_sanitization_filter.rb b/lib/banzai/filter/broadcast_message_sanitization_filter.rb
new file mode 100644
index 00000000000..042293170c8
--- /dev/null
+++ b/lib/banzai/filter/broadcast_message_sanitization_filter.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Banzai
+ module Filter
+ # Sanitize HTML produced by Markdown. Allows styling of links and usage of line breaks.
+ #
+ # Extends Banzai::Filter::BaseSanitizationFilter with specific rules.
+ class BroadcastMessageSanitizationFilter < Banzai::Filter::BaseSanitizationFilter
+ def customize_whitelist(whitelist)
+ whitelist[:elements].push('br')
+
+ whitelist[:attributes]['a'].push('class', 'style')
+
+ whitelist[:css] = { properties: %w(color border background padding margin text-decoration) }
+
+ whitelist
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/broadcast_message_pipeline.rb b/lib/banzai/pipeline/broadcast_message_pipeline.rb
index 580b5b72474..e31795e673c 100644
--- a/lib/banzai/pipeline/broadcast_message_pipeline.rb
+++ b/lib/banzai/pipeline/broadcast_message_pipeline.rb
@@ -6,7 +6,7 @@ module Banzai
def self.filters
@filters ||= FilterArray[
Filter::MarkdownFilter,
- Filter::SanitizationFilter,
+ Filter::BroadcastMessageSanitizationFilter,
Filter::EmojiFilter,
Filter::ColorFilter,
diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb
index 5164223876a..517caabc79e 100644
--- a/lib/gitlab/ci/config/entry/job.rb
+++ b/lib/gitlab/ci/config/entry/job.rb
@@ -16,8 +16,7 @@ module Gitlab
ALLOWED_KEYS = %i[tags script only except rules type image services
allow_failure type stage when start_in artifacts cache
dependencies before_script needs after_script variables
- environment coverage retry parallel extends interruptible timeout
- resource_group].freeze
+ environment coverage retry parallel extends interruptible timeout].freeze
REQUIRED_BY_NEEDS = %i[stage].freeze
@@ -49,7 +48,6 @@ module Gitlab
validates :dependencies, array_of_strings: true
validates :extends, array_of_strings_or_string: true
validates :rules, array_of_hashes: true
- validates :resource_group, type: String
end
validates :start_in, duration: { limit: '1 week' }, if: :delayed?
@@ -158,7 +156,7 @@ module Gitlab
attributes :script, :tags, :allow_failure, :when, :dependencies,
:needs, :retry, :parallel, :extends, :start_in, :rules,
- :interruptible, :timeout, :resource_group
+ :interruptible, :timeout
def self.matching?(name, config)
!name.to_s.start_with?('.') &&
@@ -245,8 +243,7 @@ module Gitlab
artifacts: artifacts_value,
after_script: after_script_value,
ignore: ignored?,
- needs: needs_defined? ? needs_value : nil,
- resource_group: resource_group }
+ needs: needs_defined? ? needs_value : nil }
end
end
end
diff --git a/lib/gitlab/ci/pipeline/chain/validate/external.rb b/lib/gitlab/ci/pipeline/chain/validate/external.rb
index 97af42b5fd6..44dc333a6a1 100644
--- a/lib/gitlab/ci/pipeline/chain/validate/external.rb
+++ b/lib/gitlab/ci/pipeline/chain/validate/external.rb
@@ -38,7 +38,7 @@ module Gitlab
raise InvalidResponseCode, "Unsupported response code received from Validation Service: #{response_code}"
end
rescue => ex
- Gitlab::Sentry.track_exception(ex)
+ Gitlab::ErrorTracking.track_exception(ex)
true
end
diff --git a/lib/gitlab/ci/pipeline/seed/build.rb b/lib/gitlab/ci/pipeline/seed/build.rb
index 98b4b4593e0..590c7f4d1dd 100644
--- a/lib/gitlab/ci/pipeline/seed/build.rb
+++ b/lib/gitlab/ci/pipeline/seed/build.rb
@@ -18,7 +18,6 @@ module Gitlab
@seed_attributes = attributes
@previous_stages = previous_stages
@needs_attributes = dig(:needs_attributes)
- @resource_group_key = attributes.delete(:resource_group_key)
@using_rules = attributes.key?(:rules)
@using_only = attributes.key?(:only)
@@ -79,7 +78,6 @@ module Gitlab
else
::Ci::Build.new(attributes).tap do |job|
job.deployment = Seed::Deployment.new(job).to_resource
- job.resource_group = Seed::Build::ResourceGroup.new(job, @resource_group_key).to_resource
end
end
end
diff --git a/lib/gitlab/ci/pipeline/seed/build/resource_group.rb b/lib/gitlab/ci/pipeline/seed/build/resource_group.rb
deleted file mode 100644
index 100eb1d4084..00000000000
--- a/lib/gitlab/ci/pipeline/seed/build/resource_group.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Ci
- module Pipeline
- module Seed
- class Build
- class ResourceGroup < Seed::Base
- include Gitlab::Utils::StrongMemoize
-
- attr_reader :build, :resource_group_key
-
- def initialize(build, resource_group_key)
- @build = build
- @resource_group_key = resource_group_key
- end
-
- def to_resource
- return unless Feature.enabled?(:ci_resource_group, build.project)
- return unless resource_group_key.present?
-
- resource_group = build.project.resource_groups
- .safe_find_or_create_by(key: expanded_resource_group_key)
-
- resource_group if resource_group.persisted?
- end
-
- private
-
- def expanded_resource_group_key
- strong_memoize(:expanded_resource_group_key) do
- ExpandVariables.expand(resource_group_key, -> { build.simple_variables })
- end
- end
- end
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/ci/yaml_processor.rb b/lib/gitlab/ci/yaml_processor.rb
index 7f3f518dfd7..8ac864eb5cf 100644
--- a/lib/gitlab/ci/yaml_processor.rb
+++ b/lib/gitlab/ci/yaml_processor.rb
@@ -64,7 +64,6 @@ module Gitlab
except: job[:except],
rules: job[:rules],
cache: job[:cache],
- resource_group_key: job[:resource_group],
options: {
image: job[:image],
services: job[:services],
diff --git a/lib/gitlab/file_type_detection.rb b/lib/gitlab/file_type_detection.rb
index ca78d49f99b..e052792675a 100644
--- a/lib/gitlab/file_type_detection.rb
+++ b/lib/gitlab/file_type_detection.rb
@@ -20,6 +20,7 @@
module Gitlab
module FileTypeDetection
SAFE_IMAGE_EXT = %w[png jpg jpeg gif bmp tiff ico].freeze
+ PDF_EXT = 'pdf'
# We recommend using the .mp4 format over .mov. Videos in .mov format can
# still be used but you really need to make sure they are served with the
# proper MIME type video/mp4 and not video/quicktime or your videos won't play
@@ -46,6 +47,10 @@ module Gitlab
extension_match?(SAFE_AUDIO_EXT)
end
+ def pdf?
+ extension_match?([PDF_EXT])
+ end
+
def embeddable?
image? || video? || audio?
end
diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml
index f822e931bdb..2fd4f18b756 100644
--- a/lib/gitlab/import_export/import_export.yml
+++ b/lib/gitlab/import_export/import_export.yml
@@ -203,8 +203,6 @@ excluded_attributes:
- :artifacts_metadata_store
- :artifacts_size
- :commands
- - :resource_group_id
- - :waiting_for_resource_at
push_event_payload:
- :event_id
project_badges:
diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb
index 5d907300d68..1438a7db001 100644
--- a/lib/gitlab/import_export/relation_factory.rb
+++ b/lib/gitlab/import_export/relation_factory.rb
@@ -38,12 +38,12 @@ module Gitlab
IMPORTED_OBJECT_MAX_RETRIES = 5.freeze
- EXISTING_OBJECT_CHECK = %i[milestone milestones label labels project_label project_labels group_label group_labels project_feature merge_request ProjectCiCdSetting].freeze
+ EXISTING_OBJECT_CHECK = %i[milestone milestones label labels project_label project_labels group_label group_labels project_feature merge_request ProjectCiCdSetting container_expiration_policy].freeze
TOKEN_RESET_MODELS = %i[Project Namespace Ci::Trigger Ci::Build Ci::Runner ProjectHook].freeze
# This represents all relations that have unique key on `project_id`
- UNIQUE_RELATIONS = %i[project_feature ProjectCiCdSetting].freeze
+ UNIQUE_RELATIONS = %i[project_feature ProjectCiCdSetting container_expiration_policy].freeze
def self.create(*args)
new(*args).create
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index f25810f23c8..d6e15fba348 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -19625,6 +19625,9 @@ msgstr ""
msgid "UserProfile|Already reported for abuse"
msgstr ""
+msgid "UserProfile|Blocked user"
+msgstr ""
+
msgid "UserProfile|Contributed projects"
msgstr ""
@@ -19685,6 +19688,9 @@ msgstr ""
msgid "UserProfile|This user hasn't starred any projects"
msgstr ""
+msgid "UserProfile|This user is blocked"
+msgstr ""
+
msgid "UserProfile|View all"
msgstr ""
diff --git a/scripts/frontend/check_no_partial_karma_jest.sh b/scripts/frontend/check_no_partial_karma_jest.sh
new file mode 100755
index 00000000000..0d0c897bb18
--- /dev/null
+++ b/scripts/frontend/check_no_partial_karma_jest.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+
+karma_files=$(find spec/javascripts ee/spec/javascripts -type f -name '*_spec.js' -not -path '*/helpers/*')
+violations=""
+
+for karma_file in $karma_files; do
+ jest_file=${karma_file/spec\/javascripts/"spec/frontend"}
+
+ if [ -f $jest_file ]; then
+ violations="$violations $jest_file"
+ fi
+done
+
+if [[ -z "$violations" ]]; then
+ echo "All good!"
+ exit 0
+else
+ echo "Danger! The following Jest specs have corresponding files in the Karma spec directory (i.e. spec/javascripts):"
+ echo ""
+ echo "------------------------------"
+ for file in $violations; do
+ echo $file
+ done
+ echo "------------------------------"
+ echo ""
+ echo "For each of these files, please either:"
+ echo ""
+ echo "1. Fully migrate the file to Jest and remove the corresponding Karma file."
+ echo "2. Remove the Jest file for now, make any relevant changes in the corresponding Karma file, and handle the migration to Jest in a separate MR."
+ echo ""
+ echo "Why is this a problem?"
+ echo ""
+ echo "- It's nice to have a single source of truth for the unit tests of a subject."
+ echo "- This will cause conflicts if the remaining Karma spec is migrated using our automated tool."
+ echo " https://gitlab.com/gitlab-org/frontend/playground/migrate-karma-to-jest"
+ echo ""
+ exit 1
+fi
diff --git a/scripts/static-analysis b/scripts/static-analysis
index b7f7100c365..1392a4f6a23 100755
--- a/scripts/static-analysis
+++ b/scripts/static-analysis
@@ -35,7 +35,8 @@ def jobs_to_run(node_index, node_total)
%w[yarn run prettier-all],
%w[bundle exec rubocop --parallel],
%w[scripts/lint-conflicts.sh],
- %w[scripts/lint-rugged]
+ %w[scripts/lint-rugged],
+ %w[scripts/frontend/check_no_partial_karma_jest.sh]
]
case node_total
diff --git a/scripts/sync-stable-branch.sh b/scripts/sync-stable-branch.sh
index fc62453d743..b44bf26a151 100644
--- a/scripts/sync-stable-branch.sh
+++ b/scripts/sync-stable-branch.sh
@@ -23,10 +23,24 @@ then
exit 1
fi
+if [[ "$SOURCE_PROJECT" == '' ]]
+then
+ echo 'The variable SOURCE_PROJECT must be set to a non-empy value'
+ exit 1
+fi
+
+if [[ "$TARGET_PROJECT" == '' ]]
+then
+ echo 'The variable TARGET_PROJECT 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/}" \
+ -F "variables[SOURCE_PROJECT]=$SOURCE_PROJECT" \
+ -F "variables[TARGET_PROJECT]=$TARGET_PROJECT" \
"$MERGE_TRAIN_TRIGGER_URL"
diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb
index f35babc1b56..ff15e685007 100644
--- a/spec/controllers/uploads_controller_spec.rb
+++ b/spec/controllers/uploads_controller_spec.rb
@@ -196,24 +196,39 @@ describe UploadsController do
describe "GET show" do
context 'Content-Disposition security measures' do
+ let(:expected_disposition) { 'inline;' }
let(:project) { create(:project, :public) }
- context 'for PNG files' do
- it 'returns Content-Disposition: inline' do
- note = create(:note, :with_attachment, project: project)
- get :show, params: { model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png' }
+ shared_examples_for 'uploaded file with disposition' do
+ it 'returns correct Content-Disposition' do
+ get :show, params: { model: 'note', mounted_as: 'attachment', id: note.id, filename: filename }
- expect(response['Content-Disposition']).to start_with('inline;')
+ expect(response['Content-Disposition']).to start_with(expected_disposition)
end
end
+ context 'for PNG files' do
+ let(:filename) { 'dk.png' }
+ let(:expected_disposition) { 'inline;' }
+ let(:note) { create(:note, :with_attachment, project: project) }
+
+ it_behaves_like 'uploaded file with disposition'
+ end
+
+ context 'for PDF files' do
+ let(:filename) { 'git-cheat-sheet.pdf' }
+ let(:expected_disposition) { 'inline;' }
+ let(:note) { create(:note, :with_pdf_attachment, project: project) }
+
+ it_behaves_like 'uploaded file with disposition'
+ end
+
context 'for SVG files' do
- it 'returns Content-Disposition: attachment' do
- note = create(:note, :with_svg_attachment, project: project)
- get :show, params: { model: 'note', mounted_as: 'attachment', id: note.id, filename: 'unsanitized.svg' }
+ let(:filename) { 'unsanitized.svg' }
+ let(:expected_disposition) { 'attachment;' }
+ let(:note) { create(:note, :with_svg_attachment, project: project) }
- expect(response['Content-Disposition']).to start_with('attachment;')
- end
+ it_behaves_like 'uploaded file with disposition'
end
end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index a38935c89ba..ecb1f1996d9 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -207,14 +207,6 @@ FactoryBot.define do
trigger_request factory: :ci_trigger_request
end
- trait :resource_group do
- waiting_for_resource_at { 5.minutes.ago }
-
- after(:build) do |build, evaluator|
- build.resource_group = create(:ci_resource_group, project: build.project)
- end
- end
-
after(:build) do |build, evaluator|
build.project ||= build.pipeline.project
end
diff --git a/spec/factories/ci/resource.rb b/spec/factories/ci/resource.rb
deleted file mode 100644
index d47b3ba4635..00000000000
--- a/spec/factories/ci/resource.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-FactoryBot.define do
- factory :ci_resource, class: Ci::Resource do
- resource_group factory: :ci_resource_group
-
- trait(:retained) do
- build factory: :ci_build
- end
- end
-end
diff --git a/spec/factories/ci/resource_group.rb b/spec/factories/ci/resource_group.rb
deleted file mode 100644
index bdfc0740a45..00000000000
--- a/spec/factories/ci/resource_group.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-FactoryBot.define do
- factory :ci_resource_group, class: Ci::ResourceGroup do
- project
- sequence(:key) { |n| "IOS_#{n}" }
- end
-end
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb
index 2f02acca794..330f5276422 100644
--- a/spec/factories/notes.rb
+++ b/spec/factories/notes.rb
@@ -167,6 +167,10 @@ FactoryBot.define do
attachment { fixture_file_upload("spec/fixtures/unsanitized.svg", "image/svg+xml") }
end
+ trait :with_pdf_attachment do
+ attachment { fixture_file_upload("spec/fixtures/git-cheat-sheet.pdf", "application/pdf") }
+ end
+
transient do
in_reply_to { nil }
end
diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb
index 932c1d8d4bd..8c2b555305a 100644
--- a/spec/features/users/show_spec.rb
+++ b/spec/features/users/show_spec.rb
@@ -59,6 +59,42 @@ describe 'User page' do
end
end
+ context 'with blocked profile' do
+ let(:user) { create(:user, state: :blocked) }
+
+ it 'shows no tab' do
+ visit(user_path(user))
+
+ expect(page).to have_css("div.profile-header")
+ expect(page).not_to have_css("ul.nav-links")
+ end
+
+ it 'shows blocked message' do
+ visit(user_path(user))
+
+ expect(page).to have_content("This user is blocked")
+ end
+
+ it 'shows user name as blocked' do
+ visit(user_path(user))
+
+ expect(page).to have_css(".cover-title", text: 'Blocked user')
+ end
+
+ it 'shows no additional fields' do
+ visit(user_path(user))
+
+ expect(page).not_to have_css(".profile-user-bio")
+ expect(page).not_to have_css(".profile-link-holder")
+ end
+
+ it 'shows username' do
+ visit(user_path(user))
+
+ expect(page).to have_content("@#{user.username}")
+ end
+ end
+
it 'shows the status if there was one' do
create(:user_status, user: user, message: "Working hard!")
diff --git a/spec/fixtures/lib/gitlab/import_export/complex/project.json b/spec/fixtures/lib/gitlab/import_export/complex/project.json
index acfd6a6924a..583d6c7b78a 100644
--- a/spec/fixtures/lib/gitlab/import_export/complex/project.json
+++ b/spec/fixtures/lib/gitlab/import_export/complex/project.json
@@ -6757,6 +6757,17 @@
"updated_at": "2017-01-16T15:25:29.637Z"
}
],
+ "container_expiration_policy": {
+ "created_at": "2019-12-13 13:45:04 UTC",
+ "updated_at": "2019-12-13 13:45:04 UTC",
+ "next_run_at": null,
+ "project_id": 5,
+ "name_regex": null,
+ "cadence": "3month",
+ "older_than": null,
+ "keep_n": 100,
+ "enabled": false
+ },
"deploy_keys": [],
"services": [
{
diff --git a/spec/frontend/error_tracking/components/error_tracking_list_spec.js b/spec/frontend/error_tracking/components/error_tracking_list_spec.js
index 9ec3d42f0d4..581581405b6 100644
--- a/spec/frontend/error_tracking/components/error_tracking_list_spec.js
+++ b/spec/frontend/error_tracking/components/error_tracking_list_spec.js
@@ -8,8 +8,8 @@ import {
GlFormInput,
GlDropdown,
GlDropdownItem,
+ GlPagination,
} from '@gitlab/ui';
-import createListState from '~/error_tracking/store/list/state';
import ErrorTrackingList from '~/error_tracking/components/error_tracking_list.vue';
import errorsList from './list_mock.json';
@@ -27,13 +27,16 @@ describe('ErrorTrackingList', () => {
const findRecentSearchesDropdown = () =>
wrapper.find('.filtered-search-history-dropdown-wrapper');
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findPagination = () => wrapper.find(GlPagination);
function mountComponent({
errorTrackingEnabled = true,
userCanEnableErrorTracking = true,
+ sync = true,
stubs = {
'gl-link': GlLink,
'gl-table': GlTable,
+ 'gl-pagination': GlPagination,
'gl-dropdown': GlDropdown,
'gl-dropdown-item': GlDropdownItem,
},
@@ -41,6 +44,7 @@ describe('ErrorTrackingList', () => {
wrapper = shallowMount(ErrorTrackingList, {
localVue,
store,
+ sync,
propsData: {
indexPath: '/path',
enableErrorTrackingLink: '/link',
@@ -69,7 +73,20 @@ describe('ErrorTrackingList', () => {
sortByField: jest.fn(),
};
- const state = createListState();
+ const state = {
+ indexPath: '',
+ recentSearches: [],
+ errors: errorsList,
+ loading: true,
+ pagination: {
+ previous: {
+ cursor: 'previousCursor',
+ },
+ next: {
+ cursor: 'nextCursor',
+ },
+ },
+ };
store = new Vuex.Store({
modules: {
@@ -252,4 +269,65 @@ describe('ErrorTrackingList', () => {
});
});
});
+
+ describe('When pagination is not required', () => {
+ beforeEach(() => {
+ store.state.list.pagination = {};
+ mountComponent();
+ });
+
+ it('should not render the pagination component', () => {
+ expect(findPagination().exists()).toBe(false);
+ });
+ });
+
+ describe('When pagination is required', () => {
+ describe('and the user is on the first page', () => {
+ beforeEach(() => {
+ mountComponent({ sync: false });
+ });
+
+ it('shows a disabled Prev button', () => {
+ expect(wrapper.find('.prev-page-item').attributes('aria-disabled')).toBe('true');
+ });
+ });
+
+ describe('and the user is not on the first page', () => {
+ describe('and the previous button is clicked', () => {
+ beforeEach(() => {
+ mountComponent({ sync: false });
+ wrapper.setData({ pageValue: 2 });
+ });
+
+ it('fetches the previous page of results', () => {
+ expect(wrapper.find('.prev-page-item').attributes('aria-disabled')).toBe(undefined);
+ wrapper.vm.goToPrevPage();
+ expect(actions.startPolling).toHaveBeenCalledTimes(2);
+ expect(actions.startPolling).toHaveBeenLastCalledWith(
+ expect.anything(),
+ '/path?cursor=previousCursor',
+ undefined,
+ );
+ });
+ });
+
+ describe('and the next page button is clicked', () => {
+ beforeEach(() => {
+ mountComponent({ sync: false });
+ });
+
+ it('fetches the next page of results', () => {
+ window.scrollTo = jest.fn();
+ findPagination().vm.$emit('input', 2);
+ expect(window.scrollTo).toHaveBeenCalledWith(0, 0);
+ expect(actions.startPolling).toHaveBeenCalledTimes(2);
+ expect(actions.startPolling).toHaveBeenLastCalledWith(
+ expect.anything(),
+ '/path?cursor=nextCursor',
+ undefined,
+ );
+ });
+ });
+ });
+ });
});
diff --git a/spec/frontend/error_tracking/store/list/actions_spec.js b/spec/frontend/error_tracking/store/list/actions_spec.js
index fb659db9ab5..7906738f5b0 100644
--- a/spec/frontend/error_tracking/store/list/actions_spec.js
+++ b/spec/frontend/error_tracking/store/list/actions_spec.js
@@ -30,6 +30,7 @@ describe('error tracking actions', () => {
{},
[
{ type: types.SET_LOADING, payload: true },
+ { type: types.SET_PAGINATION, payload: payload.pagination },
{ type: types.SET_ERRORS, payload: payload.errors },
{ type: types.SET_LOADING, payload: false },
],
diff --git a/spec/frontend/notes/mock_data.js b/spec/frontend/notes/mock_data.js
index 01cb70d395c..9ed79c61c22 100644
--- a/spec/frontend/notes/mock_data.js
+++ b/spec/frontend/notes/mock_data.js
@@ -52,7 +52,7 @@ export const noteableDataMock = {
time_estimate: 0,
title: '14',
total_time_spent: 0,
- noteable_note_url: '/group/project/merge_requests/1#note_1',
+ noteable_note_url: '/group/project/-/merge_requests/1#note_1',
updated_at: '2017-08-04T09:53:01.226Z',
updated_by_id: 1,
web_url: '/gitlab-org/gitlab-foss/issues/26',
@@ -101,8 +101,8 @@ export const individualNote = {
{ name: 'art', user: { id: 1, name: 'Root', username: 'root' } },
],
toggle_award_path: '/gitlab-org/gitlab-foss/notes/1390/toggle_award_emoji',
- noteable_note_url: '/group/project/merge_requests/1#note_1',
- note_url: '/group/project/merge_requests/1#note_1',
+ noteable_note_url: '/group/project/-/merge_requests/1#note_1',
+ note_url: '/group/project/-/merge_requests/1#note_1',
report_abuse_path:
'/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1390&user_id=1',
path: '/gitlab-org/gitlab-foss/notes/1390',
@@ -161,8 +161,8 @@ export const note = {
},
],
toggle_award_path: '/gitlab-org/gitlab-foss/notes/546/toggle_award_emoji',
- note_url: '/group/project/merge_requests/1#note_1',
- noteable_note_url: '/group/project/merge_requests/1#note_1',
+ note_url: '/group/project/-/merge_requests/1#note_1',
+ noteable_note_url: '/group/project/-/merge_requests/1#note_1',
report_abuse_path:
'/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_546&user_id=1',
path: '/gitlab-org/gitlab-foss/notes/546',
@@ -205,7 +205,7 @@ export const discussionMock = {
discussion_id: '9e3bd2f71a01de45fd166e6719eb380ad9f270b1',
emoji_awardable: true,
award_emoji: [],
- noteable_note_url: '/group/project/merge_requests/1#note_1',
+ noteable_note_url: '/group/project/-/merge_requests/1#note_1',
toggle_award_path: '/gitlab-org/gitlab-foss/notes/1395/toggle_award_emoji',
report_abuse_path:
'/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1395&user_id=1',
@@ -253,7 +253,7 @@ export const discussionMock = {
emoji_awardable: true,
award_emoji: [],
toggle_award_path: '/gitlab-org/gitlab-foss/notes/1396/toggle_award_emoji',
- noteable_note_url: '/group/project/merge_requests/1#note_1',
+ noteable_note_url: '/group/project/-/merge_requests/1#note_1',
report_abuse_path:
'/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1396&user_id=1',
path: '/gitlab-org/gitlab-foss/notes/1396',
@@ -299,7 +299,7 @@ export const discussionMock = {
discussion_id: '9e3bd2f71a01de45fd166e6719eb380ad9f270b1',
emoji_awardable: true,
award_emoji: [],
- noteable_note_url: '/group/project/merge_requests/1#note_1',
+ noteable_note_url: '/group/project/-/merge_requests/1#note_1',
toggle_award_path: '/gitlab-org/gitlab-foss/notes/1437/toggle_award_emoji',
report_abuse_path:
'/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1437&user_id=1',
@@ -349,7 +349,7 @@ export const loggedOutnoteableData = {
can_create_note: false,
can_update: false,
},
- noteable_note_url: '/group/project/merge_requests/1#note_1',
+ noteable_note_url: '/group/project/-/merge_requests/1#note_1',
create_note_path: '/gitlab-org/gitlab-foss/notes?target_id=98&target_type=issue',
preview_note_path: '/gitlab-org/gitlab-foss/preview_markdown?target_id=98&target_type=Issue',
};
@@ -483,7 +483,7 @@ export const INDIVIDUAL_NOTE_RESPONSE_MAP = {
},
},
],
- noteable_note_url: '/group/project/merge_requests/1#note_1',
+ noteable_note_url: '/group/project/-/merge_requests/1#note_1',
toggle_award_path: '/gitlab-org/gitlab-foss/notes/1390/toggle_award_emoji',
report_abuse_path:
'/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1390\u0026user_id=1',
@@ -528,7 +528,7 @@ export const INDIVIDUAL_NOTE_RESPONSE_MAP = {
discussion_id: '70d5c92a4039a36c70100c6691c18c27e4b0a790',
emoji_awardable: true,
award_emoji: [],
- noteable_note_url: '/group/project/merge_requests/1#note_1',
+ noteable_note_url: '/group/project/-/merge_requests/1#note_1',
toggle_award_path: '/gitlab-org/gitlab-foss/notes/1391/toggle_award_emoji',
report_abuse_path:
'/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F26%23note_1391\u0026user_id=1',
@@ -583,7 +583,7 @@ export const INDIVIDUAL_NOTE_RESPONSE_MAP = {
discussion_id: 'a3ed36e29b1957efb3b68c53e2d7a2b24b1df052',
emoji_awardable: true,
award_emoji: [],
- noteable_note_url: '/group/project/merge_requests/1#note_1',
+ noteable_note_url: '/group/project/-/merge_requests/1#note_1',
toggle_award_path: '/gitlab-org/gitlab-foss/notes/1471/toggle_award_emoji',
report_abuse_path:
'/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F29%23note_1471\u0026user_id=1',
@@ -635,7 +635,7 @@ export const DISCUSSION_NOTE_RESPONSE_MAP = {
emoji_awardable: true,
award_emoji: [],
toggle_award_path: '/gitlab-org/gitlab-foss/notes/1471/toggle_award_emoji',
- noteable_note_url: '/group/project/merge_requests/1#note_1',
+ noteable_note_url: '/group/project/-/merge_requests/1#note_1',
report_abuse_path:
'/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F29%23note_1471\u0026user_id=1',
path: '/gitlab-org/gitlab-foss/notes/1471',
diff --git a/spec/graphql/types/permission_types/snippet_spec.rb b/spec/graphql/types/permission_types/snippet_spec.rb
index 71843153d43..66e9fa6dfdb 100644
--- a/spec/graphql/types/permission_types/snippet_spec.rb
+++ b/spec/graphql/types/permission_types/snippet_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
describe Types::PermissionTypes::Snippet do
it 'returns the snippets permissions' do
expected_permissions = [
- :create_note, :award_emoji, :read_snippet, :update_snippet, :admin_snippet
+ :create_note, :award_emoji, :read_snippet, :update_snippet, :admin_snippet, :report_snippet
]
expected_permissions.each do |permission|
diff --git a/spec/helpers/award_emoji_helper_spec.rb b/spec/helpers/award_emoji_helper_spec.rb
index 2ad6b68a34c..975f32edd42 100644
--- a/spec/helpers/award_emoji_helper_spec.rb
+++ b/spec/helpers/award_emoji_helper_spec.rb
@@ -51,7 +51,7 @@ describe AwardEmojiHelper do
it 'returns correct url' do
@project = merge_request.project
- expected_url = "/#{@project.namespace.path}/#{@project.path}/merge_requests/#{merge_request.iid}/toggle_award_emoji"
+ expected_url = "/#{@project.namespace.path}/#{@project.path}/-/merge_requests/#{merge_request.iid}/toggle_award_emoji"
expect(subject).to eq(expected_url)
end
diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb
index 7853617c3ed..63a37a1f113 100644
--- a/spec/helpers/events_helper_spec.rb
+++ b/spec/helpers/events_helper_spec.rb
@@ -115,7 +115,7 @@ describe EventsHelper do
it 'returns a merge request url' do
event.target = create(:note_on_merge_request, note: 'LGTM!')
- expect(subject).to eq("#{project_base_url}/merge_requests/#{event.note_target.iid}#note_#{event.target.id}")
+ expect(subject).to eq("#{project_base_url}/-/merge_requests/#{event.note_target.iid}#note_#{event.target.id}")
end
end
end
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index 3238743ee26..e2dff05cfaa 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -78,13 +78,21 @@ describe LabelsHelper do
end
context 'with a type argument' do
- ['issue', :issue, 'merge_request', :merge_request].each do |type|
+ ['issue', :issue].each do |type|
context "set to #{type}" do
it 'links to correct page' do
expect(link_to_label(label_presenter, type: type)).to match %r{<a href="/#{label.project.full_path}/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}">.*</a>}
end
end
end
+
+ ['merge_request', :merge_request].each do |type|
+ context "set to #{type}" do
+ it 'links to correct page' do
+ expect(link_to_label(label_presenter, type: type)).to match %r{<a href="/#{label.project.full_path}/-/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}">.*</a>}
+ end
+ end
+ end
end
context 'with a tooltip argument' do
diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
index cbc2401262f..557244e237e 100644
--- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
@@ -461,7 +461,7 @@ describe('IDE commit module actions', () => {
.dispatch('commit/commitChanges')
.then(() => {
expect(visitUrl).toHaveBeenCalledWith(
- `webUrl/merge_requests/new?merge_request[source_branch]=${
+ `webUrl/-/merge_requests/new?merge_request[source_branch]=${
store.getters['commit/placeholderBranchName']
}&merge_request[target_branch]=master&nav_source=webide`,
);
diff --git a/spec/javascripts/jobs/components/stages_dropdown_spec.js b/spec/javascripts/jobs/components/stages_dropdown_spec.js
index e091aece564..f1a01530104 100644
--- a/spec/javascripts/jobs/components/stages_dropdown_spec.js
+++ b/spec/javascripts/jobs/components/stages_dropdown_spec.js
@@ -27,7 +27,7 @@ describe('Stages Dropdown', () => {
},
merge_request: {
iid: 1234,
- path: '/root/detached-merge-request-pipelines/merge_requests/1',
+ path: '/root/detached-merge-request-pipelines/-/merge_requests/1',
title: 'Update README.md',
source_branch: 'feature-1234',
source_branch_path: '/root/detached-merge-request-pipelines/branches/feature-1234',
diff --git a/spec/javascripts/merge_request_spec.js b/spec/javascripts/merge_request_spec.js
index dc61482fdf3..b6173b9b171 100644
--- a/spec/javascripts/merge_request_spec.js
+++ b/spec/javascripts/merge_request_spec.js
@@ -17,7 +17,7 @@ describe('MergeRequest', function() {
mock = new MockAdapter(axios);
mock
- .onPatch(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`)
+ .onPatch(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/-/merge_requests/1.json`)
.reply(200, {});
this.merge = new MergeRequest();
@@ -75,7 +75,7 @@ describe('MergeRequest', function() {
setTimeout(() => {
expect(axios.patch).toHaveBeenCalledWith(
- `${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`,
+ `${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/-/merge_requests/1.json`,
{
merge_request: {
description: '- [ ] Task List Item\n- [ ] \n- [ ] Task List Item 2\n',
@@ -93,7 +93,9 @@ describe('MergeRequest', function() {
// eslint-disable-next-line jasmine/no-disabled-tests
xit('shows an error notification when tasklist update failed', done => {
mock
- .onPatch(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`)
+ .onPatch(
+ `${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/-/merge_requests/1.json`,
+ )
.reply(409, {});
$('.js-task-list-field').trigger({
diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js
index 73b1ea4d36f..1672cf69485 100644
--- a/spec/javascripts/merge_request_tabs_spec.js
+++ b/spec/javascripts/merge_request_tabs_spec.js
@@ -147,53 +147,53 @@ describe('MergeRequestTabs', function() {
it('changes from commits', function() {
setLocation({
- pathname: '/foo/bar/merge_requests/1/commits',
+ pathname: '/foo/bar/-/merge_requests/1/commits',
});
- expect(this.subject('show')).toBe('/foo/bar/merge_requests/1');
- expect(this.subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs');
+ expect(this.subject('show')).toBe('/foo/bar/-/merge_requests/1');
+ expect(this.subject('diffs')).toBe('/foo/bar/-/merge_requests/1/diffs');
});
it('changes from diffs', function() {
setLocation({
- pathname: '/foo/bar/merge_requests/1/diffs',
+ pathname: '/foo/bar/-/merge_requests/1/diffs',
});
- expect(this.subject('show')).toBe('/foo/bar/merge_requests/1');
- expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits');
+ expect(this.subject('show')).toBe('/foo/bar/-/merge_requests/1');
+ expect(this.subject('commits')).toBe('/foo/bar/-/merge_requests/1/commits');
});
it('changes from diffs.html', function() {
setLocation({
- pathname: '/foo/bar/merge_requests/1/diffs.html',
+ pathname: '/foo/bar/-/merge_requests/1/diffs.html',
});
- expect(this.subject('show')).toBe('/foo/bar/merge_requests/1');
- expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits');
+ expect(this.subject('show')).toBe('/foo/bar/-/merge_requests/1');
+ expect(this.subject('commits')).toBe('/foo/bar/-/merge_requests/1/commits');
});
it('changes from notes', function() {
setLocation({
- pathname: '/foo/bar/merge_requests/1',
+ pathname: '/foo/bar/-/merge_requests/1',
});
- expect(this.subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs');
- expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits');
+ expect(this.subject('diffs')).toBe('/foo/bar/-/merge_requests/1/diffs');
+ expect(this.subject('commits')).toBe('/foo/bar/-/merge_requests/1/commits');
});
it('includes search parameters and hash string', function() {
setLocation({
- pathname: '/foo/bar/merge_requests/1/diffs',
+ pathname: '/foo/bar/-/merge_requests/1/diffs',
search: '?view=parallel',
hash: '#L15-35',
});
- expect(this.subject('show')).toBe('/foo/bar/merge_requests/1?view=parallel#L15-35');
+ expect(this.subject('show')).toBe('/foo/bar/-/merge_requests/1?view=parallel#L15-35');
});
it('replaces the current history state', function() {
setLocation({
- pathname: '/foo/bar/merge_requests/1',
+ pathname: '/foo/bar/-/merge_requests/1',
});
const newState = this.subject('commits');
@@ -208,10 +208,10 @@ describe('MergeRequestTabs', function() {
it('treats "show" like "notes"', function() {
setLocation({
- pathname: '/foo/bar/merge_requests/1/commits',
+ pathname: '/foo/bar/-/merge_requests/1/commits',
});
- expect(this.subject('show')).toBe('/foo/bar/merge_requests/1');
+ expect(this.subject('show')).toBe('/foo/bar/-/merge_requests/1');
});
});
diff --git a/spec/javascripts/notes/components/note_actions_spec.js b/spec/javascripts/notes/components/note_actions_spec.js
index 2e0694869ba..a65e2fc31ad 100644
--- a/spec/javascripts/notes/components/note_actions_spec.js
+++ b/spec/javascripts/notes/components/note_actions_spec.js
@@ -30,7 +30,7 @@ describe('noteActions', () => {
canAwardEmoji: true,
canReportAsAbuse: true,
noteId: '539',
- noteUrl: `${TEST_HOST}/group/project/merge_requests/1#note_1`,
+ noteUrl: `${TEST_HOST}/group/project/-/merge_requests/1#note_1`,
reportAbusePath: `${TEST_HOST}/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_539&user_id=26`,
showReply: false,
};
diff --git a/spec/lib/banzai/filter/broadcast_message_sanitization_filter_spec.rb b/spec/lib/banzai/filter/broadcast_message_sanitization_filter_spec.rb
new file mode 100644
index 00000000000..317ac7ef854
--- /dev/null
+++ b/spec/lib/banzai/filter/broadcast_message_sanitization_filter_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Banzai::Filter::BroadcastMessageSanitizationFilter do
+ include FilterSpecHelper
+
+ it_behaves_like 'default whitelist'
+
+ describe 'custom whitelist' do
+ it_behaves_like 'XSS prevention'
+ it_behaves_like 'sanitize link'
+
+ subject { filter(exp).to_html }
+
+ context 'allows `a` elements' do
+ let(:exp) { %q{<a href="/">Link</a>} }
+
+ it { is_expected.to eq(exp) }
+ end
+
+ context 'allows `br` elements' do
+ let(:exp) { %q{Hello<br>World} }
+
+ it { is_expected.to eq(exp) }
+ end
+
+ context 'when `a` elements have `style` attribute' do
+ let(:whitelisted_style) { 'color: red; border: blue; background: green; padding: 10px; margin: 10px; text-decoration: underline;' }
+
+ context 'allows specific properties' do
+ let(:exp) { %{<a href="#" style="#{whitelisted_style}">Stylish Link</a>} }
+
+ it { is_expected.to eq(exp) }
+ end
+
+ it 'disallows other properties in `style` attribute on `a` elements' do
+ style = [whitelisted_style, 'position: fixed'].join(';')
+ doc = filter(%{<a href="#" style="#{style}">Stylish Link</a>})
+
+ expect(doc.at_css('a')['style']).to eq(whitelisted_style)
+ end
+ end
+
+ context 'allows `class` on `a` elements' do
+ let(:exp) { %q{<a href="#" class="btn">Button Link</a>} }
+
+ it { is_expected.to eq(exp) }
+ end
+ end
+end
diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb
index a17a645d4d0..1efca647b8b 100644
--- a/spec/lib/banzai/filter/relative_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb
@@ -157,7 +157,7 @@ describe Banzai::Filter::RelativeLinkFilter do
end
it 'does not modify relative URLs in system notes' do
- path = "#{project_path}/merge_requests/1/diffs"
+ path = "#{project_path}/-/merge_requests/1/diffs"
doc = filter(link(path), system_note: true)
expect(doc.at_css('a')['href']).to eq path
diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb
index 8a4b819e4d6..607dc3fda47 100644
--- a/spec/lib/banzai/filter/sanitization_filter_spec.rb
+++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb
@@ -5,48 +5,12 @@ require 'spec_helper'
describe Banzai::Filter::SanitizationFilter do
include FilterSpecHelper
- describe 'default whitelist' do
- it 'sanitizes tags that are not whitelisted' do
- act = %q{<textarea>no inputs</textarea> and <blink>no blinks</blink>}
- exp = 'no inputs and no blinks'
- expect(filter(act).to_html).to eq exp
- end
-
- it 'sanitizes tag attributes' do
- act = %q{<a href="http://example.com/bar.html" onclick="bar">Text</a>}
- exp = %q{<a href="http://example.com/bar.html">Text</a>}
- expect(filter(act).to_html).to eq exp
- end
-
- it 'sanitizes javascript in attributes' do
- act = %q(<a href="javascript:alert('foo')">Text</a>)
- exp = '<a>Text</a>'
- expect(filter(act).to_html).to eq exp
- end
-
- it 'sanitizes mixed-cased javascript in attributes' do
- act = %q(<a href="javaScript:alert('foo')">Text</a>)
- exp = '<a>Text</a>'
- expect(filter(act).to_html).to eq exp
- end
-
- it 'allows whitelisted HTML tags from the user' do
- exp = act = "<dl>\n<dt>Term</dt>\n<dd>Definition</dd>\n</dl>"
- expect(filter(act).to_html).to eq exp
- end
-
- it 'sanitizes `class` attribute on any element' do
- act = %q{<strong class="foo">Strong</strong>}
- expect(filter(act).to_html).to eq %q{<strong>Strong</strong>}
- end
-
- it 'sanitizes `id` attribute on any element' do
- act = %q{<em id="foo">Emphasis</em>}
- expect(filter(act).to_html).to eq %q{<em>Emphasis</em>}
- end
- end
+ it_behaves_like 'default whitelist'
describe 'custom whitelist' do
+ it_behaves_like 'XSS prevention'
+ it_behaves_like 'sanitize link'
+
it 'customizes the whitelist only once' do
instance = described_class.new('Foo')
control_count = instance.whitelist[:transformers].size
@@ -167,142 +131,6 @@ describe Banzai::Filter::SanitizationFilter do
expect(filter(html).to_html).to eq(output)
end
- it 'removes `rel` attribute from `a` elements' do
- act = %q{<a href="#" rel="nofollow">Link</a>}
- exp = %q{<a href="#">Link</a>}
-
- expect(filter(act).to_html).to eq exp
- end
-
- # Adapted from the Sanitize test suite: http://git.io/vczrM
- protocols = {
- 'protocol-based JS injection: simple, no spaces' => {
- input: '<a href="javascript:alert(\'XSS\');">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: simple, spaces before' => {
- input: '<a href="javascript :alert(\'XSS\');">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: simple, spaces after' => {
- input: '<a href="javascript: alert(\'XSS\');">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: simple, spaces before and after' => {
- input: '<a href="javascript : alert(\'XSS\');">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: preceding colon' => {
- input: '<a href=":javascript:alert(\'XSS\');">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: UTF-8 encoding' => {
- input: '<a href="javascript&#58;">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: long UTF-8 encoding' => {
- input: '<a href="javascript&#0058;">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: long UTF-8 encoding without semicolons' => {
- input: '<a href=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: hex encoding' => {
- input: '<a href="javascript&#x3A;">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: long hex encoding' => {
- input: '<a href="javascript&#x003A;">foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: hex encoding without semicolons' => {
- input: '<a href=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>foo</a>',
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: null char' => {
- input: "<a href=java\0script:alert(\"XSS\")>foo</a>",
- output: '<a href="java"></a>'
- },
-
- 'protocol-based JS injection: invalid URL char' => {
- input: '<img src=java\script:alert("XSS")>',
- output: '<img>'
- },
-
- 'protocol-based JS injection: Unicode' => {
- input: %Q(<a href="\u0001java\u0003script:alert('XSS')">foo</a>),
- output: '<a>foo</a>'
- },
-
- 'protocol-based JS injection: spaces and entities' => {
- input: '<a href=" &#14; javascript:alert(\'XSS\');">foo</a>',
- output: '<a href="">foo</a>'
- },
-
- 'protocol whitespace' => {
- input: '<a href=" http://example.com/"></a>',
- output: '<a href="http://example.com/"></a>'
- }
- }
-
- protocols.each do |name, data|
- it "disallows #{name}" do
- doc = filter(data[:input])
-
- expect(doc.to_html).to eq data[:output]
- end
- end
-
- it 'disallows data links' do
- input = '<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">XSS</a>'
- output = filter(input)
-
- expect(output.to_html).to eq '<a>XSS</a>'
- end
-
- it 'disallows vbscript links' do
- input = '<a href="vbscript:alert(document.domain)">XSS</a>'
- output = filter(input)
-
- expect(output.to_html).to eq '<a>XSS</a>'
- end
-
- it 'disallows invalid URIs' do
- expect(Addressable::URI).to receive(:parse).with('foo://example.com')
- .and_raise(Addressable::URI::InvalidURIError)
-
- input = '<a href="foo://example.com">Foo</a>'
- output = filter(input)
-
- expect(output.to_html).to eq '<a>Foo</a>'
- end
-
- it 'allows non-standard anchor schemes' do
- exp = %q{<a href="irc://irc.freenode.net/git">IRC</a>}
- act = filter(exp)
-
- expect(act.to_html).to eq exp
- end
-
- it 'allows relative links' do
- exp = %q{<a href="foo/bar.md">foo/bar.md</a>}
- act = filter(exp)
-
- expect(act.to_html).to eq exp
- end
-
it 'allows the `data-sourcepos` attribute globally' do
exp = %q{<p data-sourcepos="1:1-1:10">foo/bar.md</p>}
act = filter(exp)
diff --git a/spec/lib/banzai/pipeline/broadcast_message_pipeline_spec.rb b/spec/lib/banzai/pipeline/broadcast_message_pipeline_spec.rb
new file mode 100644
index 00000000000..9832b132b58
--- /dev/null
+++ b/spec/lib/banzai/pipeline/broadcast_message_pipeline_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Banzai::Pipeline::BroadcastMessagePipeline do
+ before do
+ stub_commonmark_sourcepos_disabled
+ end
+
+ subject { described_class.to_html(exp, project: spy) }
+
+ context "allows `a` elements" do
+ let(:exp) { "<a>Link</a>" }
+
+ it { is_expected.to eq("<p>#{exp}</p>") }
+ end
+
+ context "allows `br` elements" do
+ let(:exp) { "Hello<br>World" }
+
+ it { is_expected.to eq("<p>#{exp}</p>") }
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build/resource_group_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build/resource_group_spec.rb
deleted file mode 100644
index bf6985156d3..00000000000
--- a/spec/lib/gitlab/ci/pipeline/seed/build/resource_group_spec.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe Gitlab::Ci::Pipeline::Seed::Build::ResourceGroup do
- let_it_be(:project) { create(:project) }
- let(:job) { build(:ci_build, project: project) }
- let(:seed) { described_class.new(job, resource_group_key) }
-
- describe '#to_resource' do
- subject { seed.to_resource }
-
- context 'when resource group key is specified' do
- let(:resource_group_key) { 'iOS' }
-
- it 'returns a resource group object' do
- is_expected.to be_a(Ci::ResourceGroup)
- expect(subject.key).to eq('iOS')
- end
-
- context 'when environment has an invalid URL' do
- let(:resource_group_key) { ':::' }
-
- it 'returns nothing' do
- is_expected.to be_nil
- end
- end
-
- context 'when there is a resource group already' do
- let!(:resource_group) { create(:ci_resource_group, project: project, key: 'iOS') }
-
- it 'does not create a new resource group' do
- expect { subject }.not_to change { Ci::ResourceGroup.count }
- end
- end
- end
-
- context 'when resource group key is nil' do
- let(:resource_group_key) { nil }
-
- it 'returns nothing' do
- is_expected.to be_nil
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
index 5526ec9e16f..2ae513aea1b 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
@@ -231,15 +231,6 @@ describe Gitlab::Ci::Pipeline::Seed::Build do
end
end
end
-
- context 'when job belongs to a resource group' do
- let(:attributes) { { name: 'rspec', ref: 'master', resource_group_key: 'iOS' } }
-
- it 'returns a job with resource group' do
- expect(subject.resource_group).not_to be_nil
- expect(subject.resource_group.key).to eq('iOS')
- end
- end
end
context 'when job is a bridge' do
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index f61b28b06c8..8f9c5c74260 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -241,21 +241,6 @@ module Gitlab
end
end
end
-
- describe 'resource group' do
- context 'when resource group is defined' do
- let(:config) do
- YAML.dump(rspec: {
- script: 'test',
- resource_group: 'iOS'
- })
- end
-
- it 'has the attributes' do
- expect(subject[:resource_group_key]).to eq 'iOS'
- end
- end
- end
end
describe '#stages_attributes' do
diff --git a/spec/lib/gitlab/data_builder/pipeline_spec.rb b/spec/lib/gitlab/data_builder/pipeline_spec.rb
index 635bf56b72e..86ab7f888ca 100644
--- a/spec/lib/gitlab/data_builder/pipeline_spec.rb
+++ b/spec/lib/gitlab/data_builder/pipeline_spec.rb
@@ -77,7 +77,7 @@ describe Gitlab::DataBuilder::Pipeline do
expect(merge_request_attrs[:target_project_id]).to eq(merge_request.target_project_id)
expect(merge_request_attrs[:state]).to eq(merge_request.state)
expect(merge_request_attrs[:merge_status]).to eq(merge_request.merge_status)
- expect(merge_request_attrs[:url]).to eq("http://localhost/#{merge_request.target_project.full_path}/merge_requests/#{merge_request.iid}")
+ expect(merge_request_attrs[:url]).to eq("http://localhost/#{merge_request.target_project.full_path}/-/merge_requests/#{merge_request.iid}")
end
end
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 16fe5f23d14..8d436fb28e0 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -444,7 +444,6 @@ project:
- service_desk_setting
- import_failures
- container_expiration_policy
-- resource_groups
award_emoji:
- awardable
- user
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index f549216ccb0..ec1b935ad63 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -240,6 +240,16 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect(sentry_issue.sentry_issue_identifier).to eq(1234567891)
end
+ it 'restores container_expiration_policy' do
+ policy = Project.find_by_path('project').container_expiration_policy
+
+ aggregate_failures do
+ expect(policy).to be_an_instance_of(ContainerExpirationPolicy)
+ expect(policy).to be_persisted
+ expect(policy.cadence).to eq('3month')
+ end
+ end
+
context 'Merge requests' do
it 'always has the new project as a target' do
expect(MergeRequest.find_by_title('MR1').target_project).to eq(@project)
diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb
index 0aab02b6c4c..d349c2928b0 100644
--- a/spec/lib/gitlab/url_builder_spec.rb
+++ b/spec/lib/gitlab/url_builder_spec.rb
@@ -55,7 +55,7 @@ describe Gitlab::UrlBuilder do
url = described_class.build(merge_request)
- expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.full_path}/merge_requests/#{merge_request.iid}"
+ expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.full_path}/-/merge_requests/#{merge_request.iid}"
end
end
@@ -118,7 +118,7 @@ describe Gitlab::UrlBuilder do
url = described_class.build(note)
- expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.full_path}/merge_requests/#{merge_request.iid}#note_#{note.id}"
+ expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.full_path}/-/merge_requests/#{merge_request.iid}#note_#{note.id}"
end
end
@@ -129,7 +129,7 @@ describe Gitlab::UrlBuilder do
url = described_class.build(note)
- expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.full_path}/merge_requests/#{merge_request.iid}#note_#{note.id}"
+ expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.full_path}/-/merge_requests/#{merge_request.iid}#note_#{note.id}"
end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 371982df2bb..8a701a461c0 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1275,68 +1275,6 @@ describe Ci::Build do
end
end
- describe '#requires_resource?' do
- subject { build.requires_resource? }
-
- context 'when build needs a resource from a resource group' do
- let(:resource_group) { create(:ci_resource_group, project: project) }
- let(:build) { create(:ci_build, resource_group: resource_group, project: project) }
-
- context 'when build has not retained a resource' do
- it { is_expected.to eq(true) }
- end
-
- context 'when build has retained a resource' do
- before do
- resource_group.retain_resource_for(build)
- end
-
- it { is_expected.to eq(false) }
-
- context 'when ci_resource_group feature flag is disabled' do
- before do
- stub_feature_flags(ci_resource_group: false)
- end
-
- it { is_expected.to eq(false) }
- end
- end
- end
-
- context 'when build does not need a resource from a resource group' do
- let(:build) { create(:ci_build, project: project) }
-
- it { is_expected.to eq(false) }
- end
- end
-
- describe '#retains_resource?' do
- subject { build.retains_resource? }
-
- context 'when build needs a resource from a resource group' do
- let(:resource_group) { create(:ci_resource_group, project: project) }
- let(:build) { create(:ci_build, resource_group: resource_group, project: project) }
-
- context 'when build has retained a resource' do
- before do
- resource_group.retain_resource_for(build)
- end
-
- it { is_expected.to eq(true) }
- end
-
- context 'when build has not retained a resource' do
- it { is_expected.to eq(false) }
- end
- end
-
- context 'when build does not need a resource from a resource group' do
- let(:build) { create(:ci_build, project: project) }
-
- it { is_expected.to eq(false) }
- end
- end
-
describe '#stops_environment?' do
subject { build.stops_environment? }
diff --git a/spec/models/ci/resource_group_spec.rb b/spec/models/ci/resource_group_spec.rb
deleted file mode 100644
index 213a57c2d78..00000000000
--- a/spec/models/ci/resource_group_spec.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe Ci::ResourceGroup do
- describe 'validation' do
- it 'valids when key includes allowed character' do
- resource_group = build(:ci_resource_group, key: 'test')
-
- expect(resource_group).to be_valid
- end
-
- it 'invalids when key includes invalid character' do
- resource_group = build(:ci_resource_group, key: ':::')
-
- expect(resource_group).not_to be_valid
- end
- end
-
- describe '#ensure_resource' do
- it 'creates one resource when resource group is created' do
- resource_group = create(:ci_resource_group)
-
- expect(resource_group.resources.count).to eq(1)
- expect(resource_group.resources.all?(&:persisted?)).to eq(true)
- end
- end
-
- describe '#retain_resource_for' do
- subject { resource_group.retain_resource_for(build) }
-
- let(:build) { create(:ci_build) }
- let(:resource_group) { create(:ci_resource_group) }
-
- it 'retains resource for the build' do
- expect(resource_group.resources.first.build).to be_nil
-
- is_expected.to eq(true)
-
- expect(resource_group.resources.first.build).to eq(build)
- end
-
- context 'when there are no free resources' do
- before do
- resource_group.retain_resource_for(create(:ci_build))
- end
-
- it 'fails to retain resource' do
- is_expected.to eq(false)
- end
- end
-
- context 'when the build has already retained a resource' do
- let!(:another_resource) { create(:ci_resource, resource_group: resource_group, build: build) }
-
- it 'fails to retain resource' do
- expect { subject }.to raise_error(ActiveRecord::RecordNotUnique)
- end
- end
- end
-
- describe '#release_resource_from' do
- subject { resource_group.release_resource_from(build) }
-
- let(:build) { create(:ci_build) }
- let(:resource_group) { create(:ci_resource_group) }
-
- context 'when the build has already retained a resource' do
- before do
- resource_group.retain_resource_for(build)
- end
-
- it 'releases resource from the build' do
- expect(resource_group.resources.first.build).to eq(build)
-
- is_expected.to eq(true)
-
- expect(resource_group.resources.first.build).to be_nil
- end
- end
-
- context 'when the build has already released a resource' do
- it 'fails to release resource' do
- is_expected.to eq(false)
- end
- end
- end
-end
diff --git a/spec/models/ci/resource_spec.rb b/spec/models/ci/resource_spec.rb
deleted file mode 100644
index 27e512e2c45..00000000000
--- a/spec/models/ci/resource_spec.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe Ci::Resource do
- describe '.free' do
- subject { described_class.free }
-
- let(:resource_group) { create(:ci_resource_group) }
- let!(:free_resource) { resource_group.resources.take }
- let!(:retained_resource) { create(:ci_resource, :retained, resource_group: resource_group) }
-
- it 'returns free resources' do
- is_expected.to eq([free_resource])
- end
- end
-
- describe '.retained_by' do
- subject { described_class.retained_by(build) }
-
- let(:build) { create(:ci_build) }
- let!(:resource) { create(:ci_resource, build: build) }
-
- it 'returns retained resources' do
- is_expected.to eq([resource])
- end
- end
-end
diff --git a/spec/models/project_services/chat_message/merge_message_spec.rb b/spec/models/project_services/chat_message/merge_message_spec.rb
index b56eb19dd55..150ee6f7472 100644
--- a/spec/models/project_services/chat_message/merge_message_spec.rb
+++ b/spec/models/project_services/chat_message/merge_message_spec.rb
@@ -52,7 +52,7 @@ describe ChatMessage::MergeMessage do
context 'open' do
it 'returns a message regarding opening of merge requests' do
expect(subject.pretext).to eq(
- 'Test User (test.user) opened <http://somewhere.com/merge_requests/100|!100 *Merge Request title*> in <http://somewhere.com|project_name>')
+ 'Test User (test.user) opened <http://somewhere.com/-/merge_requests/100|!100 *Merge Request title*> in <http://somewhere.com|project_name>')
expect(subject.attachments).to be_empty
end
end
@@ -63,7 +63,7 @@ describe ChatMessage::MergeMessage do
end
it 'returns a message regarding closing of merge requests' do
expect(subject.pretext).to eq(
- 'Test User (test.user) closed <http://somewhere.com/merge_requests/100|!100 *Merge Request title*> in <http://somewhere.com|project_name>')
+ 'Test User (test.user) closed <http://somewhere.com/-/merge_requests/100|!100 *Merge Request title*> in <http://somewhere.com|project_name>')
expect(subject.attachments).to be_empty
end
end
@@ -77,12 +77,12 @@ describe ChatMessage::MergeMessage do
context 'open' do
it 'returns a message regarding opening of merge requests' do
expect(subject.pretext).to eq(
- 'Test User (test.user) opened [!100 *Merge Request title*](http://somewhere.com/merge_requests/100) in [project_name](http://somewhere.com)')
+ 'Test User (test.user) opened [!100 *Merge Request title*](http://somewhere.com/-/merge_requests/100) in [project_name](http://somewhere.com)')
expect(subject.attachments).to be_empty
expect(subject.activity).to eq({
title: 'Merge Request opened by Test User (test.user)',
subtitle: 'in [project_name](http://somewhere.com)',
- text: '[!100 *Merge Request title*](http://somewhere.com/merge_requests/100)',
+ text: '[!100 *Merge Request title*](http://somewhere.com/-/merge_requests/100)',
image: 'http://someavatar.com'
})
end
@@ -95,12 +95,12 @@ describe ChatMessage::MergeMessage do
it 'returns a message regarding closing of merge requests' do
expect(subject.pretext).to eq(
- 'Test User (test.user) closed [!100 *Merge Request title*](http://somewhere.com/merge_requests/100) in [project_name](http://somewhere.com)')
+ 'Test User (test.user) closed [!100 *Merge Request title*](http://somewhere.com/-/merge_requests/100) in [project_name](http://somewhere.com)')
expect(subject.attachments).to be_empty
expect(subject.activity).to eq({
title: 'Merge Request closed by Test User (test.user)',
subtitle: 'in [project_name](http://somewhere.com)',
- text: '[!100 *Merge Request title*](http://somewhere.com/merge_requests/100)',
+ text: '[!100 *Merge Request title*](http://somewhere.com/-/merge_requests/100)',
image: 'http://someavatar.com'
})
end
diff --git a/spec/presenters/merge_request_presenter_spec.rb b/spec/presenters/merge_request_presenter_spec.rb
index ce437090d43..6ce6f84cf61 100644
--- a/spec/presenters/merge_request_presenter_spec.rb
+++ b/spec/presenters/merge_request_presenter_spec.rb
@@ -101,7 +101,7 @@ describe MergeRequestPresenter do
allow(presenter).to receive_message_chain(:conflicts, :can_be_resolved_by?).with(user) { true }
expect(path)
- .to eq("/#{project.full_path}/merge_requests/#{resource.iid}/conflicts")
+ .to eq("/#{project.full_path}/-/merge_requests/#{resource.iid}/conflicts")
end
end
end
@@ -179,7 +179,7 @@ describe MergeRequestPresenter do
it 'returns correct link with correct text' do
is_expected
- .to match("#{project.full_path}/merge_requests/#{resource.iid}/assign_related_issues")
+ .to match("#{project.full_path}/-/merge_requests/#{resource.iid}/assign_related_issues")
is_expected
.to match("Assign yourself to this issue")
@@ -192,7 +192,7 @@ describe MergeRequestPresenter do
it 'returns correct link with correct text' do
is_expected
- .to match("#{project.full_path}/merge_requests/#{resource.iid}/assign_related_issues")
+ .to match("#{project.full_path}/-/merge_requests/#{resource.iid}/assign_related_issues")
is_expected
.to match("Assign yourself to these issues")
@@ -221,7 +221,7 @@ describe MergeRequestPresenter do
.with(user)
.and_return(true)
- is_expected.to eq("/#{resource.project.full_path}/merge_requests/#{resource.iid}/cancel_auto_merge")
+ is_expected.to eq("/#{resource.project.full_path}/-/merge_requests/#{resource.iid}/cancel_auto_merge")
end
end
@@ -248,7 +248,7 @@ describe MergeRequestPresenter do
.and_return(true)
is_expected
- .to eq("/#{resource.project.full_path}/merge_requests/#{resource.iid}/merge")
+ .to eq("/#{resource.project.full_path}/-/merge_requests/#{resource.iid}/merge")
end
end
@@ -312,7 +312,7 @@ describe MergeRequestPresenter do
project.add_maintainer(user)
is_expected
- .to eq("/#{resource.project.full_path}/merge_requests/#{resource.iid}/remove_wip")
+ .to eq("/#{resource.project.full_path}/-/merge_requests/#{resource.iid}/remove_wip")
end
end
@@ -535,7 +535,7 @@ describe MergeRequestPresenter do
it 'returns path' do
is_expected
- .to eq("/#{project.full_path}/merge_requests/#{resource.iid}/rebase")
+ .to eq("/#{project.full_path}/-/merge_requests/#{resource.iid}/rebase")
end
end
diff --git a/spec/presenters/snippet_presenter_spec.rb b/spec/presenters/snippet_presenter_spec.rb
index d874dbcc279..87f2220979c 100644
--- a/spec/presenters/snippet_presenter_spec.rb
+++ b/spec/presenters/snippet_presenter_spec.rb
@@ -127,4 +127,20 @@ describe SnippetPresenter do
end
end
end
+
+ describe '#can_report_as_spam' do
+ let(:snippet) { personal_snippet }
+
+ subject { presenter.can_report_as_spam? }
+
+ it 'returns false if the user cannot submit the snippet as spam' do
+ expect(subject).to be_falsey
+ end
+
+ it 'returns true if the user can submit the snippet as spam' do
+ allow(snippet).to receive(:submittable_as_spam_by?).and_return(true)
+
+ expect(subject).to be_truthy
+ end
+ end
end
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index ecbb81294a0..d38b7eafe97 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -852,7 +852,7 @@ describe API::Internal::Base do
message = <<~MESSAGE.strip
To create a merge request for #{branch_name}, visit:
- http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/new?merge_request%5Bsource_branch%5D=#{branch_name}
+ http://#{Gitlab.config.gitlab.host}/#{project.full_path}/-/merge_requests/new?merge_request%5Bsource_branch%5D=#{branch_name}
MESSAGE
expect(json_response['messages']).to include(build_basic_message(message))
@@ -909,7 +909,7 @@ describe API::Internal::Base do
message = <<~MESSAGE.strip
View merge request for #{branch_name}:
- http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/1
+ http://#{Gitlab.config.gitlab.host}/#{project.full_path}/-/merge_requests/1
MESSAGE
expect(json_response['messages']).to include(build_basic_message(message))
diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb
index da04e852795..534dced16bf 100644
--- a/spec/requests/api/releases_spec.rb
+++ b/spec/requests/api/releases_spec.rb
@@ -76,7 +76,7 @@ describe API::Releases do
mr_uri = URI.parse(links['merge_requests_url'])
issue_uri = URI.parse(links['issues_url'])
- expect(mr_uri.path).to eq("#{path_base}/merge_requests")
+ expect(mr_uri.path).to eq("#{path_base}/-/merge_requests")
expect(issue_uri.path).to eq("#{path_base}/issues")
expect(mr_uri.query).to eq(expected_query)
expect(issue_uri.query).to eq(expected_query)
diff --git a/spec/requests/projects/merge_requests_discussions_spec.rb b/spec/requests/projects/merge_requests_discussions_spec.rb
index 5945561aa7b..ffc98d09e5c 100644
--- a/spec/requests/projects/merge_requests_discussions_spec.rb
+++ b/spec/requests/projects/merge_requests_discussions_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
describe 'merge requests discussions' do
# Further tests can be found at merge_requests_controller_spec.rb
- describe 'GET /:namespace/:project/merge_requests/:iid/discussions' do
+ describe 'GET /:namespace/:project/-/merge_requests/:iid/discussions' do
let(:project) { create(:project, :repository) }
let(:user) { project.owner }
let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) }
diff --git a/spec/requests/user_activity_spec.rb b/spec/requests/user_activity_spec.rb
index 15666e00b9f..3cd4911098a 100644
--- a/spec/requests/user_activity_spec.rb
+++ b/spec/requests/user_activity_spec.rb
@@ -26,8 +26,8 @@ describe 'Update of user activity' do
'/dashboard/todos',
'/group/project/issues',
'/group/project/issues/10',
- '/group/project/merge_requests',
- '/group/project/merge_requests/15'
+ '/group/project/-/merge_requests',
+ '/group/project/-/merge_requests/15'
]
context 'without an authenticated user' do
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 287db20448a..96956d85de4 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -292,71 +292,77 @@ describe 'project routing' do
describe Projects::MergeRequestsController, 'routing' do
it 'to #commits' do
- expect(get('/gitlab/gitlabhq/merge_requests/1/commits.json')).to route_to('projects/merge_requests#commits', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'json')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/1/commits.json')).to route_to('projects/merge_requests#commits', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'json')
end
it 'to #pipelines' do
- expect(get('/gitlab/gitlabhq/merge_requests/1/pipelines.json')).to route_to('projects/merge_requests#pipelines', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'json')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/1/pipelines.json')).to route_to('projects/merge_requests#pipelines', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'json')
end
it 'to #merge' do
- expect(post('/gitlab/gitlabhq/merge_requests/1/merge')).to route_to(
+ expect(post('/gitlab/gitlabhq/-/merge_requests/1/merge')).to route_to(
'projects/merge_requests#merge',
namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1'
)
end
it 'to #show' do
- expect(get('/gitlab/gitlabhq/merge_requests/1.diff')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'diff')
- expect(get('/gitlab/gitlabhq/merge_requests/1.patch')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'patch')
- expect(get('/gitlab/gitlabhq/merge_requests/1/diffs')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', tab: 'diffs')
- expect(get('/gitlab/gitlabhq/merge_requests/1/commits')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', tab: 'commits')
- expect(get('/gitlab/gitlabhq/merge_requests/1/pipelines')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', tab: 'pipelines')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/1.diff')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'diff')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/1.patch')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'patch')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/1/diffs')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', tab: 'diffs')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/1/commits')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', tab: 'commits')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/1/pipelines')).to route_to('projects/merge_requests#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', tab: 'pipelines')
end
it_behaves_like 'RESTful project resources' do
let(:controller) { 'merge_requests' }
let(:actions) { [:index, :edit, :show, :update] }
+ let(:controller_path) { '/-/merge_requests' }
end
+
+ it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/merge_requests", "/gitlab/gitlabhq/-/merge_requests"
+ it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/merge_requests/1/diffs", "/gitlab/gitlabhq/-/merge_requests/1/diffs"
end
describe Projects::MergeRequests::CreationsController, 'routing' do
it 'to #new' do
- expect(get('/gitlab/gitlabhq/merge_requests/new')).to route_to('projects/merge_requests/creations#new', namespace_id: 'gitlab', project_id: 'gitlabhq')
- expect(get('/gitlab/gitlabhq/merge_requests/new/diffs')).to route_to('projects/merge_requests/creations#new', namespace_id: 'gitlab', project_id: 'gitlabhq', tab: 'diffs')
- expect(get('/gitlab/gitlabhq/merge_requests/new/pipelines')).to route_to('projects/merge_requests/creations#new', namespace_id: 'gitlab', project_id: 'gitlabhq', tab: 'pipelines')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/new')).to route_to('projects/merge_requests/creations#new', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/new/diffs')).to route_to('projects/merge_requests/creations#new', namespace_id: 'gitlab', project_id: 'gitlabhq', tab: 'diffs')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/new/pipelines')).to route_to('projects/merge_requests/creations#new', namespace_id: 'gitlab', project_id: 'gitlabhq', tab: 'pipelines')
end
it 'to #create' do
- expect(post('/gitlab/gitlabhq/merge_requests')).to route_to('projects/merge_requests/creations#create', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ expect(post('/gitlab/gitlabhq/-/merge_requests')).to route_to('projects/merge_requests/creations#create', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
it 'to #branch_from' do
- expect(get('/gitlab/gitlabhq/merge_requests/new/branch_from')).to route_to('projects/merge_requests/creations#branch_from', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/new/branch_from')).to route_to('projects/merge_requests/creations#branch_from', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
it 'to #branch_to' do
- expect(get('/gitlab/gitlabhq/merge_requests/new/branch_to')).to route_to('projects/merge_requests/creations#branch_to', namespace_id: 'gitlab', project_id: 'gitlabhq')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/new/branch_to')).to route_to('projects/merge_requests/creations#branch_to', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
it 'to #pipelines' do
- expect(get('/gitlab/gitlabhq/merge_requests/new/pipelines.json')).to route_to('projects/merge_requests/creations#pipelines', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'json')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/new/pipelines.json')).to route_to('projects/merge_requests/creations#pipelines', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'json')
end
it 'to #diffs' do
- expect(get('/gitlab/gitlabhq/merge_requests/new/diffs.json')).to route_to('projects/merge_requests/creations#diffs', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'json')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/new/diffs.json')).to route_to('projects/merge_requests/creations#diffs', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'json')
end
+
+ it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/merge_requests/new", "/gitlab/gitlabhq/-/merge_requests/new"
end
describe Projects::MergeRequests::DiffsController, 'routing' do
it 'to #show' do
- expect(get('/gitlab/gitlabhq/merge_requests/1/diffs.json')).to route_to('projects/merge_requests/diffs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'json')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/1/diffs.json')).to route_to('projects/merge_requests/diffs#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: 'json')
end
end
describe Projects::MergeRequests::ConflictsController, 'routing' do
it 'to #show' do
- expect(get('/gitlab/gitlabhq/merge_requests/1/conflicts')).to route_to('projects/merge_requests/conflicts#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
+ expect(get('/gitlab/gitlabhq/-/merge_requests/1/conflicts')).to route_to('projects/merge_requests/conflicts#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
end
end
# raw_project_snippet GET /:project_id/snippets/:id/raw(.:format) snippets#raw
diff --git a/spec/serializers/merge_request_widget_entity_spec.rb b/spec/serializers/merge_request_widget_entity_spec.rb
index 22232682be1..80f59ef90ca 100644
--- a/spec/serializers/merge_request_widget_entity_spec.rb
+++ b/spec/serializers/merge_request_widget_entity_spec.rb
@@ -45,12 +45,12 @@ describe MergeRequestWidgetEntity do
it 'has email_patches_path' do
expect(subject[:email_patches_path])
- .to eq("/#{resource.project.full_path}/merge_requests/#{resource.iid}.patch")
+ .to eq("/#{resource.project.full_path}/-/merge_requests/#{resource.iid}.patch")
end
it 'has plain_diff_path' do
expect(subject[:plain_diff_path])
- .to eq("/#{resource.project.full_path}/merge_requests/#{resource.iid}.diff")
+ .to eq("/#{resource.project.full_path}/-/merge_requests/#{resource.iid}.diff")
end
describe 'when source project is deleted' do
diff --git a/spec/serializers/paginated_diff_entity_spec.rb b/spec/serializers/paginated_diff_entity_spec.rb
index 7432e072318..77569aaa4bc 100644
--- a/spec/serializers/paginated_diff_entity_spec.rb
+++ b/spec/serializers/paginated_diff_entity_spec.rb
@@ -26,7 +26,7 @@ describe PaginatedDiffEntity do
expect(subject[:pagination]).to eq(
current_page: 2,
next_page: 3,
- next_page_href: "/#{merge_request.project.full_path}/merge_requests/#{merge_request.iid}/diffs_batch.json?page=3",
+ next_page_href: "/#{merge_request.project.full_path}/-/merge_requests/#{merge_request.iid}/diffs_batch.json?page=3",
total_pages: 7
)
end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index 4f624368215..04e57b1a2d4 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -914,44 +914,6 @@ describe Ci::CreatePipelineService do
end
end
- context 'with resource group' do
- context 'when resource group is defined' do
- before do
- config = YAML.dump(
- test: { stage: 'test', script: 'ls', resource_group: resource_group_key }
- )
-
- stub_ci_pipeline_yaml_file(config)
- end
-
- let(:resource_group_key) { 'iOS' }
-
- it 'persists the association correctly' do
- result = execute_service
- deploy_job = result.builds.find_by_name!(:test)
- resource_group = project.resource_groups.find_by_key!(resource_group_key)
-
- expect(result).to be_persisted
- expect(deploy_job.resource_group.key).to eq(resource_group_key)
- expect(project.resource_groups.count).to eq(1)
- expect(resource_group.builds.count).to eq(1)
- expect(resource_group.resources.count).to eq(1)
- expect(resource_group.resources.first.build).to eq(nil)
- end
-
- context 'when resourc group key includes predefined variables' do
- let(:resource_group_key) { '$CI_COMMIT_REF_NAME-$CI_JOB_NAME' }
-
- it 'interpolates the variables into the key correctly' do
- result = execute_service
-
- expect(result).to be_persisted
- expect(project.resource_groups.exists?(key: 'master-test')).to eq(true)
- end
- end
- end
- end
-
context 'with timeout' do
context 'when builds with custom timeouts are configured' do
before do
diff --git a/spec/services/ci/expire_pipeline_cache_service_spec.rb b/spec/services/ci/expire_pipeline_cache_service_spec.rb
index ff2d286465a..f7fc73d9f9c 100644
--- a/spec/services/ci/expire_pipeline_cache_service_spec.rb
+++ b/spec/services/ci/expire_pipeline_cache_service_spec.rb
@@ -11,7 +11,7 @@ describe Ci::ExpirePipelineCacheService do
describe '#execute' do
it 'invalidates Etag caching for project pipelines path' do
pipelines_path = "/#{project.full_path}/pipelines.json"
- new_mr_pipelines_path = "/#{project.full_path}/merge_requests/new.json"
+ new_mr_pipelines_path = "/#{project.full_path}/-/merge_requests/new.json"
pipeline_path = "/#{project.full_path}/pipelines/#{pipeline.id}.json"
expect_any_instance_of(Gitlab::EtagCaching::Store).to receive(:touch).with(pipelines_path)
@@ -24,7 +24,7 @@ describe Ci::ExpirePipelineCacheService do
it 'invalidates Etag caching for merge request pipelines if pipeline runs on any commit of that source branch' do
pipeline = create(:ci_empty_pipeline, status: 'created', project: project, ref: 'master')
merge_request = create(:merge_request, source_project: project, source_branch: pipeline.ref)
- merge_request_pipelines_path = "/#{project.full_path}/merge_requests/#{merge_request.iid}/pipelines.json"
+ merge_request_pipelines_path = "/#{project.full_path}/-/merge_requests/#{merge_request.iid}/pipelines.json"
allow_any_instance_of(Gitlab::EtagCaching::Store).to receive(:touch)
expect_any_instance_of(Gitlab::EtagCaching::Store).to receive(:touch).with(merge_request_pipelines_path)
diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb
index 76fe6f53a11..b1368f7776b 100644
--- a/spec/services/ci/retry_build_service_spec.rb
+++ b/spec/services/ci/retry_build_service_spec.rb
@@ -31,7 +31,7 @@ describe Ci::RetryBuildService do
job_artifacts_container_scanning job_artifacts_dast
job_artifacts_license_management job_artifacts_performance
job_artifacts_codequality job_artifacts_metrics scheduled_at
- job_variables waiting_for_resource_at].freeze
+ job_variables].freeze
IGNORE_ACCESSORS =
%i[type lock_version target_url base_tags trace_sections
@@ -40,14 +40,14 @@ describe Ci::RetryBuildService do
user_id auto_canceled_by_id retried failure_reason
sourced_pipelines artifacts_file_store artifacts_metadata_store
metadata runner_session trace_chunks upstream_pipeline_id
- artifacts_file artifacts_metadata artifacts_size commands resource resource_group_id].freeze
+ artifacts_file artifacts_metadata artifacts_size commands].freeze
shared_examples 'build duplication' do
let(:another_pipeline) { create(:ci_empty_pipeline, project: project) }
let(:build) do
create(:ci_build, :failed, :expired, :erased, :queued, :coverage, :tags,
- :allowed_to_fail, :on_tag, :triggered, :teardown_environment, :resource_group,
+ :allowed_to_fail, :on_tag, :triggered, :teardown_environment,
description: 'my-job', stage: 'test', stage_id: stage.id,
pipeline: pipeline, auto_canceled_by: another_pipeline,
scheduled_at: 10.seconds.since)
diff --git a/spec/services/merge_requests/get_urls_service_spec.rb b/spec/services/merge_requests/get_urls_service_spec.rb
index dcb8c8080a1..8500aa2b852 100644
--- a/spec/services/merge_requests/get_urls_service_spec.rb
+++ b/spec/services/merge_requests/get_urls_service_spec.rb
@@ -8,8 +8,8 @@ describe MergeRequests::GetUrlsService do
let(:project) { create(:project, :public, :repository) }
let(:service) { described_class.new(project) }
let(:source_branch) { "merge-test" }
- let(:new_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/new?merge_request%5Bsource_branch%5D=#{source_branch}" }
- let(:show_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/#{merge_request.iid}" }
+ let(:new_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/-/merge_requests/new?merge_request%5Bsource_branch%5D=#{source_branch}" }
+ let(:show_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/-/merge_requests/#{merge_request.iid}" }
let(:new_branch_changes) { "#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/#{source_branch}" }
let(:deleted_branch_changes) { "d14d6c0abdd253381df51a723d58691b2ee1ab08 #{Gitlab::Git::BLANK_SHA} refs/heads/#{source_branch}" }
let(:existing_branch_changes) { "d14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/#{source_branch}" }
@@ -127,7 +127,7 @@ describe MergeRequests::GetUrlsService do
let(:new_branch_changes) { "#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/new_branch" }
let(:existing_branch_changes) { "d14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/markdown" }
let(:changes) { "#{new_branch_changes}\n#{existing_branch_changes}" }
- let(:new_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch" }
+ let(:new_merge_request_url) { "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/-/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch" }
it 'returns 2 urls for both creating new and showing merge request' do
result = service.execute(changes)
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index a952e26e338..21bf4545f34 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -328,7 +328,7 @@ describe SystemNoteService do
url = if type == 'commit'
"#{Settings.gitlab.base_url}/#{project.namespace.path}/#{project.path}/commit/#{commit.id}"
else
- "#{Settings.gitlab.base_url}/#{project.namespace.path}/#{project.path}/merge_requests/#{merge_request.iid}"
+ "#{Settings.gitlab.base_url}/#{project.namespace.path}/#{project.path}/-/merge_requests/#{merge_request.iid}"
end
link = double(object: { 'url' => url })
diff --git a/spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb b/spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb
new file mode 100644
index 00000000000..134e38833cf
--- /dev/null
+++ b/spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb
@@ -0,0 +1,182 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'default whitelist' do
+ it 'sanitizes tags that are not whitelisted' do
+ act = %q{<textarea>no inputs</textarea> and <blink>no blinks</blink>}
+ exp = 'no inputs and no blinks'
+ expect(filter(act).to_html).to eq exp
+ end
+
+ it 'sanitizes tag attributes' do
+ act = %q{<a href="http://example.com/bar.html" onclick="bar">Text</a>}
+ exp = %q{<a href="http://example.com/bar.html">Text</a>}
+ expect(filter(act).to_html).to eq exp
+ end
+
+ it 'sanitizes javascript in attributes' do
+ act = %q(<a href="javascript:alert('foo')">Text</a>)
+ exp = '<a>Text</a>'
+ expect(filter(act).to_html).to eq exp
+ end
+
+ it 'sanitizes mixed-cased javascript in attributes' do
+ act = %q(<a href="javaScript:alert('foo')">Text</a>)
+ exp = '<a>Text</a>'
+ expect(filter(act).to_html).to eq exp
+ end
+
+ it 'allows whitelisted HTML tags from the user' do
+ exp = act = "<dl>\n<dt>Term</dt>\n<dd>Definition</dd>\n</dl>"
+ expect(filter(act).to_html).to eq exp
+ end
+
+ it 'sanitizes `class` attribute on any element' do
+ act = %q{<strong class="foo">Strong</strong>}
+ expect(filter(act).to_html).to eq %q{<strong>Strong</strong>}
+ end
+
+ it 'sanitizes `id` attribute on any element' do
+ act = %q{<em id="foo">Emphasis</em>}
+ expect(filter(act).to_html).to eq %q{<em>Emphasis</em>}
+ end
+end
+
+RSpec.shared_examples 'XSS prevention' do
+ # Adapted from the Sanitize test suite: http://git.io/vczrM
+ protocols = {
+ 'protocol-based JS injection: simple, no spaces' => {
+ input: '<a href="javascript:alert(\'XSS\');">foo</a>',
+ output: '<a>foo</a>'
+ },
+
+ 'protocol-based JS injection: simple, spaces before' => {
+ input: '<a href="javascript :alert(\'XSS\');">foo</a>',
+ output: '<a>foo</a>'
+ },
+
+ 'protocol-based JS injection: simple, spaces after' => {
+ input: '<a href="javascript: alert(\'XSS\');">foo</a>',
+ output: '<a>foo</a>'
+ },
+
+ 'protocol-based JS injection: simple, spaces before and after' => {
+ input: '<a href="javascript : alert(\'XSS\');">foo</a>',
+ output: '<a>foo</a>'
+ },
+
+ 'protocol-based JS injection: preceding colon' => {
+ input: '<a href=":javascript:alert(\'XSS\');">foo</a>',
+ output: '<a>foo</a>'
+ },
+
+ 'protocol-based JS injection: UTF-8 encoding' => {
+ input: '<a href="javascript&#58;">foo</a>',
+ output: '<a>foo</a>'
+ },
+
+ 'protocol-based JS injection: long UTF-8 encoding' => {
+ input: '<a href="javascript&#0058;">foo</a>',
+ output: '<a>foo</a>'
+ },
+
+ 'protocol-based JS injection: long UTF-8 encoding without semicolons' => {
+ input: '<a href=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>foo</a>',
+ output: '<a>foo</a>'
+ },
+
+ 'protocol-based JS injection: hex encoding' => {
+ input: '<a href="javascript&#x3A;">foo</a>',
+ output: '<a>foo</a>'
+ },
+
+ 'protocol-based JS injection: long hex encoding' => {
+ input: '<a href="javascript&#x003A;">foo</a>',
+ output: '<a>foo</a>'
+ },
+
+ 'protocol-based JS injection: hex encoding without semicolons' => {
+ input: '<a href=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>foo</a>',
+ output: '<a>foo</a>'
+ },
+
+ 'protocol-based JS injection: null char' => {
+ input: "<a href=java\0script:alert(\"XSS\")>foo</a>",
+ output: '<a href="java"></a>'
+ },
+
+ 'protocol-based JS injection: invalid URL char' => {
+ input: '<img src=java\script:alert("XSS")>',
+ output: '<img>'
+ },
+
+ 'protocol-based JS injection: Unicode' => {
+ input: %Q(<a href="\u0001java\u0003script:alert('XSS')">foo</a>),
+ output: '<a>foo</a>'
+ },
+
+ 'protocol-based JS injection: spaces and entities' => {
+ input: '<a href=" &#14; javascript:alert(\'XSS\');">foo</a>',
+ output: '<a href="">foo</a>'
+ },
+
+ 'protocol whitespace' => {
+ input: '<a href=" http://example.com/"></a>',
+ output: '<a href="http://example.com/"></a>'
+ }
+ }
+
+ protocols.each do |name, data|
+ it "disallows #{name}" do
+ doc = filter(data[:input])
+
+ expect(doc.to_html).to eq data[:output]
+ end
+ end
+
+ it 'disallows data links' do
+ input = '<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">XSS</a>'
+ output = filter(input)
+
+ expect(output.to_html).to eq '<a>XSS</a>'
+ end
+
+ it 'disallows vbscript links' do
+ input = '<a href="vbscript:alert(document.domain)">XSS</a>'
+ output = filter(input)
+
+ expect(output.to_html).to eq '<a>XSS</a>'
+ end
+end
+
+RSpec.shared_examples 'sanitize link' do
+ it 'removes `rel` attribute from `a` elements' do
+ act = %q{<a href="#" rel="nofollow">Link</a>}
+ exp = %q{<a href="#">Link</a>}
+
+ expect(filter(act).to_html).to eq exp
+ end
+
+ it 'disallows invalid URIs' do
+ expect(Addressable::URI).to receive(:parse).with('foo://example.com')
+ .and_raise(Addressable::URI::InvalidURIError)
+
+ input = '<a href="foo://example.com">Foo</a>'
+ output = filter(input)
+
+ expect(output.to_html).to eq '<a>Foo</a>'
+ end
+
+ it 'allows non-standard anchor schemes' do
+ exp = %q{<a href="irc://irc.freenode.net/git">IRC</a>}
+ act = filter(exp)
+
+ expect(act.to_html).to eq exp
+ end
+
+ it 'allows relative links' do
+ exp = %q{<a href="foo/bar.md">foo/bar.md</a>}
+ act = filter(exp)
+
+ expect(act.to_html).to eq exp
+ end
+end