Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-04-05 09:09:24 +00:00
parent b6d0149b92
commit b5e513dbef
39 changed files with 559 additions and 142 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -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' -%>

View File

@ -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

View File

@ -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 %>
--

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
35aaf03898795800027c814a6f907af6f011bd5095cae7188234b46f4b2ebb90

View File

@ -0,0 +1 @@
56706c8d4139c63427838d37b7120c57fd39c7be4caee0ebba39a05cd3c453f7

View File

@ -0,0 +1 @@
ec5dfe48e13cdbce5fc0c67612cddf1a5b555b3c84e1869ee3a7f047f0f3f1a0

View File

@ -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();

View File

@ -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"
}
],
...

View File

@ -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 |

View File

@ -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",

View File

@ -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

View File

@ -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).

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -21,6 +21,7 @@ RSpec.describe Types::Ci::JobType do
downstreamPipeline
finished_at
id
kind
manual_job
name
needs

View File

@ -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

View File

@ -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

View File

@ -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) }

View File

@ -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
)

View File

@ -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) }

View File

@ -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

View File

@ -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