Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b6d0149b92
commit
b5e513dbef
|
@ -59,5 +59,17 @@ Other settings you might want to include when creating the issue.
|
|||
# /epic &
|
||||
-->
|
||||
|
||||
## Verification steps
|
||||
<!--
|
||||
Add verification steps to help GitLab team members test the implementation. This is particularly useful
|
||||
during the MR review and the ~"workflow::verification" step. You may not know exactly what the
|
||||
verification steps should be during issue refinement, so you can always come back later to add
|
||||
them.
|
||||
|
||||
1. Check-out the corresponding branch
|
||||
1. ...
|
||||
1. Profit!
|
||||
-->
|
||||
|
||||
/label ~"workflow::refinement"
|
||||
/milestone %Backlog
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module Ci
|
||||
class JobKindEnum < BaseEnum
|
||||
graphql_name 'CiJobKind'
|
||||
|
||||
value 'BUILD', value: ::Ci::Build, description: 'Standard CI job.'
|
||||
value 'BRIDGE', value: ::Ci::Bridge, description: 'Bridge CI job connecting a parent and child pipeline.'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -17,6 +17,8 @@ module Types
|
|||
description: 'Duration of the job in seconds.'
|
||||
field :id, ::Types::GlobalIDType[::CommitStatus].as('JobID'), null: true,
|
||||
description: 'ID of the job.'
|
||||
field :kind, type: ::Types::Ci::JobKindEnum, null: false,
|
||||
description: 'Indicates the type of job.'
|
||||
field :name, GraphQL::Types::String, null: true,
|
||||
description: 'Name of the job.'
|
||||
field :needs, BuildNeedType.connection_type, null: true,
|
||||
|
@ -87,6 +89,12 @@ module Types
|
|||
field :triggered, GraphQL::Types::Boolean, null: true,
|
||||
description: 'Whether the job was triggered.'
|
||||
|
||||
def kind
|
||||
return ::Ci::Build unless [::Ci::Build, ::Ci::Bridge].include?(object.class)
|
||||
|
||||
object.class
|
||||
end
|
||||
|
||||
def pipeline
|
||||
Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Pipeline, object.pipeline_id).find
|
||||
end
|
||||
|
|
|
@ -134,14 +134,14 @@ module SortingHelper
|
|||
)
|
||||
end
|
||||
|
||||
def milestone_sort_options_hash
|
||||
def milestones_sort_options_hash
|
||||
{
|
||||
sort_value_name => sort_title_name_asc,
|
||||
sort_value_name_desc => sort_title_name_desc,
|
||||
sort_value_due_date_later => sort_title_due_date_later,
|
||||
sort_value_due_date_soon => sort_title_due_date_soon,
|
||||
sort_value_due_date_later => sort_title_due_date_later,
|
||||
sort_value_start_date_soon => sort_title_start_date_soon,
|
||||
sort_value_start_date_later => sort_title_start_date_later,
|
||||
sort_value_start_date_soon => sort_title_start_date_soon
|
||||
sort_value_name => sort_title_name_asc,
|
||||
sort_value_name_desc => sort_title_name_desc
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -22,6 +22,10 @@ module Emails
|
|||
review = Review.find_by_id(review_id)
|
||||
|
||||
@notes = review.notes
|
||||
@discussions = Discussion.build_discussions(review.discussion_ids, preload_note_diff_file: true)
|
||||
@include_diff_discussion_stylesheet = @discussions.values.any? do |discussion|
|
||||
discussion.diff_discussion? && discussion.on_text?
|
||||
end
|
||||
@author = review.author
|
||||
@author_name = review.author_name
|
||||
@project = review.project
|
||||
|
|
|
@ -47,6 +47,14 @@ class Discussion
|
|||
grouped_notes.values.map { |notes| build(notes, context_noteable) }
|
||||
end
|
||||
|
||||
def self.build_discussions(discussion_ids, context_noteable = nil, preload_note_diff_file: false)
|
||||
notes = Note.where(discussion_id: discussion_ids).fresh
|
||||
notes = notes.inc_note_diff_file if preload_note_diff_file
|
||||
|
||||
grouped_notes = notes.group_by { |n| n.discussion_id }
|
||||
grouped_notes.transform_values { |notes| Discussion.build(notes, context_noteable) }
|
||||
end
|
||||
|
||||
def self.lazy_find(discussion_id)
|
||||
BatchLoader.for(discussion_id).batch do |discussion_ids, loader|
|
||||
results = Note.where(discussion_id: discussion_ids).fresh.to_a.group_by(&:discussion_id)
|
||||
|
|
|
@ -121,6 +121,7 @@ class Note < ApplicationRecord
|
|||
scope :with_discussion_ids, ->(discussion_ids) { where(discussion_id: discussion_ids) }
|
||||
scope :with_suggestions, -> { joins(:suggestions) }
|
||||
scope :inc_author, -> { includes(:author) }
|
||||
scope :inc_note_diff_file, -> { includes(:note_diff_file) }
|
||||
scope :with_api_entity_associations, -> { preload(:note_diff_file, :author) }
|
||||
scope :inc_relations_for_view, -> do
|
||||
includes({ project: :group }, { author: :status }, :updated_by, :resolved_by, :award_emoji,
|
||||
|
|
|
@ -14,6 +14,10 @@ class Review < ApplicationRecord
|
|||
|
||||
participant :author
|
||||
|
||||
def discussion_ids
|
||||
notes.select(:discussion_id)
|
||||
end
|
||||
|
||||
def all_references(current_user = nil, extractor: nil)
|
||||
ext = super
|
||||
|
||||
|
|
|
@ -2,17 +2,18 @@
|
|||
- diff_limit = local_assigns.fetch(:diff_limit, nil)
|
||||
- target_url = local_assigns.fetch(:target_url, @target_url)
|
||||
- note_style = local_assigns.fetch(:note_style, "")
|
||||
- skip_stylesheet_link = local_assigns.fetch(:skip_stylesheet_link, false)
|
||||
- include_stylesheet_link = local_assigns.fetch(:include_stylesheet_link, true)
|
||||
|
||||
- discussion = note.discussion if note.part_of_discussion?
|
||||
- author = local_assigns.fetch(:author) { note.author }
|
||||
- discussion = local_assigns.fetch(:discussion) { note.discussion } if note.part_of_discussion?
|
||||
|
||||
%p{ style: "color: #777777;" }
|
||||
= succeed ':' do
|
||||
= link_to note.author_name, user_url(note.author)
|
||||
= link_to author.name, user_url(author)
|
||||
- if discussion.nil?
|
||||
= link_to 'commented', target_url
|
||||
- else
|
||||
- if note.start_of_discussion?
|
||||
- if discussion.first_note == note
|
||||
started a new
|
||||
- else
|
||||
commented on a
|
||||
|
@ -22,10 +23,9 @@
|
|||
- else
|
||||
= link_to 'discussion', target_url
|
||||
|
||||
- if discussion&.diff_discussion? && discussion.on_text?
|
||||
- unless skip_stylesheet_link
|
||||
= content_for :head do
|
||||
= stylesheet_link_tag 'mailers/highlighted_diff_email'
|
||||
- if include_stylesheet_link && discussion&.diff_discussion? && discussion.on_text?
|
||||
= content_for :head do
|
||||
= stylesheet_link_tag 'mailers/highlighted_diff_email'
|
||||
|
||||
%table.code.gl-mb-5
|
||||
= render partial: "projects/diffs/email_line",
|
||||
|
@ -34,4 +34,4 @@
|
|||
locals: { diff_file: discussion.diff_file }
|
||||
|
||||
.md{ style: note_style }
|
||||
= markdown(note.note, pipeline: :email, author: note.author, current_user: @recipient, issuable_reference_expansion_enabled: true)
|
||||
= markdown(note.note, pipeline: :email, author: author, current_user: @recipient, issuable_reference_expansion_enabled: true)
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
<% note = local_assigns.fetch(:note, @note) -%>
|
||||
<% diff_limit = local_assigns.fetch(:diff_limit, nil) -%>
|
||||
<% target_url = local_assigns.fetch(:target_url, @target_url) -%>
|
||||
<% discussion = note.discussion if note.part_of_discussion? -%>
|
||||
<% author = local_assigns.fetch(:author) { note.author } -%>
|
||||
<% discussion = local_assigns.fetch(:discussion) { note.discussion } if note.part_of_discussion? -%>
|
||||
|
||||
<%= sanitize_name(note.author_name) -%>
|
||||
<%= sanitize_name(author.name) -%>
|
||||
<% if discussion.nil? -%>
|
||||
<%= 'commented' -%>:
|
||||
<% else -%>
|
||||
<% if note.start_of_discussion? -%>
|
||||
<% if discussion.first_note == note -%>
|
||||
<%= 'started a new discussion' -%>
|
||||
<% else -%>
|
||||
<%= 'commented on a discussion' -%>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
= content_for :head do
|
||||
= stylesheet_link_tag 'mailers/highlighted_diff_email'
|
||||
- if @include_diff_discussion_stylesheet
|
||||
= content_for :head do
|
||||
= stylesheet_link_tag 'mailers/highlighted_diff_email'
|
||||
|
||||
%table{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;margin:0 auto;border-collapse:separate;border-spacing:0;" }
|
||||
%tbody
|
||||
|
@ -15,5 +16,9 @@
|
|||
%tr
|
||||
%td{ style: "overflow:hidden;font-size:14px;line-height:1.4;display:grid;" }
|
||||
- @notes.each do |note|
|
||||
-# Get preloaded note discussion
|
||||
- discussion = @discussions[note.discussion_id] if note.part_of_discussion?
|
||||
-# Preload project for discussions first note
|
||||
- discussion.first_note.project = @project if discussion&.first_note
|
||||
- target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{note.id}")
|
||||
= render 'note_email', note: note, diff_limit: 3, target_url: target_url, note_style: "border-bottom:1px solid #ededed;", skip_stylesheet_link: true
|
||||
= render 'note_email', note: note, diff_limit: 3, target_url: target_url, note_style: "border-bottom:1px solid #ededed;", include_stylesheet_link: false, discussion: discussion, author: @author
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
|
||||
--
|
||||
<% @notes.each_with_index do |note, index| %>
|
||||
<!-- Get preloaded note discussion-->
|
||||
<% discussion = @discussions[note.discussion_id] if note.part_of_discussion?%>
|
||||
<% target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{note.id}") %>
|
||||
<%= render 'note_email', note: note, diff_limit: 3, target_url: target_url %>
|
||||
<%= render 'note_email', note: note, diff_limit: 3, target_url: target_url, discussion: discussion, author: @author %>
|
||||
|
||||
<% if index != @notes.length-1 %>
|
||||
--
|
||||
|
|
|
@ -1,22 +1,4 @@
|
|||
.dropdown.inline.gl-ml-3
|
||||
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } }
|
||||
%span.light
|
||||
- if @sort.present?
|
||||
= milestone_sort_options_hash[@sort]
|
||||
- else
|
||||
= sort_title_due_date_soon
|
||||
= sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3')
|
||||
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-sort
|
||||
%li
|
||||
= link_to page_filter_path(sort: sort_value_due_date_soon) do
|
||||
= sort_title_due_date_soon
|
||||
= link_to page_filter_path(sort: sort_value_due_date_later) do
|
||||
= sort_title_due_date_later
|
||||
= link_to page_filter_path(sort: sort_value_start_date_soon) do
|
||||
= sort_title_start_date_soon
|
||||
= link_to page_filter_path(sort: sort_value_start_date_later) do
|
||||
= sort_title_start_date_later
|
||||
= link_to page_filter_path(sort: sort_value_name) do
|
||||
= sort_title_name_asc
|
||||
= link_to page_filter_path(sort: sort_value_name_desc) do
|
||||
= sort_title_name_desc
|
||||
- milestones_sort_options = milestones_sort_options_hash.map { |value, text| { value: value, text: text, href: page_filter_path(sort: value) } }
|
||||
|
||||
%div{ data: {testid: 'milestone_sort_by_dropdown'} }
|
||||
= gl_redirect_listbox_tag milestones_sort_options, @sort, class: 'gl-ml-3'
|
||||
|
|
|
@ -207,10 +207,6 @@ pages_deployments:
|
|||
- table: ci_builds
|
||||
column: ci_build_id
|
||||
on_delete: async_nullify
|
||||
project_pages_metadata:
|
||||
- table: ci_job_artifacts
|
||||
column: artifacts_archive_id
|
||||
on_delete: async_nullify
|
||||
requirements_management_test_reports:
|
||||
- table: ci_builds
|
||||
column: build_id
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class UntrackDeletionsOnCiJobArtifacts < Gitlab::Database::Migration[1.0]
|
||||
include Gitlab::Database::MigrationHelpers::LooseForeignKeyHelpers
|
||||
|
||||
enable_lock_retries!
|
||||
|
||||
def up
|
||||
untrack_record_deletions(:ci_job_artifacts)
|
||||
end
|
||||
|
||||
def down
|
||||
track_record_deletions(:ci_job_artifacts)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveProjectPagesMetadataArtifactsArchiveIdColumn < Gitlab::Database::Migration[1.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
remove_column :project_pages_metadata, :artifacts_archive_id
|
||||
end
|
||||
|
||||
def down
|
||||
unless column_exists?(:project_pages_metadata, :artifacts_archive_id)
|
||||
add_column :project_pages_metadata, :artifacts_archive_id, :bigint
|
||||
end
|
||||
|
||||
add_concurrent_index(
|
||||
:project_pages_metadata,
|
||||
:artifacts_archive_id,
|
||||
name: "index_project_pages_metadata_on_artifacts_archive_id"
|
||||
)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveLeftoverCiJobArtifactDeletions < Gitlab::Database::Migration[1.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
# Delete all pending record deletions in the public.ci_job_artifacts until
|
||||
# there are no more rows left.
|
||||
loop do
|
||||
result = execute <<~SQL
|
||||
DELETE FROM "loose_foreign_keys_deleted_records"
|
||||
WHERE
|
||||
("loose_foreign_keys_deleted_records"."partition", "loose_foreign_keys_deleted_records"."id") IN (
|
||||
SELECT "loose_foreign_keys_deleted_records"."partition", "loose_foreign_keys_deleted_records"."id"
|
||||
FROM "loose_foreign_keys_deleted_records"
|
||||
WHERE
|
||||
"loose_foreign_keys_deleted_records"."fully_qualified_table_name" = 'public.ci_job_artifacts' AND
|
||||
"loose_foreign_keys_deleted_records"."status" = 1
|
||||
LIMIT 100
|
||||
)
|
||||
SQL
|
||||
|
||||
break if result.cmd_tuples == 0
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
35aaf03898795800027c814a6f907af6f011bd5095cae7188234b46f4b2ebb90
|
|
@ -0,0 +1 @@
|
|||
56706c8d4139c63427838d37b7120c57fd39c7be4caee0ebba39a05cd3c453f7
|
|
@ -0,0 +1 @@
|
|||
ec5dfe48e13cdbce5fc0c67612cddf1a5b555b3c84e1869ee3a7f047f0f3f1a0
|
|
@ -19278,7 +19278,6 @@ ALTER SEQUENCE project_mirror_data_id_seq OWNED BY project_mirror_data.id;
|
|||
CREATE TABLE project_pages_metadata (
|
||||
project_id bigint NOT NULL,
|
||||
deployed boolean DEFAULT false NOT NULL,
|
||||
artifacts_archive_id bigint,
|
||||
pages_deployment_id bigint
|
||||
);
|
||||
|
||||
|
@ -28666,8 +28665,6 @@ CREATE UNIQUE INDEX index_project_mirror_data_on_project_id ON project_mirror_da
|
|||
|
||||
CREATE INDEX index_project_mirror_data_on_status ON project_mirror_data USING btree (status);
|
||||
|
||||
CREATE INDEX index_project_pages_metadata_on_artifacts_archive_id ON project_pages_metadata USING btree (artifacts_archive_id);
|
||||
|
||||
CREATE INDEX index_project_pages_metadata_on_pages_deployment_id ON project_pages_metadata USING btree (pages_deployment_id);
|
||||
|
||||
CREATE INDEX index_project_pages_metadata_on_project_id_and_deployed_is_true ON project_pages_metadata USING btree (project_id) WHERE (deployed = true);
|
||||
|
@ -30968,8 +30965,6 @@ CREATE TRIGGER chat_names_loose_fk_trigger AFTER DELETE ON chat_names REFERENCIN
|
|||
|
||||
CREATE TRIGGER ci_builds_loose_fk_trigger AFTER DELETE ON ci_builds REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records();
|
||||
|
||||
CREATE TRIGGER ci_job_artifacts_loose_fk_trigger AFTER DELETE ON ci_job_artifacts REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records();
|
||||
|
||||
CREATE TRIGGER ci_pipelines_loose_fk_trigger AFTER DELETE ON ci_pipelines REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records();
|
||||
|
||||
CREATE TRIGGER ci_runners_loose_fk_trigger AFTER DELETE ON ci_runners REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records();
|
||||
|
|
|
@ -282,7 +282,8 @@ Deployments created by users on GitLab Premium or higher include the `approvals`
|
|||
"web_url": "http://localhost:3000/project_6_bot"
|
||||
},
|
||||
"status": "approved",
|
||||
"created_at": "2022-02-24T20:22:30.097Z"
|
||||
"created_at": "2022-02-24T20:22:30.097Z",
|
||||
"comment": "Looks good to me"
|
||||
}
|
||||
],
|
||||
...
|
||||
|
@ -342,20 +343,7 @@ Deployments created by users on GitLab Premium or higher include the `approvals`
|
|||
{
|
||||
"status": "created",
|
||||
"pending_approval_count": 0,
|
||||
"approvals": [
|
||||
{
|
||||
"user": {
|
||||
"id": 49,
|
||||
"username": "project_6_bot",
|
||||
"name": "****",
|
||||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/e83ac685f68ea07553ad3054c738c709?s=80&d=identicon",
|
||||
"web_url": "http://localhost:3000/project_6_bot"
|
||||
},
|
||||
"status": "approved",
|
||||
"created_at": "2022-02-24T20:22:30.097Z"
|
||||
}
|
||||
],
|
||||
"approvals": [],
|
||||
...
|
||||
}
|
||||
```
|
||||
|
@ -420,7 +408,8 @@ Deployments created by users on GitLab Premium or higher include the `approvals`
|
|||
"web_url": "http://localhost:3000/project_6_bot"
|
||||
},
|
||||
"status": "approved",
|
||||
"created_at": "2022-02-24T20:22:30.097Z"
|
||||
"created_at": "2022-02-24T20:22:30.097Z",
|
||||
"comment": "Looks good to me"
|
||||
}
|
||||
],
|
||||
...
|
||||
|
|
|
@ -9353,6 +9353,7 @@ Represents the total number of issues and their weights for a particular day.
|
|||
| <a id="cijobduration"></a>`duration` | [`Int`](#int) | Duration of the job in seconds. |
|
||||
| <a id="cijobfinishedat"></a>`finishedAt` | [`Time`](#time) | When a job has finished running. |
|
||||
| <a id="cijobid"></a>`id` | [`JobID`](#jobid) | ID of the job. |
|
||||
| <a id="cijobkind"></a>`kind` | [`CiJobKind!`](#cijobkind) | Indicates the type of job. |
|
||||
| <a id="cijobmanualjob"></a>`manualJob` | [`Boolean`](#boolean) | Whether the job has a manual action. |
|
||||
| <a id="cijobname"></a>`name` | [`String`](#string) | Name of the job. |
|
||||
| <a id="cijobneeds"></a>`needs` | [`CiBuildNeedConnection`](#cibuildneedconnection) | References to builds that must complete before the jobs run. (see [Connections](#connections)) |
|
||||
|
@ -17772,6 +17773,13 @@ Values for YAML processor result.
|
|||
| <a id="ciconfigstatusinvalid"></a>`INVALID` | Configuration file is not valid. |
|
||||
| <a id="ciconfigstatusvalid"></a>`VALID` | Configuration file is valid. |
|
||||
|
||||
### `CiJobKind`
|
||||
|
||||
| Value | Description |
|
||||
| ----- | ----------- |
|
||||
| <a id="cijobkindbridge"></a>`BRIDGE` | Bridge CI job connecting a parent and child pipeline. |
|
||||
| <a id="cijobkindbuild"></a>`BUILD` | Standard CI job. |
|
||||
|
||||
### `CiJobStatus`
|
||||
|
||||
| Value | Description |
|
||||
|
|
|
@ -63,6 +63,15 @@ Example response:
|
|||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root",
|
||||
"created_at": "2012-09-22T14:13:35Z",
|
||||
"created_by": {
|
||||
"id": 2,
|
||||
"username": "john_doe",
|
||||
"name": "John Doe",
|
||||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root"
|
||||
},
|
||||
"expires_at": "2012-10-22T14:13:35Z",
|
||||
"access_level": 30,
|
||||
"group_saml_identity": null,
|
||||
|
@ -75,6 +84,15 @@ Example response:
|
|||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root",
|
||||
"created_at": "2012-09-22T14:13:35Z",
|
||||
"created_by": {
|
||||
"id": 1,
|
||||
"username": "raymond_smith",
|
||||
"name": "Raymond Smith",
|
||||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root"
|
||||
},
|
||||
"expires_at": "2012-10-22T14:13:35Z",
|
||||
"access_level": 30,
|
||||
"email": "john@example.com",
|
||||
|
@ -132,6 +150,15 @@ Example response:
|
|||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root",
|
||||
"created_at": "2012-09-22T14:13:35Z",
|
||||
"created_by": {
|
||||
"id": 2,
|
||||
"username": "john_doe",
|
||||
"name": "John Doe",
|
||||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root"
|
||||
},
|
||||
"expires_at": "2012-10-22T14:13:35Z",
|
||||
"access_level": 30,
|
||||
"group_saml_identity": null,
|
||||
|
@ -144,6 +171,15 @@ Example response:
|
|||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root",
|
||||
"created_at": "2012-09-22T14:13:35Z",
|
||||
"created_by": {
|
||||
"id": 1,
|
||||
"username": "raymond_smith",
|
||||
"name": "Raymond Smith",
|
||||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root"
|
||||
},
|
||||
"expires_at": "2012-10-22T14:13:35Z",
|
||||
"access_level": 30,
|
||||
"email": "john@example.com",
|
||||
|
@ -161,6 +197,15 @@ Example response:
|
|||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root",
|
||||
"created_at": "2012-10-22T14:13:35Z",
|
||||
"created_by": {
|
||||
"id": 2,
|
||||
"username": "john_doe",
|
||||
"name": "John Doe",
|
||||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root"
|
||||
},
|
||||
"expires_at": "2012-11-22T14:13:35Z",
|
||||
"access_level": 30,
|
||||
"group_saml_identity": null,
|
||||
|
@ -201,6 +246,14 @@ Example response:
|
|||
"access_level": 30,
|
||||
"email": "john@example.com",
|
||||
"created_at": "2012-10-22T14:13:35Z",
|
||||
"created_by": {
|
||||
"id": 2,
|
||||
"username": "john_doe",
|
||||
"name": "John Doe",
|
||||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root"
|
||||
},
|
||||
"expires_at": null,
|
||||
"group_saml_identity": null,
|
||||
"membership_state": "active"
|
||||
|
@ -239,6 +292,15 @@ Example response:
|
|||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root",
|
||||
"access_level": 30,
|
||||
"created_at": "2012-10-22T14:13:35Z",
|
||||
"created_by": {
|
||||
"id": 2,
|
||||
"username": "john_doe",
|
||||
"name": "John Doe",
|
||||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root"
|
||||
},
|
||||
"email": "john@example.com",
|
||||
"expires_at": null,
|
||||
"group_saml_identity": null,
|
||||
|
@ -454,6 +516,15 @@ Example response:
|
|||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root",
|
||||
"created_at": "2012-10-22T14:13:35Z",
|
||||
"created_by": {
|
||||
"id": 2,
|
||||
"username": "john_doe",
|
||||
"name": "John Doe",
|
||||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root"
|
||||
},
|
||||
"expires_at": "2012-10-22T14:13:35Z",
|
||||
"access_level": 30,
|
||||
"email": "john@example.com",
|
||||
|
@ -492,6 +563,15 @@ Example response:
|
|||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root",
|
||||
"created_at": "2012-10-22T14:13:35Z",
|
||||
"created_by": {
|
||||
"id": 2,
|
||||
"username": "john_doe",
|
||||
"name": "John Doe",
|
||||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root"
|
||||
},
|
||||
"expires_at": "2012-10-22T14:13:35Z",
|
||||
"access_level": 40,
|
||||
"email": "john@example.com",
|
||||
|
@ -529,6 +609,15 @@ Example response:
|
|||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root",
|
||||
"created_at": "2012-10-22T14:13:35Z",
|
||||
"created_by": {
|
||||
"id": 2,
|
||||
"username": "john_doe",
|
||||
"name": "John Doe",
|
||||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root"
|
||||
},
|
||||
"expires_at": "2012-10-22T14:13:35Z",
|
||||
"access_level": 40,
|
||||
"email": "john@example.com",
|
||||
|
@ -566,6 +655,15 @@ Example response:
|
|||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root",
|
||||
"created_at": "2012-10-22T14:13:35Z",
|
||||
"created_by": {
|
||||
"id": 2,
|
||||
"username": "john_doe",
|
||||
"name": "John Doe",
|
||||
"state": "active",
|
||||
"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
|
||||
"web_url": "http://192.168.1.8:3000/root"
|
||||
},
|
||||
"expires_at": "2012-10-22",
|
||||
"access_level": 40,
|
||||
"email": "john@example.com",
|
||||
|
|
|
@ -76,36 +76,36 @@ Certain operations can only be performed according to the
|
|||
|
||||
GitLab CI/CD features, grouped by DevOps stage, include:
|
||||
|
||||
| Feature | Description |
|
||||
|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **Configure** | |
|
||||
| [Auto DevOps](../topics/autodevops/index.md) | Set up your app's entire lifecycle. |
|
||||
| [ChatOps](chatops/index.md) | Trigger CI jobs from chat, with results sent back to the channel. |
|
||||
| [Connect to cloud services](cloud_services/index.md) | Connect to cloud providers using OpenID Connect (OIDC) to retrieve temporary credentials to access services or secrets. |
|
||||
| **Verify** | |
|
||||
| [Browser Performance Testing](../user/project/merge_requests/browser_performance_testing.md) | Quickly determine the browser performance impact of pending code changes. |
|
||||
| [Load Performance Testing](../user/project/merge_requests/load_performance_testing.md) | Quickly determine the server performance impact of pending code changes. |
|
||||
| [CI services](services/index.md) | Link Docker containers with your base image. |
|
||||
| [GitLab CI/CD for external repositories](ci_cd_for_external_repos/index.md) | Get the benefits of GitLab CI/CD combined with repositories in GitHub and Bitbucket Cloud. |
|
||||
| [Interactive Web Terminals](interactive_web_terminal/index.md) | Open an interactive web terminal to debug the running jobs. |
|
||||
| [Review Apps](review_apps/index.md) | Configure GitLab CI/CD to preview code changes. |
|
||||
| [Unit test reports](unit_test_reports.md) | Identify test failures directly on merge requests. |
|
||||
| [Using Docker images](docker/using_docker_images.md) | Use GitLab and GitLab Runner with Docker to build and test applications. |
|
||||
| **Release** | |
|
||||
| [Auto Deploy](../topics/autodevops/stages.md#auto-deploy) | Deploy your application to a production environment in a Kubernetes cluster. |
|
||||
| [Building Docker images](docker/using_docker_build.md) | Maintain Docker-based projects using GitLab CI/CD. |
|
||||
| [Canary Deployments](../user/project/canary_deployments.md) | Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature. |
|
||||
| [Deploy boards](../user/project/deploy_boards.md) | Check the current health and status of each CI/CD environment running on Kubernetes. |
|
||||
| [Feature Flags](../operations/feature_flags.md) | Deploy your features behind Feature Flags. |
|
||||
| [GitLab Pages](../user/project/pages/index.md) | Deploy static websites. |
|
||||
| [GitLab Releases](../user/project/releases/index.md) | Add release notes to Git tags. |
|
||||
| [Cloud deployment](cloud_deployment/index.md) | Deploy your application to a main cloud provider. |
|
||||
| **Secure** | |
|
||||
| [Code Quality](../user/project/merge_requests/code_quality.md) | Analyze your source code quality. |
|
||||
| [Container Scanning](../user/application_security/container_scanning/index.md) | Check your Docker containers for known vulnerabilities. |
|
||||
| [Dependency Scanning](../user/application_security/dependency_scanning/index.md) | Analyze your dependencies for known vulnerabilities. |
|
||||
| [License Compliance](../user/compliance/license_compliance/index.md) | Search your project dependencies for their licenses. |
|
||||
| [Security Test reports](../user/application_security/index.md) | Check for app vulnerabilities. |
|
||||
| Feature | Description |
|
||||
|:---------------------------------------------------------------------------------------------|:------------|
|
||||
| **Configure** | |
|
||||
| [Auto DevOps](../topics/autodevops/index.md) | Set up your app's entire lifecycle. |
|
||||
| [ChatOps](chatops/index.md) | Trigger CI jobs from chat, with results sent back to the channel. |
|
||||
| [Connect to cloud services](cloud_services/index.md) | Connect to cloud providers using OpenID Connect (OIDC) to retrieve temporary credentials to access services or secrets. |
|
||||
| **Verify** | |
|
||||
| [Browser Performance Testing](../user/project/merge_requests/browser_performance_testing.md) | Quickly determine the browser performance impact of pending code changes. |
|
||||
| [Load Performance Testing](../user/project/merge_requests/load_performance_testing.md) | Quickly determine the server performance impact of pending code changes. |
|
||||
| [CI services](services/index.md) | Link Docker containers with your base image. |
|
||||
| [GitLab CI/CD for external repositories](ci_cd_for_external_repos/index.md) | Get the benefits of GitLab CI/CD combined with repositories in GitHub and Bitbucket Cloud. |
|
||||
| [Interactive Web Terminals](interactive_web_terminal/index.md) | Open an interactive web terminal to debug the running jobs. |
|
||||
| [Review Apps](review_apps/index.md) | Configure GitLab CI/CD to preview code changes. |
|
||||
| [Unit test reports](unit_test_reports.md) | Identify test failures directly on merge requests. |
|
||||
| [Using Docker images](docker/using_docker_images.md) | Use GitLab and GitLab Runner with Docker to build and test applications. |
|
||||
| **Release** | |
|
||||
| [Auto Deploy](../topics/autodevops/stages.md#auto-deploy) | Deploy your application to a production environment in a Kubernetes cluster. |
|
||||
| [Building Docker images](docker/using_docker_build.md) | Maintain Docker-based projects using GitLab CI/CD. |
|
||||
| [Canary Deployments](../user/project/canary_deployments.md) | Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature. |
|
||||
| [Deploy boards](../user/project/deploy_boards.md) | Check the current health and status of each CI/CD environment running on Kubernetes. |
|
||||
| [Feature Flags](../operations/feature_flags.md) | Deploy your features behind Feature Flags. |
|
||||
| [GitLab Pages](../user/project/pages/index.md) | Deploy static websites. |
|
||||
| [GitLab Releases](../user/project/releases/index.md) | Add release notes to Git tags. |
|
||||
| [Cloud deployment](cloud_deployment/index.md) | Deploy your application to a main cloud provider. |
|
||||
| **Secure** | |
|
||||
| [Code Quality](../user/project/merge_requests/code_quality.md) | Analyze your source code quality. |
|
||||
| [Container Scanning](../user/application_security/container_scanning/index.md) | Check your Docker containers for known vulnerabilities. |
|
||||
| [Dependency Scanning](../user/application_security/dependency_scanning/index.md) | Analyze your dependencies for known vulnerabilities. |
|
||||
| [License Compliance](../user/compliance/license_compliance/index.md) | Search your project dependencies for their licenses. |
|
||||
| [Security Test reports](../user/application_security/index.md) | Check for app vulnerabilities. |
|
||||
|
||||
## Examples
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ the following table) as these were used for development and testing:
|
|||
| GitLab version | Minimum PostgreSQL version |
|
||||
|----------------|----------------------------|
|
||||
| 13.0 | 11 |
|
||||
| 14.0 | 12 |
|
||||
| 14.0 | 12.10 |
|
||||
|
||||
You must also ensure the following extensions are loaded into every
|
||||
GitLab database. [Read more about this requirement, and troubleshooting](postgresql_extensions.md).
|
||||
|
|
|
@ -398,6 +398,8 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
|
|||
|
||||
### 14.8.0
|
||||
|
||||
- If upgrading from a version earlier than 14.6.5, 14.7.4, or 14.8.2, please review the [Critical Security Release: 14.8.2, 14.7.4, and 14.6.5](https://about.gitlab.com/releases/2022/02/25/critical-security-release-gitlab-14-8-2-released/) blog post.
|
||||
Updating to 14.8.2 or later will reset runner registration tokens for your groups and projects.
|
||||
- The agent server for Kubernetes [is enabled by default](https://about.gitlab.com/releases/2022/02/22/gitlab-14-8-released/#the-agent-server-for-kubernetes-is-enabled-by-default)
|
||||
on Omnibus installations. If you run GitLab at scale,
|
||||
such as [the reference architectures](../administration/reference_architectures/index.md),
|
||||
|
@ -421,11 +423,15 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
|
|||
### 14.7.0
|
||||
|
||||
- See [LFS objects import and mirror issue in GitLab 14.6.0 to 14.7.2](#lfs-objects-import-and-mirror-issue-in-gitlab-1460-to-1472).
|
||||
- If upgrading from a version earlier than 14.6.5, 14.7.4, or 14.8.2, please review the [Critical Security Release: 14.8.2, 14.7.4, and 14.6.5](https://about.gitlab.com/releases/2022/02/25/critical-security-release-gitlab-14-8-2-released/) blog post.
|
||||
Updating to 14.7.4 or later will reset runner registration tokens for your groups and projects.
|
||||
|
||||
### 14.6.0
|
||||
|
||||
- See [LFS objects import and mirror issue in GitLab 14.6.0 to 14.7.2](#lfs-objects-import-and-mirror-issue-in-gitlab-1460-to-1472).
|
||||
|
||||
- If upgrading from a version earlier than 14.6.5, 14.7.4, or 14.8.2, please review the [Critical Security Release: 14.8.2, 14.7.4, and 14.6.5](https://about.gitlab.com/releases/2022/02/25/critical-security-release-gitlab-14-8-2-released/) blog post.
|
||||
Updating to 14.6.5 or later will reset runner registration tokens for your groups and projects.
|
||||
|
||||
### 14.5.0
|
||||
|
||||
- When `make` is run, Gitaly builds are now created in `_build/bin` and no longer in the root directory of the source directory. If you
|
||||
|
|
|
@ -6,6 +6,7 @@ module API
|
|||
expose :user, merge: true, using: UserBasic
|
||||
expose :access_level
|
||||
expose :created_at
|
||||
expose :created_by, with: UserBasic, expose_nil: false
|
||||
expose :expires_at
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,7 +17,7 @@ RSpec.describe 'Milestones sorting', :js do
|
|||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'visit group milestones and sort by due_date_asc' do
|
||||
it 'visit group milestones and sort by due_date_asc', :js do
|
||||
visit group_milestones_path(group)
|
||||
|
||||
expect(page).to have_button('Due soon')
|
||||
|
@ -27,13 +27,13 @@ RSpec.describe 'Milestones sorting', :js do
|
|||
expect(page.all('ul.content-list > li strong > a').map(&:text)).to eq(['v2.0', 'v2.0', 'v3.0', 'v1.0', 'v1.0'])
|
||||
end
|
||||
|
||||
click_button 'Due soon'
|
||||
within '[data-testid=milestone_sort_by_dropdown]' do
|
||||
click_button 'Due soon'
|
||||
expect(find('.gl-new-dropdown-contents').all('.gl-new-dropdown-item-text-wrapper p').map(&:text)).to eq(['Due soon', 'Due later', 'Start soon', 'Start later', 'Name, ascending', 'Name, descending'])
|
||||
|
||||
expect(find('ul.dropdown-menu-sort li').all('a').map(&:text)).to eq(['Due soon', 'Due later', 'Start soon', 'Start later', 'Name, ascending', 'Name, descending'])
|
||||
|
||||
click_link 'Due later'
|
||||
|
||||
expect(page).to have_button('Due later')
|
||||
click_button 'Due later'
|
||||
expect(page).to have_button('Due later')
|
||||
end
|
||||
|
||||
# assert descending sorting
|
||||
within '.milestones' do
|
||||
|
|
|
@ -5,49 +5,55 @@ require 'spec_helper'
|
|||
RSpec.describe 'Milestones sorting', :js do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, name: 'test', namespace: user.namespace) }
|
||||
let(:milestones_for_sort_by) do
|
||||
{
|
||||
'Due later' => %w[b c a],
|
||||
'Name, ascending' => %w[a b c],
|
||||
'Name, descending' => %w[c b a],
|
||||
'Start later' => %w[a c b],
|
||||
'Start soon' => %w[b c a],
|
||||
'Due soon' => %w[a c b]
|
||||
}
|
||||
end
|
||||
|
||||
let(:ordered_milestones) do
|
||||
['Due soon', 'Due later', 'Start soon', 'Start later', 'Name, ascending', 'Name, descending']
|
||||
end
|
||||
|
||||
before do
|
||||
# Milestones
|
||||
create(:milestone,
|
||||
due_date: 10.days.from_now,
|
||||
created_at: 2.hours.ago,
|
||||
title: "aaa", project: project)
|
||||
create(:milestone,
|
||||
due_date: 11.days.from_now,
|
||||
created_at: 1.hour.ago,
|
||||
title: "bbb", project: project)
|
||||
create(:milestone, start_date: 7.days.from_now, due_date: 10.days.from_now, title: "a", project: project)
|
||||
create(:milestone, start_date: 6.days.from_now, due_date: 11.days.from_now, title: "c", project: project)
|
||||
create(:milestone, start_date: 5.days.from_now, due_date: 12.days.from_now, title: "b", project: project)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'visit project milestones and sort by due_date_asc' do
|
||||
it 'visit project milestones and sort by various orders' do
|
||||
visit project_milestones_path(project)
|
||||
|
||||
expect(page).to have_button('Due soon')
|
||||
|
||||
# assert default sorting
|
||||
# assert default sorting order
|
||||
within '.milestones' do
|
||||
expect(page.all('ul.content-list > li').first.text).to include('aaa')
|
||||
expect(page.all('ul.content-list > li').last.text).to include('bbb')
|
||||
expect(page.all('ul.content-list > li strong > a').map(&:text)).to eq(%w[a c b])
|
||||
end
|
||||
|
||||
click_button 'Due soon'
|
||||
# assert milestones listed for given sort order
|
||||
selected_sort_order = 'Due soon'
|
||||
milestones_for_sort_by.each do |sort_by, expected_milestones|
|
||||
within '[data-testid=milestone_sort_by_dropdown]' do
|
||||
click_button selected_sort_order
|
||||
milestones = find('.gl-new-dropdown-contents').all('.gl-new-dropdown-item-text-wrapper p').map(&:text)
|
||||
expect(milestones).to eq(ordered_milestones)
|
||||
|
||||
sort_options = find('ul.dropdown-menu-sort li').all('a').collect(&:text)
|
||||
click_button sort_by
|
||||
expect(page).to have_button(sort_by)
|
||||
end
|
||||
|
||||
expect(sort_options[0]).to eq('Due soon')
|
||||
expect(sort_options[1]).to eq('Due later')
|
||||
expect(sort_options[2]).to eq('Start soon')
|
||||
expect(sort_options[3]).to eq('Start later')
|
||||
expect(sort_options[4]).to eq('Name, ascending')
|
||||
expect(sort_options[5]).to eq('Name, descending')
|
||||
within '.milestones' do
|
||||
expect(page.all('ul.content-list > li strong > a').map(&:text)).to eq(expected_milestones)
|
||||
end
|
||||
|
||||
click_link 'Due later'
|
||||
|
||||
expect(page).to have_button('Due later')
|
||||
|
||||
within '.milestones' do
|
||||
expect(page.all('ul.content-list > li').first.text).to include('bbb')
|
||||
expect(page.all('ul.content-list > li').last.text).to include('aaa')
|
||||
selected_sort_order = sort_by
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe GitlabSchema.types['CiJobKind'] do
|
||||
it 'exposes some job type values' do
|
||||
expect(described_class.values.keys).to match_array(
|
||||
(%w[BRIDGE BUILD])
|
||||
)
|
||||
end
|
||||
end
|
|
@ -21,6 +21,7 @@ RSpec.describe Types::Ci::JobType do
|
|||
downstreamPipeline
|
||||
finished_at
|
||||
id
|
||||
kind
|
||||
manual_job
|
||||
name
|
||||
needs
|
||||
|
|
|
@ -2181,18 +2181,46 @@ RSpec.describe Notify do
|
|||
context 'when diff note' do
|
||||
let!(:notes) { create_list(:diff_note_on_merge_request, 3, review: review, project: project, author: review.author, noteable: merge_request) }
|
||||
|
||||
it 'links to notes' do
|
||||
it 'links to notes and discussions', :aggregate_failures do
|
||||
reply_note = create(:diff_note_on_merge_request, review: review, project: project, author: review.author, noteable: merge_request, in_reply_to: notes.first)
|
||||
|
||||
review.notes.each do |note|
|
||||
# Text part
|
||||
expect(subject.text_part.body.raw_source).to include(
|
||||
project_merge_request_url(project, merge_request, anchor: "note_#{note.id}")
|
||||
)
|
||||
|
||||
if note == reply_note
|
||||
expect(subject.text_part.body.raw_source).to include("commented on a discussion on #{note.discussion.file_path}")
|
||||
else
|
||||
expect(subject.text_part.body.raw_source).to include("started a new discussion on #{note.discussion.file_path}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'includes only one link to the highlighted_diff_email' do
|
||||
expect(subject.html_part.body.raw_source).to include('assets/mailers/highlighted_diff_email').once
|
||||
end
|
||||
|
||||
it 'avoids N+1 cached queries when rendering html', :use_sql_query_cache, :request_store do
|
||||
control_count = ActiveRecord::QueryRecorder.new(query_recorder_debug: true, skip_cached: false) do
|
||||
subject.html_part
|
||||
end
|
||||
|
||||
create_list(:diff_note_on_merge_request, 3, review: review, project: project, author: review.author, noteable: merge_request)
|
||||
|
||||
expect { described_class.new_review_email(recipient.id, review.id).html_part }.not_to exceed_all_query_limit(control_count)
|
||||
end
|
||||
|
||||
it 'avoids N+1 cached queries when rendering text', :use_sql_query_cache, :request_store do
|
||||
control_count = ActiveRecord::QueryRecorder.new(query_recorder_debug: true, skip_cached: false) do
|
||||
subject.text_part
|
||||
end
|
||||
|
||||
create_list(:diff_note_on_merge_request, 3, review: review, project: project, author: review.author, noteable: merge_request)
|
||||
|
||||
expect { described_class.new_review_email(recipient.id, review.id).text_part }.not_to exceed_all_query_limit(control_count)
|
||||
end
|
||||
end
|
||||
|
||||
it 'contains review author name' do
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
# frozen_string_literal: true
|
||||
require 'spec_helper'
|
||||
|
||||
require_migration!
|
||||
|
||||
RSpec.describe RemoveLeftoverCiJobArtifactDeletions do
|
||||
let(:deleted_records) { table(:loose_foreign_keys_deleted_records) }
|
||||
|
||||
target_table_name = Ci::JobArtifact.table_name
|
||||
|
||||
let(:pending_record1) do
|
||||
deleted_records.create!(
|
||||
id: 1,
|
||||
fully_qualified_table_name: "public.#{target_table_name}",
|
||||
primary_key_value: 1,
|
||||
status: 1
|
||||
)
|
||||
end
|
||||
|
||||
let(:pending_record2) do
|
||||
deleted_records.create!(
|
||||
id: 2,
|
||||
fully_qualified_table_name: "public.#{target_table_name}",
|
||||
primary_key_value: 2,
|
||||
status: 1
|
||||
)
|
||||
end
|
||||
|
||||
let(:other_pending_record1) do
|
||||
deleted_records.create!(
|
||||
id: 3,
|
||||
fully_qualified_table_name: 'public.projects',
|
||||
primary_key_value: 1,
|
||||
status: 1
|
||||
)
|
||||
end
|
||||
|
||||
let(:other_pending_record2) do
|
||||
deleted_records.create!(
|
||||
id: 4,
|
||||
fully_qualified_table_name: 'public.ci_builds',
|
||||
primary_key_value: 1,
|
||||
status: 1
|
||||
)
|
||||
end
|
||||
|
||||
let(:processed_record1) do
|
||||
deleted_records.create!(
|
||||
id: 5,
|
||||
fully_qualified_table_name: 'public.external_pull_requests',
|
||||
primary_key_value: 3,
|
||||
status: 2
|
||||
)
|
||||
end
|
||||
|
||||
let(:other_processed_record1) do
|
||||
deleted_records.create!(
|
||||
id: 6,
|
||||
fully_qualified_table_name: 'public.ci_builds',
|
||||
primary_key_value: 2,
|
||||
status: 2
|
||||
)
|
||||
end
|
||||
|
||||
let!(:persisted_ids_before) do
|
||||
[
|
||||
pending_record1,
|
||||
pending_record2,
|
||||
other_pending_record1,
|
||||
other_pending_record2,
|
||||
processed_record1,
|
||||
other_processed_record1
|
||||
].map(&:id).sort
|
||||
end
|
||||
|
||||
let!(:persisted_ids_after) do
|
||||
[
|
||||
other_pending_record1,
|
||||
other_pending_record2,
|
||||
processed_record1,
|
||||
other_processed_record1
|
||||
].map(&:id).sort
|
||||
end
|
||||
|
||||
def all_ids
|
||||
deleted_records.all.map(&:id).sort
|
||||
end
|
||||
|
||||
it 'deletes pending external_pull_requests records' do
|
||||
expect { migrate! }.to change { all_ids }.from(persisted_ids_before).to(persisted_ids_after)
|
||||
end
|
||||
end
|
|
@ -700,10 +700,6 @@ RSpec.describe Ci::JobArtifact do
|
|||
MSG
|
||||
end
|
||||
|
||||
it_behaves_like 'it has loose foreign keys' do
|
||||
let(:factory_name) { :ci_job_artifact }
|
||||
end
|
||||
|
||||
context 'loose foreign key on ci_job_artifacts.project_id' do
|
||||
it_behaves_like 'cleanup by a loose foreign key' do
|
||||
let!(:parent) { create(:project) }
|
||||
|
|
|
@ -52,6 +52,7 @@ RSpec.describe 'Query.project(fullPath).pipelines.job(id)' do
|
|||
'name' => job_2.name,
|
||||
'allowFailure' => job_2.allow_failure,
|
||||
'duration' => 25,
|
||||
'kind' => 'BUILD',
|
||||
'queuedDuration' => 2.0,
|
||||
'status' => job_2.status.upcase
|
||||
)
|
||||
|
|
|
@ -155,6 +155,56 @@ RSpec.describe 'Query.project.pipeline' do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.jobs.kind' do
|
||||
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
|
||||
|
||||
let(:query) do
|
||||
%(
|
||||
query {
|
||||
project(fullPath: "#{project.full_path}") {
|
||||
pipeline(iid: "#{pipeline.iid}") {
|
||||
stages {
|
||||
nodes {
|
||||
groups{
|
||||
nodes {
|
||||
jobs {
|
||||
nodes {
|
||||
kind
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
context 'when the job is a build' do
|
||||
it 'returns BUILD' do
|
||||
create(:ci_build, pipeline: pipeline)
|
||||
|
||||
post_graphql(query, current_user: user)
|
||||
|
||||
job_data = graphql_data_at(:project, :pipeline, :stages, :nodes, :groups, :nodes, :jobs, :nodes).first
|
||||
expect(job_data['kind']).to eq 'BUILD'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the job is a bridge' do
|
||||
it 'returns BRIDGE' do
|
||||
create(:ci_bridge, pipeline: pipeline)
|
||||
|
||||
post_graphql(query, current_user: user)
|
||||
|
||||
job_data = graphql_data_at(:project, :pipeline, :stages, :nodes, :groups, :nodes, :jobs, :nodes).first
|
||||
expect(job_data['kind']).to eq 'BRIDGE'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.jobs.artifacts' do
|
||||
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
|
||||
|
||||
|
|
|
@ -11,16 +11,16 @@ RSpec.describe API::Members do
|
|||
|
||||
let(:project) do
|
||||
create(:project, :public, creator_id: maintainer.id, namespace: maintainer.namespace) do |project|
|
||||
project.add_developer(developer)
|
||||
project.add_maintainer(maintainer)
|
||||
project.add_developer(developer, current_user: maintainer)
|
||||
project.request_access(access_requester)
|
||||
end
|
||||
end
|
||||
|
||||
let!(:group) do
|
||||
create(:group, :public) do |group|
|
||||
group.add_developer(developer)
|
||||
group.add_owner(maintainer)
|
||||
group.add_developer(developer, maintainer)
|
||||
create(:group_member, :minimal_access, source: group, user: user_with_minimal_access)
|
||||
group.request_access(access_requester)
|
||||
end
|
||||
|
@ -50,6 +50,10 @@ RSpec.describe API::Members do
|
|||
expect(json_response).to be_an Array
|
||||
expect(json_response.size).to eq(2)
|
||||
expect(json_response.map { |u| u['id'] }).to match_array [maintainer.id, developer.id]
|
||||
expect(json_response).to contain_exactly(
|
||||
a_hash_including('created_by' => a_hash_including('id' => maintainer.id)),
|
||||
hash_not_including('created_by')
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'shared/_milestones_sort_dropdown.html.haml' do
|
||||
describe 'render' do
|
||||
describe 'when a sort option is not selected' do
|
||||
it 'renders a default sort option' do
|
||||
render 'shared/milestones_sort_dropdown'
|
||||
|
||||
expect(rendered).to have_content 'Due soon'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when a sort option is selected' do
|
||||
before do
|
||||
assign(:sort, 'due_date_desc')
|
||||
|
||||
render 'shared/milestones_sort_dropdown'
|
||||
end
|
||||
|
||||
it 'renders the selected sort option' do
|
||||
expect(rendered).to have_content 'Due later'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue