Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/ci/reports.gitlab-ci.yml7
-rw-r--r--app/assets/javascripts/alert_management/components/alert_management_table.vue4
-rw-r--r--app/assets/javascripts/members/components/table/members_table.vue41
-rw-r--r--app/assets/javascripts/members/constants.js2
-rw-r--r--app/controllers/projects/pipelines/tests_controller.rb2
-rw-r--r--app/mailers/emails/pipelines.rb15
-rw-r--r--app/services/boards/base_items_list_service.rb128
-rw-r--r--app/services/boards/issues/list_service.rb111
-rw-r--r--app/services/projects/container_repository/gitlab/delete_tags_service.rb2
-rw-r--r--app/views/notify/pipeline_failed_email.html.haml2
-rw-r--r--app/views/notify/pipeline_failed_email.text.erb2
-rw-r--r--app/views/notify/pipeline_fixed_email.html.haml2
-rw-r--r--app/views/notify/pipeline_fixed_email.text.erb2
-rw-r--r--app/views/notify/pipeline_success_email.html.haml2
-rw-r--r--app/views/notify/pipeline_success_email.text.erb2
-rw-r--r--changelogs/unreleased/276965-allow-basic-auth-for-generic-packages.yml5
-rw-r--r--changelogs/unreleased/epic_boards_epic_list.yml5
-rw-r--r--changelogs/unreleased/peterhegman-hide-mobile-actions-field-with-no-buttons.yml5
-rw-r--r--changelogs/unreleased/pipeline-status-conciseness.yml5
-rw-r--r--db/migrate/20210104163218_add_epic_board_position_index.rb18
-rw-r--r--db/schema_migrations/202101041632181
-rw-r--r--db/structure.sql2
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql30
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json63
-rw-r--r--doc/api/graphql/reference/index.md1
-rw-r--r--doc/push_rules/push_rules.md34
-rw-r--r--doc/user/admin_area/settings/user_and_ip_rate_limits.md38
-rw-r--r--doc/user/packages/generic_packages/index.md21
-rw-r--r--lib/api/generic_packages.rb8
-rw-r--r--locale/gitlab.pot6
-rw-r--r--spec/frontend/members/components/table/members_table_spec.js24
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/mailers/emails/pipelines_spec.rb32
-rw-r--r--spec/rack_servers/puma_spec.rb2
-rw-r--r--spec/rack_servers/unicorn_spec.rb2
-rw-r--r--spec/requests/api/generic_packages_spec.rb189
-rw-r--r--spec/support/shared_examples/services/boards/issues_list_service_shared_examples.rb80
-rw-r--r--spec/support/shared_examples/services/boards/items_list_service_shared_examples.rb65
-rw-r--r--spec/views/notify/pipeline_failed_email.html.haml_spec.rb2
-rw-r--r--spec/views/notify/pipeline_failed_email.text.erb_spec.rb2
-rw-r--r--spec/views/notify/pipeline_fixed_email.html.haml_spec.rb2
-rw-r--r--spec/views/notify/pipeline_fixed_email.text.erb_spec.rb2
-rw-r--r--spec/views/notify/pipeline_success_email.html.haml_spec.rb2
-rw-r--r--spec/views/notify/pipeline_success_email.text.erb_spec.rb2
44 files changed, 675 insertions, 298 deletions
diff --git a/.gitlab/ci/reports.gitlab-ci.yml b/.gitlab/ci/reports.gitlab-ci.yml
index 095fd4e784e..77ada89aa6a 100644
--- a/.gitlab/ci/reports.gitlab-ci.yml
+++ b/.gitlab/ci/reports.gitlab-ci.yml
@@ -119,7 +119,7 @@ dependency_scanning gemnasium:
# git-lfs is needed for auto-remediation
- apk add git-lfs
after_script:
- # Post-processing: This will be an after_script once this job will use the Dependency Scanning CI template
+ # Post-processing
- apk add jq
# Lower execa severity based on https://gitlab.com/gitlab-org/gitlab/-/issues/223859#note_452922390
- jq '(.vulnerabilities[] | select (.cve == "yarn.lock:execa:gemnasium:05cfa2e8-2d0c-42c1-8894-638e2f12ff3d")).severity = "Medium"' gl-dependency-scanning-report.json > temp.json && mv temp.json gl-dependency-scanning-report.json
@@ -134,6 +134,11 @@ dependency_scanning retire-js:
image:
name: "$SECURE_ANALYZERS_PREFIX/retire.js:$DS_MAJOR_VERSION"
+dependency_scanning gemnasium-python:
+ extends: .dependency_scanning
+ image:
+ name: "$SECURE_ANALYZERS_PREFIX/gemnasium-python:$DS_MAJOR_VERSION"
+
# Analyze dependencies for malicious behavior
# See https://gitlab.com/gitlab-com/gl-security/security-research/package-hunter
package_hunter:
diff --git a/app/assets/javascripts/alert_management/components/alert_management_table.vue b/app/assets/javascripts/alert_management/components/alert_management_table.vue
index d6e31b57cba..2bad15faa85 100644
--- a/app/assets/javascripts/alert_management/components/alert_management_table.vue
+++ b/app/assets/javascripts/alert_management/components/alert_management_table.vue
@@ -23,7 +23,7 @@ import {
} from '~/vue_shared/components/paginated_table_with_search_and_tabs/constants';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import { convertToSnakeCase } from '~/lib/utils/text_utility';
-import getAlerts from '~/graphql_shared/queries/get_alerts.query.graphql';
+import getAlertsQuery from '~/graphql_shared/queries/get_alerts.query.graphql';
import getAlertsCountByStatus from '../graphql/queries/get_count_by_status.query.graphql';
import {
ALERTS_STATUS_TABS,
@@ -119,7 +119,7 @@ export default {
apollo: {
alerts: {
fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
- query: getAlerts,
+ query: getAlertsQuery,
variables() {
return {
searchTerm: this.searchTerm,
diff --git a/app/assets/javascripts/members/components/table/members_table.vue b/app/assets/javascripts/members/components/table/members_table.vue
index 0c79bf06d64..16e0cd5ad4e 100644
--- a/app/assets/javascripts/members/components/table/members_table.vue
+++ b/app/assets/javascripts/members/components/table/members_table.vue
@@ -36,7 +36,14 @@ export default {
filteredFields() {
return FIELDS.filter(
(field) => this.tableFields.includes(field.key) && this.showField(field),
- );
+ ).map((field) => {
+ const tdClassFunction = this[field.tdClassFunction];
+
+ return {
+ ...field,
+ ...(tdClassFunction && { tdClass: tdClassFunction }),
+ };
+ });
},
userIsLoggedIn() {
return this.currentUserId !== null;
@@ -46,6 +53,14 @@ export default {
initUserPopovers(this.$el.querySelectorAll('.js-user-link'));
},
methods: {
+ hasActionButtons(member) {
+ return (
+ canRemove(member, this.sourceId) ||
+ canResend(member) ||
+ canUpdate(member, this.currentUserId, this.sourceId) ||
+ canOverride(member)
+ );
+ },
showField(field) {
if (!Object.prototype.hasOwnProperty.call(field, 'showFunction')) {
return true;
@@ -58,14 +73,20 @@ export default {
return false;
}
- return this.members.some((member) => {
- return (
- canRemove(member, this.sourceId) ||
- canResend(member) ||
- canUpdate(member, this.currentUserId, this.sourceId) ||
- canOverride(member)
- );
- });
+ return this.members.some((member) => this.hasActionButtons(member));
+ },
+ tdClassActions(value, key, member) {
+ if (this.hasActionButtons(member)) {
+ return 'col-actions';
+ }
+
+ return ['col-actions', 'gl-display-none!', 'gl-display-lg-table-cell!'];
+ },
+ tbodyTrAttr(member) {
+ return {
+ ...this.tableAttrs.tr,
+ ...(member?.id && { 'data-testid': `members-table-row-${member.id}` }),
+ };
},
},
};
@@ -85,7 +106,7 @@ export default {
thead-class="border-bottom"
:empty-text="__('No members found')"
show-empty
- :tbody-tr-attr="tableAttrs.tr"
+ :tbody-tr-attr="tbodyTrAttr"
>
<template #cell(account)="{ item: member }">
<members-table-cell #default="{ memberType, isCurrentUser }" :member="member">
diff --git a/app/assets/javascripts/members/constants.js b/app/assets/javascripts/members/constants.js
index 21af825f795..77cb150bff6 100644
--- a/app/assets/javascripts/members/constants.js
+++ b/app/assets/javascripts/members/constants.js
@@ -70,8 +70,8 @@ export const FIELDS = [
{
key: 'actions',
thClass: 'col-actions',
- tdClass: 'col-actions',
showFunction: 'showActionsField',
+ tdClassFunction: 'tdClassActions',
},
];
diff --git a/app/controllers/projects/pipelines/tests_controller.rb b/app/controllers/projects/pipelines/tests_controller.rb
index 1c212964df5..924d52898ea 100644
--- a/app/controllers/projects/pipelines/tests_controller.rb
+++ b/app/controllers/projects/pipelines/tests_controller.rb
@@ -6,6 +6,8 @@ module Projects
before_action :authorize_read_build!
before_action :builds, only: [:show]
+ feature_category :code_testing
+
def summary
respond_to do |format|
format.json do
diff --git a/app/mailers/emails/pipelines.rb b/app/mailers/emails/pipelines.rb
index f2538d28a1a..0b830f4ee5e 100644
--- a/app/mailers/emails/pipelines.rb
+++ b/app/mailers/emails/pipelines.rb
@@ -3,15 +3,15 @@
module Emails
module Pipelines
def pipeline_success_email(pipeline, recipients)
- pipeline_mail(pipeline, recipients, 'succeeded')
+ pipeline_mail(pipeline, recipients, 'Succesful')
end
def pipeline_failed_email(pipeline, recipients)
- pipeline_mail(pipeline, recipients, 'failed')
+ pipeline_mail(pipeline, recipients, 'Failed')
end
def pipeline_fixed_email(pipeline, recipients)
- pipeline_mail(pipeline, recipients, 'been fixed')
+ pipeline_mail(pipeline, recipients, 'Fixed')
end
private
@@ -50,10 +50,13 @@ module Emails
end
def pipeline_subject(status)
- commit = [@pipeline.short_sha]
- commit << "in #{@merge_request.to_reference}" if @merge_request
+ subject = []
- subject("Pipeline ##{@pipeline.id} has #{status} for #{@pipeline.source_ref}", commit.join(' '))
+ subject << "#{status} pipeline for #{@pipeline.source_ref}"
+ subject << @project.name if @project
+ subject << @pipeline.short_sha
+
+ subject.join(' | ')
end
end
end
diff --git a/app/services/boards/base_items_list_service.rb b/app/services/boards/base_items_list_service.rb
new file mode 100644
index 00000000000..851120ef597
--- /dev/null
+++ b/app/services/boards/base_items_list_service.rb
@@ -0,0 +1,128 @@
+# frozen_string_literal: true
+
+module Boards
+ class BaseItemsListService < Boards::BaseService
+ include Gitlab::Utils::StrongMemoize
+ include ActiveRecord::ConnectionAdapters::Quoting
+
+ def execute
+ return items.order_closed_date_desc if list&.closed?
+
+ ordered_items
+ end
+
+ private
+
+ def ordered_items
+ raise NotImplementedError
+ end
+
+ def finder
+ raise NotImplementedError
+ end
+
+ def board
+ raise NotImplementedError
+ end
+
+ def item_model
+ raise NotImplementedError
+ end
+
+ # We memoize the query here since the finder methods we use are quite complex. This does not memoize the result of the query.
+ # rubocop: disable CodeReuse/ActiveRecord
+ def items
+ strong_memoize(:items) do
+ filter(finder.execute).reorder(nil)
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def filter(items)
+ # when grouping board issues by epics (used in board swimlanes)
+ # we need to get all issues in the board
+ # TODO: ignore hidden columns -
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/233870
+ return items if params[:all_lists]
+
+ items = without_board_labels(items) unless list&.movable? || list&.closed?
+ items = with_list_label(items) if list&.label?
+ items
+ end
+
+ def list
+ return unless params.key?(:id)
+
+ strong_memoize(:list) do
+ id = params[:id]
+
+ if board.lists.loaded?
+ board.lists.find { |l| l.id == id }
+ else
+ board.lists.find(id)
+ end
+ end
+ end
+
+ def filter_params
+ set_parent
+ set_state
+ set_attempt_search_optimizations
+
+ params
+ end
+
+ def set_parent
+ if parent.is_a?(Group)
+ params[:group_id] = parent.id
+ else
+ params[:project_id] = parent.id
+ end
+ end
+
+ def set_state
+ return if params[:all_lists]
+
+ params[:state] = list && list.closed? ? 'closed' : 'opened'
+ end
+
+ def set_attempt_search_optimizations
+ return unless params[:search].present?
+
+ if board.group_board?
+ params[:attempt_group_search_optimizations] = true
+ else
+ params[:attempt_project_search_optimizations] = true
+ end
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def board_label_ids
+ @board_label_ids ||= board.lists.movable.pluck(:label_id)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def without_board_labels(items)
+ return items unless board_label_ids.any?
+
+ items.where.not('EXISTS (?)', label_links(board_label_ids).limit(1))
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def label_links(label_ids)
+ LabelLink
+ .where('label_links.target_type = ?', item_model)
+ .where(item_model.arel_table[:id].eq(LabelLink.arel_table[:target_id]).to_sql)
+ .where(label_id: label_ids)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def with_list_label(items)
+ items.where('EXISTS (?)', label_links(list.label_id).limit(1))
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+end
diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb
index ab9d11abe98..27d59e052c7 100644
--- a/app/services/boards/issues/list_service.rb
+++ b/app/services/boards/issues/list_service.rb
@@ -2,26 +2,20 @@
module Boards
module Issues
- class ListService < Boards::BaseService
+ class ListService < Boards::BaseItemsListService
include Gitlab::Utils::StrongMemoize
def self.valid_params
IssuesFinder.valid_params
end
- def execute
- return fetch_issues.order_closed_date_desc if list&.closed?
-
- fetch_issues.order_by_position_and_priority(with_cte: params[:search].present?)
- end
-
# rubocop: disable CodeReuse/ActiveRecord
def metadata
issues = Issue.arel_table
keys = metadata_fields.keys
# TODO: eliminate need for SQL literal fragment
columns = Arel.sql(metadata_fields.values_at(*keys).join(', '))
- results = Issue.where(id: fetch_issues.select(issues[:id])).pluck(columns)
+ results = Issue.where(id: items.select(issues[:id])).pluck(columns)
Hash[keys.zip(results.flatten)]
end
@@ -29,74 +23,28 @@ module Boards
private
- def metadata_fields
- { size: 'COUNT(*)' }
- end
-
- # We memoize the query here since the finder methods we use are quite complex. This does not memoize the result of the query.
- # rubocop: disable CodeReuse/ActiveRecord
- def fetch_issues
- strong_memoize(:fetch_issues) do
- issues = IssuesFinder.new(current_user, filter_params).execute
-
- filter(issues).reorder(nil)
- end
+ def ordered_items
+ items.order_by_position_and_priority(with_cte: params[:search].present?)
end
- # rubocop: enable CodeReuse/ActiveRecord
- def filter(issues)
- # when grouping board issues by epics (used in board swimlanes)
- # we need to get all issues in the board
- # TODO: ignore hidden columns -
- # https://gitlab.com/gitlab-org/gitlab/-/issues/233870
- return issues if params[:all_lists]
-
- issues = without_board_labels(issues) unless list&.movable? || list&.closed?
- issues = with_list_label(issues) if list&.label?
- issues
+ def finder
+ IssuesFinder.new(current_user, filter_params)
end
def board
@board ||= parent.boards.find(params[:board_id])
end
- def list
- return unless params.key?(:id)
-
- strong_memoize(:list) do
- id = params[:id]
-
- if board.lists.loaded?
- board.lists.find { |l| l.id == id }
- else
- board.lists.find(id)
- end
- end
+ def metadata_fields
+ { size: 'COUNT(*)' }
end
def filter_params
- set_parent
- set_state
set_scope
set_non_archived
- set_attempt_search_optimizations
set_issue_types
- params
- end
-
- def set_parent
- if parent.is_a?(Group)
- params[:group_id] = parent.id
- else
- params[:project_id] = parent.id
- end
- end
-
- def set_state
- return if params[:all_lists]
-
- params[:state] = list && list.closed? ? 'closed' : 'opened'
+ super
end
def set_scope
@@ -107,49 +55,12 @@ module Boards
params[:non_archived] = parent.is_a?(Group)
end
- def set_attempt_search_optimizations
- return unless params[:search].present?
-
- if board.group_board?
- params[:attempt_group_search_optimizations] = true
- else
- params[:attempt_project_search_optimizations] = true
- end
- end
-
def set_issue_types
params[:issue_types] = Issue::TYPES_FOR_LIST
end
- # rubocop: disable CodeReuse/ActiveRecord
- def board_label_ids
- @board_label_ids ||= board.lists.movable.pluck(:label_id)
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- # rubocop: disable CodeReuse/ActiveRecord
- def without_board_labels(issues)
- return issues unless board_label_ids.any?
-
- issues.where.not('EXISTS (?)', issues_label_links.limit(1))
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- # rubocop: disable CodeReuse/ActiveRecord
- def issues_label_links
- LabelLink.where("label_links.target_type = 'Issue' AND label_links.target_id = issues.id").where(label_id: board_label_ids)
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- # rubocop: disable CodeReuse/ActiveRecord
- def with_list_label(issues)
- issues.where('EXISTS (?)', LabelLink.where("label_links.target_type = 'Issue' AND label_links.target_id = issues.id")
- .where("label_links.label_id = ?", list.label_id).limit(1))
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- def board_group
- board.group_board? ? parent : parent.group
+ def item_model
+ Issue
end
end
end
diff --git a/app/services/projects/container_repository/gitlab/delete_tags_service.rb b/app/services/projects/container_repository/gitlab/delete_tags_service.rb
index cc7de7eb776..589aac5c3ac 100644
--- a/app/services/projects/container_repository/gitlab/delete_tags_service.rb
+++ b/app/services/projects/container_repository/gitlab/delete_tags_service.rb
@@ -24,7 +24,7 @@ module Projects
return success(deleted: []) if @tag_names.empty?
delete_tags
- rescue => e
+ rescue TimeoutError, ::Faraday::Error => e
::Gitlab::ErrorTracking.track_exception(e, tags_count: @tag_names&.size, container_repository_id: @container_repository&.id)
error('error while deleting tags', nil, pass_back: { deleted: @deleted_tags, exception_class_name: e.class.name })
end
diff --git a/app/views/notify/pipeline_failed_email.html.haml b/app/views/notify/pipeline_failed_email.html.haml
index 575ec8c488e..4e8d8a20ef1 100644
--- a/app/views/notify/pipeline_failed_email.html.haml
+++ b/app/views/notify/pipeline_failed_email.html.haml
@@ -6,7 +6,7 @@
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;line-height:1;" }
%img{ alt: "✖", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gif'), style: "display:block;", width: "13" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
- Your pipeline has failed.
+ Pipeline ##{@pipeline.id} has failed!
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
diff --git a/app/views/notify/pipeline_failed_email.text.erb b/app/views/notify/pipeline_failed_email.text.erb
index a30e331d892..2deca375db1 100644
--- a/app/views/notify/pipeline_failed_email.text.erb
+++ b/app/views/notify/pipeline_failed_email.text.erb
@@ -1,4 +1,4 @@
-Your pipeline has failed.
+Pipeline #<%= @pipeline.id %> has failed!
Project: <%= @project.name %> ( <%= project_url(@project) %> )
Branch: <%= @pipeline.source_ref %> ( <%= commits_url(@pipeline) %> )
diff --git a/app/views/notify/pipeline_fixed_email.html.haml b/app/views/notify/pipeline_fixed_email.html.haml
index 05c0027a6fc..f2dbb3b20b7 100644
--- a/app/views/notify/pipeline_fixed_email.html.haml
+++ b/app/views/notify/pipeline_fixed_email.html.haml
@@ -1 +1 @@
-= render 'notify/successful_pipeline', title: 'Your pipeline has been fixed!'
+= render 'notify/successful_pipeline', title: "Pipeline has been fixed and ##{@pipeline.id} has passed!"
diff --git a/app/views/notify/pipeline_fixed_email.text.erb b/app/views/notify/pipeline_fixed_email.text.erb
index 75268531bdc..32334260a5e 100644
--- a/app/views/notify/pipeline_fixed_email.text.erb
+++ b/app/views/notify/pipeline_fixed_email.text.erb
@@ -1 +1 @@
-<%= render 'notify/successful_pipeline', title: 'Your pipeline has been fixed!' -%>
+<%= render 'notify/successful_pipeline', title: "Pipeline has been fixed and ##{@pipeline.id} has passed!" -%>
diff --git a/app/views/notify/pipeline_success_email.html.haml b/app/views/notify/pipeline_success_email.html.haml
index c34e02b5fee..47832907663 100644
--- a/app/views/notify/pipeline_success_email.html.haml
+++ b/app/views/notify/pipeline_success_email.html.haml
@@ -1 +1 @@
-= render 'notify/successful_pipeline', title: 'Your pipeline has passed.'
+= render 'notify/successful_pipeline', title: "Pipeline ##{@pipeline.id} has passed!"
diff --git a/app/views/notify/pipeline_success_email.text.erb b/app/views/notify/pipeline_success_email.text.erb
index b554bffc908..83cdb72d252 100644
--- a/app/views/notify/pipeline_success_email.text.erb
+++ b/app/views/notify/pipeline_success_email.text.erb
@@ -1 +1 @@
-<%= render 'notify/successful_pipeline', title: 'Your pipeline has passed.' -%>
+<%= render 'notify/successful_pipeline', title: "Pipeline ##{@pipeline.id} has passed!" -%>
diff --git a/changelogs/unreleased/276965-allow-basic-auth-for-generic-packages.yml b/changelogs/unreleased/276965-allow-basic-auth-for-generic-packages.yml
new file mode 100644
index 00000000000..2a4c3356a51
--- /dev/null
+++ b/changelogs/unreleased/276965-allow-basic-auth-for-generic-packages.yml
@@ -0,0 +1,5 @@
+---
+title: "Allow HTTP Basic Auth and deploy token authentication for generic packages"
+merge_request: 48540
+author: Moshe Katz @kohenkatz
+type: added
diff --git a/changelogs/unreleased/epic_boards_epic_list.yml b/changelogs/unreleased/epic_boards_epic_list.yml
new file mode 100644
index 00000000000..00cd47c4f31
--- /dev/null
+++ b/changelogs/unreleased/epic_boards_epic_list.yml
@@ -0,0 +1,5 @@
+---
+title: Added epic board position database index
+merge_request: 50277
+author:
+type: added
diff --git a/changelogs/unreleased/peterhegman-hide-mobile-actions-field-with-no-buttons.yml b/changelogs/unreleased/peterhegman-hide-mobile-actions-field-with-no-buttons.yml
new file mode 100644
index 00000000000..00fb54d0049
--- /dev/null
+++ b/changelogs/unreleased/peterhegman-hide-mobile-actions-field-with-no-buttons.yml
@@ -0,0 +1,5 @@
+---
+title: Hide "Actions" label on group members view if no action buttons exist
+merge_request: 50304
+author:
+type: fixed
diff --git a/changelogs/unreleased/pipeline-status-conciseness.yml b/changelogs/unreleased/pipeline-status-conciseness.yml
new file mode 100644
index 00000000000..6958c023caa
--- /dev/null
+++ b/changelogs/unreleased/pipeline-status-conciseness.yml
@@ -0,0 +1,5 @@
+---
+title: More concise pipeline notification emails.
+merge_request: 50405
+author:
+type: changed
diff --git a/db/migrate/20210104163218_add_epic_board_position_index.rb b/db/migrate/20210104163218_add_epic_board_position_index.rb
new file mode 100644
index 00000000000..32c829f0288
--- /dev/null
+++ b/db/migrate/20210104163218_add_epic_board_position_index.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddEpicBoardPositionIndex < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'index_boards_epic_board_positions_on_scoped_relative_position'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :boards_epic_board_positions, [:epic_board_id, :epic_id, :relative_position], name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :boards_epic_board_positions, INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20210104163218 b/db/schema_migrations/20210104163218
new file mode 100644
index 00000000000..c58a142f92e
--- /dev/null
+++ b/db/schema_migrations/20210104163218
@@ -0,0 +1 @@
+37aa0564d2ade1cab56a669facccbaaf08e4d9856c7a4cc120968d33cff161bd \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 58a1f452209..d768b885ad6 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -20946,6 +20946,8 @@ CREATE UNIQUE INDEX index_boards_epic_board_positions_on_epic_board_id_and_epic_
CREATE INDEX index_boards_epic_board_positions_on_epic_id ON boards_epic_board_positions USING btree (epic_id);
+CREATE INDEX index_boards_epic_board_positions_on_scoped_relative_position ON boards_epic_board_positions USING btree (epic_board_id, epic_id, relative_position);
+
CREATE INDEX index_boards_epic_boards_on_group_id ON boards_epic_boards USING btree (group_id);
CREATE INDEX index_boards_epic_lists_on_epic_board_id ON boards_epic_lists USING btree (epic_board_id);
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index bf453f24e4b..71d75db6f23 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -8512,6 +8512,11 @@ type EpicBoard {
first: Int
"""
+ Find an epic board list by ID.
+ """
+ id: BoardsEpicListID
+
+ """
Returns the last _n_ elements from the list.
"""
last: Int
@@ -9123,6 +9128,31 @@ Represents an epic board list
"""
type EpicList {
"""
+ List epics.
+ """
+ epics(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+ ): EpicConnection
+
+ """
Global ID of the board list.
"""
id: BoardsEpicListID!
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 5a9925b114c..bd646acfc5b 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -23566,6 +23566,16 @@
"description": "Epic board lists.",
"args": [
{
+ "name": "id",
+ "description": "Find an epic board list by ID.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "BoardsEpicListID",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
@@ -25360,6 +25370,59 @@
"description": "Represents an epic board list",
"fields": [
{
+ "name": "epics",
+ "description": "List epics.",
+ "args": [
+ {
+ "name": "after",
+ "description": "Returns the elements in the list that come after the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "before",
+ "description": "Returns the elements in the list that come before the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": "Returns the first _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "last",
+ "description": "Returns the last _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "EpicConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "id",
"description": "Global ID of the board list.",
"args": [
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 1a90b912c04..02db15bd603 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -1489,6 +1489,7 @@ Represents an epic board list.
| Field | Type | Description |
| ----- | ---- | ----------- |
+| `epics` | EpicConnection | List epics. |
| `id` | BoardsEpicListID! | Global ID of the board list. |
| `label` | Label | Label of the list. |
| `listType` | String! | Type of the list. |
diff --git a/doc/push_rules/push_rules.md b/doc/push_rules/push_rules.md
index 05ed7216495..4f902382fd5 100644
--- a/doc/push_rules/push_rules.md
+++ b/doc/push_rules/push_rules.md
@@ -84,21 +84,21 @@ override them in a project's settings. They can be also set on a [group level](.
1. Set the rule you want
1. Click **Save Push Rules** for the changes to take effect
-The following options are available.
-
-| Push rule | GitLab version | Description |
-| --------- | :------------: | ----------- |
-| Removal of tags with `git push` | **Starter** 7.10 | Forbid users to remove Git tags with `git push`. Tags will still be able to be deleted through the web UI. |
-| Check whether author is a GitLab user | **Starter** 7.10 | Restrict commits by author (email) to existing GitLab users. |
-| Committer restriction | **Premium** 10.2 | GitLab will reject any commit that was not committed by the current authenticated user |
-| Check whether commit is signed through GPG | **Premium** 10.1 | Reject commit when it is not signed through GPG. Read [signing commits with GPG](../user/project/repository/gpg_signed_commits/index.md). |
-| Prevent committing secrets to Git | **Starter** 8.12 | GitLab will reject any files that are likely to contain secrets. Read [what files are forbidden](#prevent-pushing-secrets-to-the-repository). |
-| Restrict by commit message | **Starter** 7.10 | Only commit messages that match this regular expression are allowed to be pushed. Leave empty to allow any commit message. Uses multiline mode, which can be disabled using `(?-m)`. |
-| Restrict by commit message (negative match)| **Starter** 11.1 | Only commit messages that do not match this regular expression are allowed to be pushed. Leave empty to allow any commit message. Uses multiline mode, which can be disabled using `(?-m)`. |
-| Restrict by branch name | **Starter** 9.3 | Only branch names that match this regular expression are allowed to be pushed. Leave empty to allow any branch name. |
-| Restrict by commit author's email | **Starter** 7.10 | Only commit author's email that match this regular expression are allowed to be pushed. Leave empty to allow any email. |
-| Prohibited file names | **Starter** 7.10 | Any committed filenames that match this regular expression and do not already exist in the repository are not allowed to be pushed. Leave empty to allow any filenames. See [common examples](#prohibited-file-names). |
-| Maximum file size | **Starter** 7.12 | Pushes that contain added or updated files that exceed this file size (in MB) are rejected. Set to 0 to allow files of any size. Files tracked by Git LFS are exempted. |
+The following options are available:
+
+| Push rule | Description |
+|---------------------------------|-------------|
+| Removal of tags with `git push` | Forbid users to remove Git tags with `git push`. Tags will still be able to be deleted through the web UI. |
+| Check whether author is a GitLab user | Restrict commits by author (email) to existing GitLab users. |
+| Committer restriction **(PREMIUM)** | GitLab will reject any commit that was not committed by the current authenticated user. |
+| Check whether commit is signed through GPG **(PREMIUM)** | Reject commit when it is not signed through GPG. Read [signing commits with GPG](../user/project/repository/gpg_signed_commits/index.md). |
+| Prevent committing secrets to Git | GitLab will reject any files that are likely to contain secrets. Read [what files are forbidden](#prevent-pushing-secrets-to-the-repository). |
+| Restrict by commit message | Only commit messages that match this regular expression are allowed to be pushed. Leave empty to allow any commit message. Uses multiline mode, which can be disabled using `(?-m)`. |
+| Restrict by commit message (negative match) | Only commit messages that do not match this regular expression are allowed to be pushed. Leave empty to allow any commit message. Uses multiline mode, which can be disabled using `(?-m)`. |
+| Restrict by branch name | Only branch names that match this regular expression are allowed to be pushed. Leave empty to allow any branch name. |
+| Restrict by commit author's email | Only commit author's email that match this regular expression are allowed to be pushed. Leave empty to allow any email. |
+| Prohibited file names | Any committed filenames that match this regular expression and do not already exist in the repository are not allowed to be pushed. Leave empty to allow any filenames. See [common examples](#prohibited-file-names). |
+| Maximum file size | Pushes that contain added or updated files that exceed this file size (in MB) are rejected. Set to 0 to allow files of any size. Files tracked by Git LFS are exempted. |
NOTE:
GitLab uses [RE2 syntax](https://github.com/google/re2/wiki/Syntax) for regular expressions in push rules, and you can test them at the [regex101 regex tester](https://regex101.com/).
@@ -174,8 +174,8 @@ id_ecdsa
#####################
# Any file ending with _history or .history extension
#####################
-pry.history
-bash_history
+*.history
+*_history
```
## Prohibited file names
diff --git a/doc/user/admin_area/settings/user_and_ip_rate_limits.md b/doc/user/admin_area/settings/user_and_ip_rate_limits.md
index 7496ef224df..e24068901df 100644
--- a/doc/user/admin_area/settings/user_and_ip_rate_limits.md
+++ b/doc/user/admin_area/settings/user_and_ip_rate_limits.md
@@ -21,20 +21,21 @@ IP rate limits**:
These limits are disabled by default.
NOTE:
-By default, all Git operations are first tried unathenticated. Because of this, HTTP Git operations may trigger the rate limits configured for unauthenticated requests.
+By default, all Git operations are first tried unauthenticated. Because of this, HTTP Git operations
+may trigger the rate limits configured for unauthenticated requests.
![user-and-ip-rate-limits](img/user_and_ip_rate_limits.png)
## Response text
-A request that exceeds a rate limit will get a 429 response code and a
+A request that exceeds a rate limit returns a 429 response code and a
plain-text body, which by default is:
```plaintext
Retry later
```
-It is possible to customize this response text in the admin area.
+It is possible to customize this response text in the Admin Area.
## Response headers
@@ -80,7 +81,7 @@ GitLab. For example:
in `/etc/default/gitlab`.
It is important that your load balancer erases or overwrites the bypass
-header on all incoming traffic, because otherwise you must trust your
+header on all incoming traffic. Otherwise, you must trust your
users to not set that header and bypass the GitLab rate limiter.
Note that the bypass only works if the header is set to `1`.
@@ -92,7 +93,9 @@ are marked with `"throttle_safelist":"throttle_bypass_header"` in
To disable the bypass mechanism, make sure the environment variable
`GITLAB_THROTTLE_BYPASS_HEADER` is unset or empty.
-## Allowing specific users to bypass authenticated request rate limiting
+## Allow specific users to bypass authenticated request rate limiting
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49127) in GitLab 13.7.
Similarly to the bypass header described above, it is possible to allow
a certain set of users to bypass the rate limiter. This only applies
@@ -115,13 +118,12 @@ are marked with `"throttle_safelist":"throttle_user_allowlist"` in
At application startup, the allowlist is logged in [`auth.log`](../../../administration/logs.md#authlog).
-## Trying out throttling settings before enforcing them
+## Try out throttling settings before enforcing them
> [Introduced](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/629) in GitLab 13.6.
-Trying out throttling settings can be done by setting the
-`GITLAB_THROTTLE_DRY_RUN` environment variable to a comma-separated
-list of throttle names.
+You can try out throttling settings by setting the `GITLAB_THROTTLE_DRY_RUN` environment variable to
+a comma-separated list of throttle names.
The possible names are:
@@ -132,21 +134,19 @@ The possible names are:
- `throttle_authenticated_protected_paths_api`
- `throttle_authenticated_protected_paths_web`
-For example: trying out throttles for all authenticated requests to
-non-protected paths could be done by setting
+For example, to try out throttles for all authenticated requests to
+non-protected paths can be done by setting
`GITLAB_THROTTLE_DRY_RUN='throttle_authenticated_web,throttle_authenticated_api'`.
-To enable the dry-run mode for all throttles, the variable can be set
-to `*`.
+To enable dry run mode for all throttles, the variable can be set to `*`.
-Setting a throttle to dry-run mode will log a message to the
-[`auth.log`](../../../administration/logs.md#authlog) when it would
-hit the limit, while letting the request continue as normal. The log
-message will contain an `env` field set to `track`. The `matched`
-field will contain the name of throttle that was hit.
+Setting a throttle to dry run mode logs a message to the
+[`auth.log`](../../../administration/logs.md#authlog) when it would hit the limit, while letting the
+request continue as normal. The log message contains an `env` field set to `track`. The `matched`
+field contains the name of throttle that was hit.
It is important to set the environment variable **before** enabling
-the rate limiting in the settings. The settings in the admin panel
+the rate limiting in the settings. The settings in the Admin Area
take effect immediately, while setting the environment variable
requires a restart of all the Puma processes.
diff --git a/doc/user/packages/generic_packages/index.md b/doc/user/packages/generic_packages/index.md
index ebdc48d50b9..82c72481984 100644
--- a/doc/user/packages/generic_packages/index.md
+++ b/doc/user/packages/generic_packages/index.md
@@ -20,8 +20,14 @@ Publish generic files, like release binaries, in your project’s Package Regist
## Authenticate to the Package Registry
-To authenticate to the Package Registry, you need either a [personal access token](../../../api/README.md#personalproject-access-tokens)
-or [CI job token](../../../api/README.md#gitlab-ci-job-token).
+To authenticate to the Package Registry, you need either a [personal access token](../../../api/README.md#personalproject-access-tokens),
+[CI job token](../../../api/README.md#gitlab-ci-job-token), or [deploy token](../../project/deploy_tokens/index.md).
+
+In addition to the standard API authentication mechanisms, the generic package
+API allows authentication with HTTP Basic authentication for use with tools that
+do not support the other available mechanisms. The `user-id` is not checked and
+may be any value, and the `password` must be either a [personal access token](../../../api/README.md#personalproject-access-tokens),
+a [CI job token](../../../api/README.md#gitlab-ci-job-token), or a [deploy token](../../project/deploy_tokens/index.md).
## Publish a package file
@@ -31,7 +37,7 @@ If a package with the same name, version, and filename already exists, it is als
Prerequisites:
-- You need to [authenticate with the API](../../../api/README.md#authentication).
+- You need to [authenticate with the API](../../../api/README.md#authentication). If authenticating with a deploy token, it must be configured with the `write_package_registry` scope.
```plaintext
PUT /projects/:id/packages/generic/:package_name/:package_version/:file_name
@@ -70,7 +76,7 @@ If multiple packages have the same name, version, and filename, then the most re
Prerequisites:
-- You need to [authenticate with the API](../../../api/README.md#authentication).
+- You need to [authenticate with the API](../../../api/README.md#authentication). If authenticating with a deploy token, it must be configured with the `read_package_registry` and/or `write_package_registry` scope.
```plaintext
GET /projects/:id/packages/generic/:package_name/:package_version/:file_name
@@ -92,6 +98,13 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" \
"https://gitlab.example.com/api/v4/projects/24/packages/generic/my_package/0.0.1/file.txt"
```
+Example request that uses HTTP Basic authentication:
+
+```shell
+curl --user "user:<your_access_token>" \
+ https://gitlab.example.com/api/v4/projects/24/packages/generic/my_package/0.0.1/file.txt
+```
+
## Publish a generic package by using CI/CD
To work with generic packages in [GitLab CI/CD](../../../ci/README.md), you can use
diff --git a/lib/api/generic_packages.rb b/lib/api/generic_packages.rb
index 3e1dd044c8d..167531fdaec 100644
--- a/lib/api/generic_packages.rb
+++ b/lib/api/generic_packages.rb
@@ -21,7 +21,7 @@ module API
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- route_setting :authentication, job_token_allowed: true
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
namespace ':id/packages/generic' do
namespace ':package_name/*package_version/:file_name', requirements: GENERIC_PACKAGES_REQUIREMENTS do
@@ -29,7 +29,7 @@ module API
detail 'This feature was introduced in GitLab 13.5'
end
- route_setting :authentication, job_token_allowed: true
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
params do
requires :package_name, type: String, desc: 'Package name', regexp: Gitlab::Regex.generic_package_name_regex, file_path: true
@@ -52,7 +52,7 @@ module API
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
end
- route_setting :authentication, job_token_allowed: true
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
put do
authorize_upload!(project)
@@ -82,7 +82,7 @@ module API
requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.generic_package_file_name_regex, file_path: true
end
- route_setting :authentication, job_token_allowed: true
+ route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
get do
authorize_read_package!(project)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 77c9ad3f012..bba8eed23ec 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -31240,6 +31240,9 @@ msgstr ""
msgid "Vulnerability|Detected"
msgstr ""
+msgid "Vulnerability|Download"
+msgstr ""
+
msgid "Vulnerability|Evidence"
msgstr ""
@@ -31267,6 +31270,9 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
+msgid "Vulnerability|Reproduction Assets"
+msgstr ""
+
msgid "Vulnerability|Request"
msgstr ""
diff --git a/spec/frontend/members/components/table/members_table_spec.js b/spec/frontend/members/components/table/members_table_spec.js
index 735889c3b46..dbaccde069c 100644
--- a/spec/frontend/members/components/table/members_table_spec.js
+++ b/spec/frontend/members/components/table/members_table_spec.js
@@ -63,6 +63,10 @@ describe('MembersTable', () => {
createWrapper(getByTestIdHelper(wrapper.element, id, options));
const findTable = () => wrapper.find(GlTable);
+ const findTableCellByMemberId = (tableCellLabel, memberId) =>
+ getByTestId(`members-table-row-${memberId}`).find(
+ `[data-label="${tableCellLabel}"][role="cell"]`,
+ );
afterEach(() => {
wrapper.destroy();
@@ -131,16 +135,30 @@ describe('MembersTable', () => {
canRemove: true,
};
+ const memberNoPermissions = {
+ ...memberMock,
+ id: 2,
+ };
+
describe.each`
permission | members
- ${'canUpdate'} | ${[memberCanUpdate]}
- ${'canRemove'} | ${[memberCanRemove]}
- ${'canResend'} | ${[invite]}
+ ${'canUpdate'} | ${[memberNoPermissions, memberCanUpdate]}
+ ${'canRemove'} | ${[memberNoPermissions, memberCanRemove]}
+ ${'canResend'} | ${[memberNoPermissions, invite]}
`('when one of the members has $permission permissions', ({ members }) => {
it('renders the "Actions" field', () => {
createComponent({ members, tableFields: ['actions'] });
expect(getByTestId('col-actions').exists()).toBe(true);
+
+ expect(findTableCellByMemberId('Actions', members[0].id).classes()).toStrictEqual([
+ 'col-actions',
+ 'gl-display-none!',
+ 'gl-display-lg-table-cell!',
+ ]);
+ expect(findTableCellByMemberId('Actions', members[1].id).classes()).toStrictEqual([
+ 'col-actions',
+ ]);
});
});
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 071a64325dc..67768e2db8f 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -723,6 +723,7 @@ epic:
- user_mentions
- note_authors
- boards_epic_user_preferences
+- epic_board_positions
epic_issue:
- epic
- issue
diff --git a/spec/mailers/emails/pipelines_spec.rb b/spec/mailers/emails/pipelines_spec.rb
index f0f23fe2097..3ac68721357 100644
--- a/spec/mailers/emails/pipelines_spec.rb
+++ b/spec/mailers/emails/pipelines_spec.rb
@@ -11,8 +11,8 @@ RSpec.describe Emails::Pipelines do
shared_examples_for 'correct pipeline information' do
it 'has a correct information' do
expect(subject)
- .to have_subject "#{project.name} | Pipeline ##{pipeline.id} has " \
- "#{status} for #{pipeline.source_ref} | " \
+ .to have_subject "#{status} pipeline for #{pipeline.source_ref} | " \
+ "#{project.name} | " \
"#{pipeline.short_sha}".to_s
expect(subject).to have_body_text pipeline.source_ref
@@ -29,8 +29,8 @@ RSpec.describe Emails::Pipelines do
it 'has correct information that there is no merge request link' do
expect(subject)
- .to have_subject "#{project.name} | Pipeline ##{pipeline.id} has " \
- "#{status} for #{pipeline.source_ref} | " \
+ .to have_subject "#{status} pipeline for #{pipeline.source_ref} | " \
+ "#{project.name} | " \
"#{pipeline.short_sha}".to_s
expect(subject).to have_body_text pipeline.source_ref
@@ -49,9 +49,9 @@ RSpec.describe Emails::Pipelines do
it 'has correct information that there is a merge request link' do
expect(subject)
- .to have_subject "#{project.name} | Pipeline ##{pipeline.id} has " \
- "#{status} for #{pipeline.source_ref} | " \
- "#{pipeline.short_sha} in !#{merge_request.iid}".to_s
+ .to have_subject "#{status} pipeline for #{pipeline.source_ref} | " \
+ "#{project.name} | " \
+ "#{pipeline.short_sha}".to_s
expect(subject).to have_body_text merge_request.to_reference
expect(subject).to have_body_text pipeline.source_ref
@@ -71,9 +71,9 @@ RSpec.describe Emails::Pipelines do
it 'has correct information that there is a merge request link' do
expect(subject)
- .to have_subject "#{project.name} | Pipeline ##{pipeline.id} has " \
- "#{status} for #{pipeline.source_ref} | " \
- "#{pipeline.short_sha} in !#{merge_request.iid}".to_s
+ .to have_subject "#{status} pipeline for #{pipeline.source_ref} | " \
+ "#{project.name} | " \
+ "#{pipeline.short_sha}".to_s
expect(subject).to have_body_text merge_request.to_reference
expect(subject).to have_body_text pipeline.source_ref
@@ -89,8 +89,8 @@ RSpec.describe Emails::Pipelines do
let(:sha) { project.commit(ref).sha }
it_behaves_like 'correct pipeline information' do
- let(:status) { 'succeeded' }
- let(:status_text) { 'Your pipeline has passed.' }
+ let(:status) { 'Succesful' }
+ let(:status_text) { "Pipeline ##{pipeline.id} has passed!" }
end
end
@@ -102,8 +102,8 @@ RSpec.describe Emails::Pipelines do
let(:sha) { project.commit(ref).sha }
it_behaves_like 'correct pipeline information' do
- let(:status) { 'failed' }
- let(:status_text) { 'Your pipeline has failed.' }
+ let(:status) { 'Failed' }
+ let(:status_text) { "Pipeline ##{pipeline.id} has failed!" }
end
end
@@ -115,8 +115,8 @@ RSpec.describe Emails::Pipelines do
let(:sha) { project.commit(ref).sha }
it_behaves_like 'correct pipeline information' do
- let(:status) { 'been fixed' }
- let(:status_text) { 'Your pipeline has been fixed!' }
+ let(:status) { 'Fixed' }
+ let(:status_text) { "Pipeline has been fixed and ##{pipeline.id} has passed!" }
end
end
end
diff --git a/spec/rack_servers/puma_spec.rb b/spec/rack_servers/puma_spec.rb
index 80595b267fa..6729119fe92 100644
--- a/spec/rack_servers/puma_spec.rb
+++ b/spec/rack_servers/puma_spec.rb
@@ -44,7 +44,7 @@ RSpec.describe 'Puma' do
end
after(:all) do
- WebMock.disable_net_connect!(allow_localhost: true)
+ webmock_enable!
Process.kill('TERM', @puma_master_pid)
rescue Errno::ESRCH
end
diff --git a/spec/rack_servers/unicorn_spec.rb b/spec/rack_servers/unicorn_spec.rb
index 5887b49d269..52d44b6e7e0 100644
--- a/spec/rack_servers/unicorn_spec.rb
+++ b/spec/rack_servers/unicorn_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe 'Unicorn' do
end
after(:all) do
- WebMock.disable_net_connect!(allow_localhost: true)
+ webmock_enable!
Process.kill('TERM', @unicorn_master_pid)
end
diff --git a/spec/requests/api/generic_packages_spec.rb b/spec/requests/api/generic_packages_spec.rb
index b8e79853486..d162d288129 100644
--- a/spec/requests/api/generic_packages_spec.rb
+++ b/spec/requests/api/generic_packages_spec.rb
@@ -3,8 +3,16 @@
require 'spec_helper'
RSpec.describe API::GenericPackages do
+ include HttpBasicAuthHelpers
+
let_it_be(:personal_access_token) { create(:personal_access_token) }
let_it_be(:project, reload: true) { create(:project) }
+ let_it_be(:deploy_token_rw) { create(:deploy_token, read_package_registry: true, write_package_registry: true) }
+ let_it_be(:project_deploy_token_rw) { create(:project_deploy_token, deploy_token: deploy_token_rw, project: project) }
+ let_it_be(:deploy_token_ro) { create(:deploy_token, read_package_registry: true, write_package_registry: false) }
+ let_it_be(:project_deploy_token_ro) { create(:project_deploy_token, deploy_token: deploy_token_ro, project: project) }
+ let_it_be(:deploy_token_wo) { create(:deploy_token, read_package_registry: false, write_package_registry: true) }
+ let_it_be(:project_deploy_token_wo) { create(:project_deploy_token, deploy_token: deploy_token_wo, project: project) }
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:workhorse_header) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
let(:user) { personal_access_token.user }
@@ -22,6 +30,23 @@ RSpec.describe API::GenericPackages do
personal_access_token_header('wrong token')
when :invalid_job_token
job_token_header('wrong token')
+ when :user_basic_auth
+ user_basic_auth_header(user)
+ when :invalid_user_basic_auth
+ basic_auth_header('invalid user', 'invalid password')
+ end
+ end
+
+ def deploy_token_auth_header
+ case authenticate_with
+ when :deploy_token_rw
+ deploy_token_header(deploy_token_rw.token)
+ when :deploy_token_ro
+ deploy_token_header(deploy_token_ro.token)
+ when :deploy_token_wo
+ deploy_token_header(deploy_token_wo.token)
+ when :invalid_deploy_token
+ deploy_token_header('wrong token')
end
end
@@ -33,6 +58,10 @@ RSpec.describe API::GenericPackages do
{ Gitlab::Auth::AuthFinders::JOB_TOKEN_HEADER => value || ci_build.token }
end
+ def deploy_token_header(value)
+ { Gitlab::Auth::AuthFinders::DEPLOY_TOKEN_HEADER => value }
+ end
+
shared_examples 'secure endpoint' do
before do
project.add_developer(user)
@@ -54,19 +83,35 @@ RSpec.describe API::GenericPackages do
'PUBLIC' | :guest | true | :personal_access_token | :forbidden
'PUBLIC' | :developer | true | :invalid_personal_access_token | :unauthorized
'PUBLIC' | :guest | true | :invalid_personal_access_token | :unauthorized
+ 'PUBLIC' | :developer | true | :user_basic_auth | :success
+ 'PUBLIC' | :guest | true | :user_basic_auth | :forbidden
+ 'PUBLIC' | :developer | true | :invalid_user_basic_auth | :unauthorized
+ 'PUBLIC' | :guest | true | :invalid_user_basic_auth | :unauthorized
'PUBLIC' | :developer | false | :personal_access_token | :forbidden
'PUBLIC' | :guest | false | :personal_access_token | :forbidden
'PUBLIC' | :developer | false | :invalid_personal_access_token | :unauthorized
'PUBLIC' | :guest | false | :invalid_personal_access_token | :unauthorized
+ 'PUBLIC' | :developer | false | :user_basic_auth | :forbidden
+ 'PUBLIC' | :guest | false | :user_basic_auth | :forbidden
+ 'PUBLIC' | :developer | false | :invalid_user_basic_auth | :unauthorized
+ 'PUBLIC' | :guest | false | :invalid_user_basic_auth | :unauthorized
'PUBLIC' | :anonymous | false | :none | :unauthorized
'PRIVATE' | :developer | true | :personal_access_token | :success
'PRIVATE' | :guest | true | :personal_access_token | :forbidden
'PRIVATE' | :developer | true | :invalid_personal_access_token | :unauthorized
'PRIVATE' | :guest | true | :invalid_personal_access_token | :unauthorized
+ 'PRIVATE' | :developer | true | :user_basic_auth | :success
+ 'PRIVATE' | :guest | true | :user_basic_auth | :forbidden
+ 'PRIVATE' | :developer | true | :invalid_user_basic_auth | :unauthorized
+ 'PRIVATE' | :guest | true | :invalid_user_basic_auth | :unauthorized
'PRIVATE' | :developer | false | :personal_access_token | :not_found
'PRIVATE' | :guest | false | :personal_access_token | :not_found
'PRIVATE' | :developer | false | :invalid_personal_access_token | :unauthorized
'PRIVATE' | :guest | false | :invalid_personal_access_token | :unauthorized
+ 'PRIVATE' | :developer | false | :user_basic_auth | :not_found
+ 'PRIVATE' | :guest | false | :user_basic_auth | :not_found
+ 'PRIVATE' | :developer | false | :invalid_user_basic_auth | :unauthorized
+ 'PRIVATE' | :guest | false | :invalid_user_basic_auth | :unauthorized
'PRIVATE' | :anonymous | false | :none | :unauthorized
'PUBLIC' | :developer | true | :job_token | :success
'PUBLIC' | :developer | true | :invalid_job_token | :unauthorized
@@ -90,6 +135,21 @@ RSpec.describe API::GenericPackages do
expect(response).to have_gitlab_http_status(expected_status)
end
end
+
+ where(:authenticate_with, :expected_status) do
+ :deploy_token_rw | :success
+ :deploy_token_wo | :success
+ :deploy_token_ro | :forbidden
+ :invalid_deploy_token | :unauthorized
+ end
+
+ with_them do
+ it "responds with #{params[:expected_status]}" do
+ authorize_upload_file(workhorse_header.merge(deploy_token_auth_header))
+
+ expect(response).to have_gitlab_http_status(expected_status)
+ end
+ end
end
context 'application security' do
@@ -138,20 +198,34 @@ RSpec.describe API::GenericPackages do
where(:project_visibility, :user_role, :member?, :authenticate_with, :expected_status) do
'PUBLIC' | :guest | true | :personal_access_token | :forbidden
+ 'PUBLIC' | :guest | true | :user_basic_auth | :forbidden
'PUBLIC' | :developer | true | :invalid_personal_access_token | :unauthorized
'PUBLIC' | :guest | true | :invalid_personal_access_token | :unauthorized
+ 'PUBLIC' | :developer | true | :invalid_user_basic_auth | :unauthorized
+ 'PUBLIC' | :guest | true | :invalid_user_basic_auth | :unauthorized
'PUBLIC' | :developer | false | :personal_access_token | :forbidden
'PUBLIC' | :guest | false | :personal_access_token | :forbidden
+ 'PUBLIC' | :developer | false | :user_basic_auth | :forbidden
+ 'PUBLIC' | :guest | false | :user_basic_auth | :forbidden
'PUBLIC' | :developer | false | :invalid_personal_access_token | :unauthorized
'PUBLIC' | :guest | false | :invalid_personal_access_token | :unauthorized
+ 'PUBLIC' | :developer | false | :invalid_user_basic_auth | :unauthorized
+ 'PUBLIC' | :guest | false | :invalid_user_basic_auth | :unauthorized
'PUBLIC' | :anonymous | false | :none | :unauthorized
'PRIVATE' | :guest | true | :personal_access_token | :forbidden
+ 'PRIVATE' | :guest | true | :user_basic_auth | :forbidden
'PRIVATE' | :developer | true | :invalid_personal_access_token | :unauthorized
'PRIVATE' | :guest | true | :invalid_personal_access_token | :unauthorized
+ 'PRIVATE' | :developer | true | :invalid_user_basic_auth | :unauthorized
+ 'PRIVATE' | :guest | true | :invalid_user_basic_auth | :unauthorized
'PRIVATE' | :developer | false | :personal_access_token | :not_found
'PRIVATE' | :guest | false | :personal_access_token | :not_found
+ 'PRIVATE' | :developer | false | :user_basic_auth | :not_found
+ 'PRIVATE' | :guest | false | :user_basic_auth | :not_found
'PRIVATE' | :developer | false | :invalid_personal_access_token | :unauthorized
'PRIVATE' | :guest | false | :invalid_personal_access_token | :unauthorized
+ 'PRIVATE' | :developer | false | :invalid_user_basic_auth | :unauthorized
+ 'PRIVATE' | :guest | false | :invalid_user_basic_auth | :unauthorized
'PRIVATE' | :anonymous | false | :none | :unauthorized
'PUBLIC' | :developer | true | :invalid_job_token | :unauthorized
'PUBLIC' | :developer | false | :job_token | :forbidden
@@ -175,6 +249,21 @@ RSpec.describe API::GenericPackages do
expect(response).to have_gitlab_http_status(expected_status)
end
end
+
+ where(:authenticate_with, :expected_status) do
+ :deploy_token_ro | :forbidden
+ :invalid_deploy_token | :unauthorized
+ end
+
+ with_them do
+ it "responds with #{params[:expected_status]}" do
+ headers = workhorse_header.merge(deploy_token_auth_header)
+
+ upload_file(params, headers)
+
+ expect(response).to have_gitlab_http_status(expected_status)
+ end
+ end
end
context 'when user can upload packages and has valid credentials' do
@@ -182,43 +271,58 @@ RSpec.describe API::GenericPackages do
project.add_developer(user)
end
- it 'creates package and package file when valid personal access token is used' do
- headers = workhorse_header.merge(personal_access_token_header)
+ shared_examples 'creates a package and package file' do
+ it 'creates a package and package file' do
+ headers = workhorse_header.merge(auth_header)
- expect { upload_file(params, headers) }
- .to change { project.packages.generic.count }.by(1)
- .and change { Packages::PackageFile.count }.by(1)
+ expect { upload_file(params, headers) }
+ .to change { project.packages.generic.count }.by(1)
+ .and change { Packages::PackageFile.count }.by(1)
- aggregate_failures do
- expect(response).to have_gitlab_http_status(:created)
+ aggregate_failures do
+ expect(response).to have_gitlab_http_status(:created)
- package = project.packages.generic.last
- expect(package.name).to eq('mypackage')
- expect(package.version).to eq('0.0.1')
- expect(package.original_build_info).to be_nil
+ package = project.packages.generic.last
+ expect(package.name).to eq('mypackage')
+ expect(package.version).to eq('0.0.1')
- package_file = package.package_files.last
- expect(package_file.file_name).to eq('myfile.tar.gz')
+ if should_set_build_info
+ expect(package.original_build_info.pipeline).to eq(ci_build.pipeline)
+ else
+ expect(package.original_build_info).to be_nil
+ end
+
+ package_file = package.package_files.last
+ expect(package_file.file_name).to eq('myfile.tar.gz')
+ end
end
end
- it 'creates package, package file, and package build info when valid job token is used' do
- headers = workhorse_header.merge(job_token_header)
-
- expect { upload_file(params, headers) }
- .to change { project.packages.generic.count }.by(1)
- .and change { Packages::PackageFile.count }.by(1)
+ context 'when valid personal access token is used' do
+ it_behaves_like 'creates a package and package file' do
+ let(:auth_header) { personal_access_token_header }
+ let(:should_set_build_info) { false }
+ end
+ end
- aggregate_failures do
- expect(response).to have_gitlab_http_status(:created)
+ context 'when valid basic auth is used' do
+ it_behaves_like 'creates a package and package file' do
+ let(:auth_header) { user_basic_auth_header(user) }
+ let(:should_set_build_info) { false }
+ end
+ end
- package = project.packages.generic.last
- expect(package.name).to eq('mypackage')
- expect(package.version).to eq('0.0.1')
- expect(package.original_build_info.pipeline).to eq(ci_build.pipeline)
+ context 'when valid deploy token is used' do
+ it_behaves_like 'creates a package and package file' do
+ let(:auth_header) { deploy_token_header(deploy_token_wo.token) }
+ let(:should_set_build_info) { false }
+ end
+ end
- package_file = package.package_files.last
- expect(package_file.file_name).to eq('myfile.tar.gz')
+ context 'when valid job token is used' do
+ it_behaves_like 'creates a package and package file' do
+ let(:auth_header) { job_token_header }
+ let(:should_set_build_info) { true }
end
end
@@ -309,21 +413,37 @@ RSpec.describe API::GenericPackages do
where(:project_visibility, :user_role, :member?, :authenticate_with, :expected_status) do
'PUBLIC' | :developer | true | :personal_access_token | :success
'PUBLIC' | :guest | true | :personal_access_token | :success
+ 'PUBLIC' | :developer | true | :user_basic_auth | :success
+ 'PUBLIC' | :guest | true | :user_basic_auth | :success
'PUBLIC' | :developer | true | :invalid_personal_access_token | :unauthorized
'PUBLIC' | :guest | true | :invalid_personal_access_token | :unauthorized
+ 'PUBLIC' | :developer | true | :invalid_user_basic_auth | :unauthorized
+ 'PUBLIC' | :guest | true | :invalid_user_basic_auth | :unauthorized
'PUBLIC' | :developer | false | :personal_access_token | :success
'PUBLIC' | :guest | false | :personal_access_token | :success
+ 'PUBLIC' | :developer | false | :user_basic_auth | :success
+ 'PUBLIC' | :guest | false | :user_basic_auth | :success
'PUBLIC' | :developer | false | :invalid_personal_access_token | :unauthorized
'PUBLIC' | :guest | false | :invalid_personal_access_token | :unauthorized
+ 'PUBLIC' | :developer | false | :invalid_user_basic_auth | :unauthorized
+ 'PUBLIC' | :guest | false | :invalid_user_basic_auth | :unauthorized
'PUBLIC' | :anonymous | false | :none | :unauthorized
'PRIVATE' | :developer | true | :personal_access_token | :success
'PRIVATE' | :guest | true | :personal_access_token | :forbidden
+ 'PRIVATE' | :developer | true | :user_basic_auth | :success
+ 'PRIVATE' | :guest | true | :user_basic_auth | :forbidden
'PRIVATE' | :developer | true | :invalid_personal_access_token | :unauthorized
'PRIVATE' | :guest | true | :invalid_personal_access_token | :unauthorized
+ 'PRIVATE' | :developer | true | :invalid_user_basic_auth | :unauthorized
+ 'PRIVATE' | :guest | true | :invalid_user_basic_auth | :unauthorized
'PRIVATE' | :developer | false | :personal_access_token | :not_found
'PRIVATE' | :guest | false | :personal_access_token | :not_found
+ 'PRIVATE' | :developer | false | :user_basic_auth | :not_found
+ 'PRIVATE' | :guest | false | :user_basic_auth | :not_found
'PRIVATE' | :developer | false | :invalid_personal_access_token | :unauthorized
'PRIVATE' | :guest | false | :invalid_personal_access_token | :unauthorized
+ 'PRIVATE' | :developer | false | :invalid_user_basic_auth | :unauthorized
+ 'PRIVATE' | :guest | false | :invalid_user_basic_auth | :unauthorized
'PRIVATE' | :anonymous | false | :none | :unauthorized
'PUBLIC' | :developer | true | :job_token | :success
'PUBLIC' | :developer | true | :invalid_job_token | :unauthorized
@@ -347,6 +467,21 @@ RSpec.describe API::GenericPackages do
expect(response).to have_gitlab_http_status(expected_status)
end
end
+
+ where(:authenticate_with, :expected_status) do
+ :deploy_token_rw | :success
+ :deploy_token_wo | :success
+ :deploy_token_ro | :success
+ :invalid_deploy_token | :unauthorized
+ end
+
+ with_them do
+ it "responds with #{params[:expected_status]}" do
+ download_file(deploy_token_auth_header)
+
+ expect(response).to have_gitlab_http_status(expected_status)
+ end
+ end
end
context 'event tracking' do
diff --git a/spec/support/shared_examples/services/boards/issues_list_service_shared_examples.rb b/spec/support/shared_examples/services/boards/issues_list_service_shared_examples.rb
index 06e2b715e6d..197b0694741 100644
--- a/spec/support/shared_examples/services/boards/issues_list_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/boards/issues_list_service_shared_examples.rb
@@ -19,78 +19,12 @@ RSpec.shared_examples 'issues list service' do
end
end
- it 'avoids N+1' do
- params = { board_id: board.id }
- control = ActiveRecord::QueryRecorder.new { described_class.new(parent, user, params).execute }
-
- create(:list, board: board)
-
- expect { described_class.new(parent, user, params).execute }.not_to exceed_query_limit(control)
- end
-
- context 'issues are ordered by priority' do
- it 'returns opened issues when list_id is missing' do
- params = { board_id: board.id }
-
- issues = described_class.new(parent, user, params).execute
-
- expect(issues).to eq [opened_issue2, reopened_issue1, opened_issue1]
- end
-
- it 'returns opened issues when listing issues from Backlog' do
- params = { board_id: board.id, id: backlog.id }
-
- issues = described_class.new(parent, user, params).execute
-
- expect(issues).to eq [opened_issue2, reopened_issue1, opened_issue1]
- end
-
- it 'returns opened issues that have label list applied when listing issues from a label list' do
- params = { board_id: board.id, id: list1.id }
-
- issues = described_class.new(parent, user, params).execute
-
- expect(issues).to eq [list1_issue3, list1_issue1, list1_issue2]
- end
- end
-
- context 'issues are ordered by date of closing' do
- it 'returns closed issues when listing issues from Closed' do
- params = { board_id: board.id, id: closed.id }
-
- issues = described_class.new(parent, user, params).execute
-
- expect(issues).to eq [closed_issue1, closed_issue2, closed_issue3, closed_issue4, closed_issue5]
- end
- end
-
- context 'with list that does not belong to the board' do
- it 'raises an error' do
- list = create(:list)
- service = described_class.new(parent, user, board_id: board.id, id: list.id)
-
- expect { service.execute }.to raise_error(ActiveRecord::RecordNotFound)
- end
- end
-
- context 'with invalid list id' do
- it 'raises an error' do
- service = described_class.new(parent, user, board_id: board.id, id: nil)
-
- expect { service.execute }.to raise_error(ActiveRecord::RecordNotFound)
- end
- end
-
- context 'when :all_lists is used' do
- it 'returns issues from all lists' do
- params = { board_id: board.id, all_lists: true }
-
- issues = described_class.new(parent, user, params).execute
-
- expected = [opened_issue2, reopened_issue1, opened_issue1, list1_issue1,
- list1_issue2, list1_issue3, list2_issue1, closed_issue1,
- closed_issue2, closed_issue3, closed_issue4, closed_issue5]
- expect(issues).to match_array(expected)
- end
+ it_behaves_like 'items list service' do
+ let(:backlog_items) { [opened_issue2, reopened_issue1, opened_issue1] }
+ let(:list1_items) { [list1_issue3, list1_issue1, list1_issue2] }
+ let(:closed_items) { [closed_issue1, closed_issue2, closed_issue3, closed_issue4, closed_issue5] }
+ let(:all_items) { backlog_items + list1_items + closed_items + [list2_issue1] }
+ let(:list_factory) { :list }
+ let(:new_list) { create(:list, board: board) }
end
end
diff --git a/spec/support/shared_examples/services/boards/items_list_service_shared_examples.rb b/spec/support/shared_examples/services/boards/items_list_service_shared_examples.rb
new file mode 100644
index 00000000000..9a3a0cc9cc8
--- /dev/null
+++ b/spec/support/shared_examples/services/boards/items_list_service_shared_examples.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'items list service' do
+ it 'avoids N+1' do
+ params = { board_id: board.id }
+ control = ActiveRecord::QueryRecorder.new { described_class.new(parent, user, params).execute }
+
+ new_list
+
+ expect { described_class.new(parent, user, params).execute }.not_to exceed_query_limit(control)
+ end
+
+ it 'returns opened items when list_id is missing' do
+ params = { board_id: board.id }
+
+ items = described_class.new(parent, user, params).execute
+
+ expect(items).to match_array(backlog_items)
+ end
+
+ it 'returns opened items when listing items from Backlog' do
+ params = { board_id: board.id, id: backlog.id }
+
+ items = described_class.new(parent, user, params).execute
+
+ expect(items).to match_array(backlog_items)
+ end
+
+ it 'returns opened items that have label list applied when listing items from a label list' do
+ params = { board_id: board.id, id: list1.id }
+
+ items = described_class.new(parent, user, params).execute
+
+ expect(items).to match_array(list1_items)
+ end
+
+ it 'returns closed items when listing items from Closed sorted by closed_at in descending order' do
+ params = { board_id: board.id, id: closed.id }
+
+ items = described_class.new(parent, user, params).execute
+
+ expect(items).to eq(closed_items)
+ end
+
+ it 'raises an error if the list does not belong to the board' do
+ list = create(list_factory) # rubocop:disable Rails/SaveBang
+ service = described_class.new(parent, user, board_id: board.id, id: list.id)
+
+ expect { service.execute }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'raises an error if list id is invalid' do
+ service = described_class.new(parent, user, board_id: board.id, id: nil)
+
+ expect { service.execute }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'returns items from all lists if :all_list is used' do
+ params = { board_id: board.id, all_lists: true }
+
+ items = described_class.new(parent, user, params).execute
+
+ expect(items).to match_array(all_items)
+ end
+end
diff --git a/spec/views/notify/pipeline_failed_email.html.haml_spec.rb b/spec/views/notify/pipeline_failed_email.html.haml_spec.rb
index b41933f9c36..defd8190eda 100644
--- a/spec/views/notify/pipeline_failed_email.html.haml_spec.rb
+++ b/spec/views/notify/pipeline_failed_email.html.haml_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe 'notify/pipeline_failed_email.html.haml' do
it_behaves_like 'pipeline status changes email' do
- let(:title) { 'Your pipeline has failed' }
+ let(:title) { "Pipeline ##{pipeline.id} has failed!" }
let(:status) { :failed }
end
end
diff --git a/spec/views/notify/pipeline_failed_email.text.erb_spec.rb b/spec/views/notify/pipeline_failed_email.text.erb_spec.rb
index a5c31632b0d..dd637af5137 100644
--- a/spec/views/notify/pipeline_failed_email.text.erb_spec.rb
+++ b/spec/views/notify/pipeline_failed_email.text.erb_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe 'notify/pipeline_failed_email.text.erb' do
it 'renders the email correctly' do
render
- expect(rendered).to have_content('Your pipeline has failed')
+ expect(rendered).to have_content("Pipeline ##{pipeline.id} has failed!")
expect(rendered).to have_content(pipeline.project.name)
expect(rendered).to have_content(pipeline.git_commit_message.truncate(50).gsub(/\s+/, ' '))
expect(rendered).to have_content(pipeline.commit.author_name)
diff --git a/spec/views/notify/pipeline_fixed_email.html.haml_spec.rb b/spec/views/notify/pipeline_fixed_email.html.haml_spec.rb
index 2b3b08f8e8c..bdfc8fb5f6b 100644
--- a/spec/views/notify/pipeline_fixed_email.html.haml_spec.rb
+++ b/spec/views/notify/pipeline_fixed_email.html.haml_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe 'notify/pipeline_fixed_email.html.haml' do
it_behaves_like 'pipeline status changes email' do
- let(:title) { 'Your pipeline has been fixed!' }
+ let(:title) { "Pipeline has been fixed and ##{pipeline.id} has passed!" }
let(:status) { :success }
end
end
diff --git a/spec/views/notify/pipeline_fixed_email.text.erb_spec.rb b/spec/views/notify/pipeline_fixed_email.text.erb_spec.rb
index 8640998acaa..d0bc110f95c 100644
--- a/spec/views/notify/pipeline_fixed_email.text.erb_spec.rb
+++ b/spec/views/notify/pipeline_fixed_email.text.erb_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe 'notify/pipeline_fixed_email.text.erb' do
it_behaves_like 'pipeline status changes email' do
- let(:title) { 'Your pipeline has been fixed!' }
+ let(:title) { "Pipeline has been fixed and ##{pipeline.id} has passed!" }
let(:status) { :success }
end
end
diff --git a/spec/views/notify/pipeline_success_email.html.haml_spec.rb b/spec/views/notify/pipeline_success_email.html.haml_spec.rb
index 51ea7ef5066..ce03f672700 100644
--- a/spec/views/notify/pipeline_success_email.html.haml_spec.rb
+++ b/spec/views/notify/pipeline_success_email.html.haml_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe 'notify/pipeline_success_email.html.haml' do
it_behaves_like 'pipeline status changes email' do
- let(:title) { 'Your pipeline has passed' }
+ let(:title) { "Pipeline ##{pipeline.id} has passed!" }
let(:status) { :success }
end
end
diff --git a/spec/views/notify/pipeline_success_email.text.erb_spec.rb b/spec/views/notify/pipeline_success_email.text.erb_spec.rb
index 3acf4dd3e26..02334a48fa3 100644
--- a/spec/views/notify/pipeline_success_email.text.erb_spec.rb
+++ b/spec/views/notify/pipeline_success_email.text.erb_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe 'notify/pipeline_success_email.text.erb' do
it_behaves_like 'pipeline status changes email' do
- let(:title) { 'Your pipeline has passed' }
+ let(:title) { "Pipeline ##{pipeline.id} has passed!" }
let(:status) { :success }
end
end