Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-02-21 18:07:30 +00:00
parent aafd8d0b36
commit e503135bfe
38 changed files with 398 additions and 96 deletions

View File

@ -265,6 +265,14 @@
- "GITALY_SERVER_VERSION"
- "lib/gitlab/setup_helper.rb"
# versions of all these components can affect cloud native deployment
.cng-dependency-patterns: &cng-dependency-patterns
- GITLAB_WORKHORSE_VERSION
- GITLAB_SHELL_VERSION
- GITLAB_KAS_VERSION
- GITLAB_PAGES_VERSION
- GITALY_SERVER_VERSION
.workhorse-patterns: &workhorse-patterns
- ".gitlab/ci/version.yml"
- ".gitlab/ci/workhorse.gitlab-ci.yml"
@ -1318,6 +1326,8 @@
- <<: *if-merge-request-labels-run-review-app
- <<: *if-merge-request
changes: *qa-patterns
- <<: *if-merge-request
changes: *cng-dependency-patterns
- <<: *if-merge-request-targeting-stable-branch
changes: *setup-test-env-patterns
- <<: *if-default-refs
@ -1887,14 +1897,14 @@
when: never
- <<: *if-security-schedule
when: never
- <<: *if-merge-request
changes: *cng-dependency-patterns
- !reference [".qa:rules:e2e-blocking-base-before", rules]
- !reference [".prevent-tier-2-and-below", rules]
- !reference [".qa:rules:e2e-blocking-base-after", rules]
- !reference [".qa:rules:e2e-schedule-nightly", rules]
- <<: *if-dot-com-gitlab-org-schedule
variables:
<<: *qa-e2e-test-schedule-variables
QA_RUN_IN_PARALLEL: "true"
variables: *qa-e2e-test-schedule-variables
.qa:rules:test-on-omnibus-nightly:
rules:

View File

@ -1083,6 +1083,13 @@ entry.
- [Quarantine a flaky test](https://gitlab.com/gitlab-org/gitlab/-/commit/c932e35efdc0e3c6f316a3c2d37045e115ce8cd5) ([merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/176452))
- [Finalize migration BackfillRemoteDevelopmentAgentConfigsProjectId](https://gitlab.com/gitlab-org/gitlab/-/commit/da4c63d7aab3685c3fbe9d1e48f68ba2162a0b5e) ([merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/172769))
## 17.8.3 (2025-02-21)
### Fixed (2 changes)
- [Use primary DB when authenticating via job token in jobs API](https://gitlab.com/gitlab-org/gitlab/-/commit/6eee5c6811cac82981252280f1b08316ae8c1fd5) ([merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/181872))
- [Revert stricter workhorse route regexes](https://gitlab.com/gitlab-org/gitlab/-/commit/aba07e94e0587dd378dccbdf18dfe839f09078bf) ([merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/181358))
## 17.8.2 (2025-02-11)
### Fixed (3 changes)
@ -1567,6 +1574,12 @@ entry.
- [Remove default on `group_saved_replies_flag feature flag](https://gitlab.com/gitlab-org/gitlab/-/commit/75d49fe13646e1e0d3b68233ac4a965c86853917) ([merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/175647))
- [Remove use_actual_plan_in_license_check flag](https://gitlab.com/gitlab-org/gitlab/-/commit/b8c3fe16aedb69c82ff52d1c695d72e933c4b946) ([merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/175649))
## 17.7.5 (2025-02-21)
### Fixed (1 change)
- [Revert stricter workhorse route regexes](https://gitlab.com/gitlab-org/gitlab/-/commit/9f1a05217022094de570ca4e4afd5b96b9b68c56) ([merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/181359))
## 17.7.4 (2025-02-11)
### Security (8 changes)

View File

@ -330,10 +330,7 @@ export default {
class="rca-bar-component gl-fixed gl-left-0 gl-flex gl-w-full gl-items-center"
data-testid="rca-bar-component"
>
<div
class="rca-bar-content gl-flex gl-w-full gl-justify-end"
data-testid="rca-bar-content"
>
<div class="rca-bar-content gl-flex gl-w-full" data-testid="rca-bar-content">
<root-cause-analysis-button
:job-id="job.id"
:job-status-group="job.status.group"

View File

@ -22,6 +22,7 @@ function createSuggestionPlugin({
return Suggestion({
editor,
char,
allowSpaces: true,
pluginKey: new PluginKey(uniqueId('suggestions')),
command: ({ editor: tiptapEditor, range, props }) => {

View File

@ -1,6 +1,6 @@
<script>
import { GlEmptyState, GlButton } from '@gitlab/ui';
import emptySvgUrl from '@gitlab/svgs/dist/illustrations/status/status-new-md.svg';
import EMPTY_SVG_PATH from '@gitlab/svgs/dist/illustrations/status/status-new-md.svg';
import * as Sentry from '~/sentry/sentry_browser_wrapper';
import { s__, __, sprintf } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
@ -87,7 +87,7 @@ export default {
CANDIDATES_DOCS_PATH: helpPagePath('user/project/ml/experiment_tracking/mlflow_client.md', {
anchor: 'logging-runs-to-a-model',
}),
EMPTY_SVG_PATH: emptySvgUrl,
EMPTY_SVG_PATH,
};
</script>
<template>

View File

@ -1,7 +1,7 @@
import $ from 'jquery';
import BranchGraph from '~/network/branch_graph';
const vph = $(window).height() - 250;
const vph = $(window).height() - $('.project-network-header').height();
export default class Network {
constructor(opts) {
@ -11,6 +11,8 @@ export default class Network {
this.filter_ref.click(() => this.submit());
this.branch_graph = new BranchGraph(this.network_graph, this.opts);
this.network_graph.css({ height: `${vph}px` });
$('body').css({ 'overflow-y': 'hidden' });
$('.content-wrapper').css({ 'padding-bottom': 0 });
}
submit() {

View File

@ -19,6 +19,8 @@ import {
TODO_ACTION_TYPE_UNMERGEABLE,
TODO_ACTION_TYPE_SSH_KEY_EXPIRED,
TODO_ACTION_TYPE_SSH_KEY_EXPIRING_SOON,
TODO_ACTION_TYPE_DUO_PRO_ACCESS_GRANTED,
TODO_ACTION_TYPE_DUO_ENTERPRISE_ACCESS_GRANTED,
} from '../constants';
export default {
@ -58,7 +60,15 @@ export default {
this.todo.action !== TODO_ACTION_TYPE_MERGE_TRAIN_REMOVED &&
this.todo.action !== TODO_ACTION_TYPE_UNMERGEABLE &&
this.todo.action !== TODO_ACTION_TYPE_SSH_KEY_EXPIRED &&
this.todo.action !== TODO_ACTION_TYPE_SSH_KEY_EXPIRING_SOON
this.todo.action !== TODO_ACTION_TYPE_SSH_KEY_EXPIRING_SOON &&
this.todo.action !== TODO_ACTION_TYPE_DUO_PRO_ACCESS_GRANTED &&
this.todo.action !== TODO_ACTION_TYPE_DUO_ENTERPRISE_ACCESS_GRANTED
);
},
showAvatarOnNote() {
return (
this.todo.action !== TODO_ACTION_TYPE_DUO_PRO_ACCESS_GRANTED &&
this.todo.action !== TODO_ACTION_TYPE_DUO_ENTERPRISE_ACCESS_GRANTED
);
},
author() {
@ -152,6 +162,18 @@ export default {
name = s__('Todos|Your SSH key is expiring soon');
}
if (this.todo.action === TODO_ACTION_TYPE_DUO_PRO_ACCESS_GRANTED) {
name = s__(
'Todos|You now have access to AI-powered features. Boost your productivity with Code Suggestions and GitLab Duo Chat',
);
}
if (this.todo.action === TODO_ACTION_TYPE_DUO_ENTERPRISE_ACCESS_GRANTED) {
name = s__(
'Todos|You now have access to AI-powered features. Boost your productivity with Code Suggestions, GitLab Duo Chat, Vulnerability Explanation, and more',
);
}
if (!name) {
Sentry.captureException(
new Error(`Encountered unknown TODO_ACTION_TYPE ${this.todo.action}`),
@ -170,7 +192,7 @@ export default {
<template>
<div class="gl-flex gl-items-start gl-px-2" data-testid="todo-item-container">
<div class="gl-mr-3 gl-hidden sm:gl-inline-block">
<div v-if="showAvatarOnNote" class="gl-mr-3 gl-hidden sm:gl-inline-block">
<gl-avatar-link :href="author.webUrl" aria-hidden="true" tabindex="-1">
<gl-avatar :size="24" :src="author.avatarUrl" role="none" />
</gl-avatar-link>

View File

@ -2,6 +2,7 @@
import { GlIcon } from '@gitlab/ui';
import StatusBadge from '~/issuable/components/status_badge.vue';
import { STATUS_OPEN, TYPE_ALERT, TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/issues/constants';
import { s__ } from '~/locale';
import {
TODO_ACTION_TYPE_MEMBER_ACCESS_REQUESTED,
TODO_TARGET_TYPE_ALERT,
@ -11,6 +12,8 @@ import {
TODO_TARGET_TYPE_MERGE_REQUEST,
TODO_TARGET_TYPE_PIPELINE,
TODO_TARGET_TYPE_SSH_KEY,
TODO_ACTION_TYPE_DUO_PRO_ACCESS_GRANTED,
TODO_ACTION_TYPE_DUO_ENTERPRISE_ACCESS_GRANTED,
} from '../constants';
export default {
@ -40,6 +43,12 @@ export default {
isMemberAccessRequestAction() {
return this.todo.action === TODO_ACTION_TYPE_MEMBER_ACCESS_REQUESTED;
},
isDuoActionType() {
return (
this.todo.action === TODO_ACTION_TYPE_DUO_PRO_ACCESS_GRANTED ||
this.todo.action === TODO_ACTION_TYPE_DUO_ENTERPRISE_ACCESS_GRANTED
);
},
issuableType() {
if (this.isMergeRequest) {
return TYPE_MERGE_REQUEST;
@ -79,6 +88,8 @@ export default {
* Full title line of the todo title + full reference, joined by a middot
*/
todoTitle() {
if (this.isDuoActionType) return s__('Todos|Getting started with GitLab Duo');
return [this.targetName, this.targetFullReference].filter(Boolean).join(' · ');
},
/**
@ -130,6 +141,8 @@ export default {
return '';
},
icon() {
if (this.isDuoActionType) return 'book';
switch (this.todo.targetType) {
case TODO_TARGET_TYPE_ISSUE:
return 'issues';

View File

@ -27,6 +27,8 @@ export const TODO_ACTION_TYPE_OKR_CHECKIN_REQUESTED = 'okr_checkin_requested';
export const TODO_ACTION_TYPE_ADDED_APPROVER = 'added_approver';
export const TODO_ACTION_TYPE_SSH_KEY_EXPIRED = 'ssh_key_expired';
export const TODO_ACTION_TYPE_SSH_KEY_EXPIRING_SOON = 'ssh_key_expiring_soon';
export const TODO_ACTION_TYPE_DUO_PRO_ACCESS_GRANTED = 'duo_pro_access_granted';
export const TODO_ACTION_TYPE_DUO_ENTERPRISE_ACCESS_GRANTED = 'duo_enterprise_access_granted';
export const TODO_EMPTY_TITLE_POOL = [
s__("Todos|Good job! Looks like you don't have anything left on your To-Do List"),

View File

@ -24,4 +24,3 @@
.top-app-header {
top: $calc-application-header-height;
}

View File

@ -257,8 +257,6 @@
bottom: $calc-application-footer-height;
z-index: $zindex-dropdown-menu;
height: var(--rca-bar-height);
padding-left: $super-sidebar-width;
padding-right: $right-sidebar-collapsed-width;
background: var(--gl-background-color-default);
border-top: 1px solid var(--gl-border-color-default);
@apply gl-transition-padding;
@ -272,7 +270,12 @@
.rca-bar-content {
max-width: $limited-layout-width;
padding: 0 $container-margin;
margin: 0 auto;
margin-left: $super-sidebar-width + 0.5rem;
margin-right: auto;
@media (max-width: map-get($grid-breakpoints, sm)-1) {
margin: 0 auto;
}
}
.loader-animation {

View File

@ -17,5 +17,7 @@ module Types
value 'added_approver', value: 13, description: 'User was added as an approver.'
value 'ssh_key_expired', value: 14, description: 'SSH key of the user has expired.'
value 'ssh_key_expiring_soon', value: 15, description: 'SSH key of the user will expire soon.'
value 'duo_pro_access_granted', value: 16, description: 'Access to Duo Pro has been granted to the user.'
value 'duo_enterprise_access_granted', value: 17, description: 'Access to Duo Enterprise has been granted to the user.'
end
end

View File

@ -413,7 +413,11 @@ class Commit
message_body = ["(cherry picked from commit #{sha})"]
if merged_merge_request?(user)
commits_in_merge_request = merged_merge_request(user).commits
commits_in_merge_request = if Feature.enabled?(:optimized_commit_storage, project)
merged_merge_request(user).commits(load_from_gitaly: true)
else
merged_merge_request(user).commits
end
if commits_in_merge_request.present?
message_body << ""

View File

@ -345,11 +345,19 @@ class MergeRequestDiff < ApplicationRecord
end
def first_commit
commits.last
if Feature.enabled?(:optimized_commit_storage, project)
commits(load_from_gitaly: true).last
else
commits.last
end
end
def last_commit
commits.first
if Feature.enabled?(:optimized_commit_storage, project)
commits(load_from_gitaly: true).first
else
commits.first
end
end
def base_commit
@ -847,7 +855,7 @@ class MergeRequestDiff < ApplicationRecord
end
def save_commits
MergeRequestDiffCommit.create_bulk(self.id, compare.commits.reverse)
MergeRequestDiffCommit.create_bulk(self.id, compare.commits.reverse, skip_commit_data: Feature.enabled?(:optimized_commit_storage, project))
self.class.uncached { merge_request_diff_commits.reset }
end

View File

@ -39,7 +39,7 @@ class MergeRequestDiffCommit < ApplicationRecord
# Deprecated; use `bulk_insert!` from `BulkInsertSafe` mixin instead.
# cf. https://gitlab.com/gitlab-org/gitlab/issues/207989 for progress
def self.create_bulk(merge_request_diff_id, commits)
def self.create_bulk(merge_request_diff_id, commits, skip_commit_data: false)
commit_hashes, user_tuples = prepare_commits_for_bulk_insert(commits)
users = MergeRequest::DiffCommitUser.bulk_find_or_create(user_tuples)
@ -59,7 +59,7 @@ class MergeRequestDiffCommit < ApplicationRecord
commit_hash = commit_hash
.except(:author_name, :author_email, :committer_name, :committer_email, :extended_trailers)
commit_hash.merge(
commit_hash = commit_hash.merge(
commit_author_id: author.id,
committer_id: committer.id,
merge_request_diff_id: merge_request_diff_id,
@ -69,6 +69,12 @@ class MergeRequestDiffCommit < ApplicationRecord
committed_date: Gitlab::Database.sanitize_timestamp(commit_hash[:committed_date]),
trailers: Gitlab::Json.dump(commit_hash.fetch(:trailers, {}))
)
if skip_commit_data
commit_hash.merge(message: '')
else
commit_hash
end
end
ApplicationRecord.legacy_bulk_insert(self.table_name, rows) # rubocop:disable Gitlab/BulkInsert

View File

@ -26,6 +26,8 @@ class Todo < ApplicationRecord
ADDED_APPROVER = 13 # This is an EE-only feature,
SSH_KEY_EXPIRED = 14
SSH_KEY_EXPIRING_SOON = 15
DUO_PRO_ACCESS_GRANTED = 16 # This is an EE-only feature,
DUO_ENTERPRISE_ACCESS_GRANTED = 17 # This is an EE-only feature,
ACTION_NAMES = {
ASSIGNED => :assigned,
@ -42,11 +44,20 @@ class Todo < ApplicationRecord
OKR_CHECKIN_REQUESTED => :okr_checkin_requested,
ADDED_APPROVER => :added_approver,
SSH_KEY_EXPIRED => :ssh_key_expired,
SSH_KEY_EXPIRING_SOON => :ssh_key_expiring_soon
SSH_KEY_EXPIRING_SOON => :ssh_key_expiring_soon,
DUO_PRO_ACCESS_GRANTED => :duo_pro_access_granted,
DUO_ENTERPRISE_ACCESS_GRANTED => :duo_enterprise_access_granted
}.freeze
ACTIONS_MULTIPLE_ALLOWED = [Todo::MENTIONED, Todo::DIRECTLY_ADDRESSED, Todo::MEMBER_ACCESS_REQUESTED].freeze
PARENTLESS_ACTION_TYPES = [
DUO_PRO_ACCESS_GRANTED,
DUO_ENTERPRISE_ACCESS_GRANTED,
SSH_KEY_EXPIRED,
SSH_KEY_EXPIRING_SOON
].freeze
belongs_to :author, class_name: "User"
belongs_to :note
belongs_to :project
@ -68,8 +79,8 @@ class Todo < ApplicationRecord
validates :author, presence: true
validates :target_id, presence: true, unless: :for_commit?
validates :commit_id, presence: true, if: :for_commit?
validates :project, presence: true, unless: -> { group_id || for_ssh_key? }
validates :group, presence: true, unless: -> { project_id || for_ssh_key? }
validates :project, presence: true, unless: -> { group_id || parentless_type? }
validates :group, presence: true, unless: -> { project_id || parentless_type? }
scope :pending, -> { with_state(:pending) }
scope :snoozed, -> { where(arel_table[:snoozed_until].gt(Time.current)) }
@ -353,6 +364,14 @@ class Todo < ApplicationRecord
target_type == Key.name
end
def for_duo_access_granted?
action == DUO_PRO_ACCESS_GRANTED || action == DUO_ENTERPRISE_ACCESS_GRANTED
end
def parentless_type?
PARENTLESS_ACTION_TYPES.include?(action)
end
# override to return commits, which are not active record
def target
if for_commit?
@ -379,6 +398,8 @@ class Todo < ApplicationRecord
def target_url
return if target.nil?
return build_duo_getting_started_url if for_duo_access_granted?
case target
when WorkItem
build_work_item_target_url
@ -413,6 +434,10 @@ class Todo < ApplicationRecord
private
def build_duo_getting_started_url
::Gitlab::Routing.url_helpers.help_page_path('user/get_started/getting_started_gitlab_duo.md')
end
def build_work_item_target_url
::Gitlab::UrlBuilder.build(
target,

View File

@ -387,20 +387,20 @@ class TodoService
end
def create_assignment_todo(target, author, old_assignees = [])
if target.assignees.any?
project = target.project
assignees = target.assignees - old_assignees
attributes = attributes_for_todo(project, target, author, Todo::ASSIGNED)
return unless target.assignees.any?
create_todos(assignees, attributes, target_namespace(target), project)
end
project = target.project
assignees = target.assignees - old_assignees
attributes = attributes_for_todo(project, target, author, ::Todo::ASSIGNED)
create_todos(assignees, attributes, target_namespace(target), project)
end
def create_reviewer_todo(target, author, old_reviewers = [])
if target.reviewers.any?
reviewers = target.reviewers - old_reviewers
create_request_review_todo(target, author, reviewers)
end
return unless target.reviewers.any?
reviewers = target.reviewers - old_reviewers
create_request_review_todo(target, author, reviewers)
end
def create_mention_todos(parent, target, author, note = nil, skip_users = [])

View File

@ -1,6 +0,0 @@
.row-content-block.second-block.content-component-block.gl-px-0.gl-py-3
.gl-w-max.gl-max-w-full
#js-graph-ref-switcher{ data: { project_id: @project.id, ref: @ref, network_path: project_network_path(@project, @ref, ref_type: @ref_type) } }
.oneline
= _("You can move around the graph by using the arrow keys.")

View File

@ -1,18 +1,28 @@
- breadcrumb_title _("Graph")
- page_title _("Graph"), @ref
- title = _("Repository graph")
- breadcrumb_title title
- page_title title, @ref
- network_path = project_network_path(@project, @ref, ref_type: @ref_type)
= render "head"
.gl-mt-5
.project-network.gl-border-1.gl-border-solid.gl-border-gray-300
.controls.gl-bg-strong.gl-p-2.gl-text-base.gl-text-subtle.gl-border-b-1.gl-border-b-solid.gl-border-b-gray-300
= form_tag network_path, method: :get, class: 'form-inline network-form' do |f|
= text_field_tag :extended_sha1, @options[:extended_sha1], placeholder: _("Git revision"), class: 'search-input form-control gl-form-input input-mx-250 search-sha gl-mr-2'
= render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, icon: 'search', button_options: {'aria-label': _("Search"), 'title': _("Search")})
.form-group{ class: 'gl-ml-5 !gl-mb-3' }
.project-network-header.gl-flex.gl-flex-col.gl-overflow-hidden
= render ::Layouts::PageHeadingComponent.new(title) do |c|
- c.with_description do
= _("You can move around the graph by using the arrow keys.")
.project-network
.gl-flex.gl-flex-wrap.gl-items-start.gl-gap-3.gl-p-5.gl-bg-subtle.gl-border-t.gl-border-b
.gl-min-w-26
#js-graph-ref-switcher{ data: { project_id: @project.id, ref: @ref, network_path: project_network_path(@project, @ref, ref_type: @ref_type) } }
= form_tag network_path, method: :get, class: 'gl-grow gl-flex gl-flex-wrap gl-gap-3 gl-items-center network-form' do |f|
.gl-flex
= text_field_tag :extended_sha1, @options[:extended_sha1], placeholder: _("Git revision"), class: 'search-input form-control gl-form-input search-sha !gl-rounded-r-none'
= render Pajamas::ButtonComponent.new(type: :submit, icon: 'search', button_options: { class: '!gl-rounded-l-none -gl-ml-[1px]', 'aria-label': _("Search"), 'title': _("Search") })
.gl-mt-3
= render Pajamas::CheckboxTagComponent.new(name: :filter_ref, checked: @options[:filter_ref]) do |c|
- c.with_label do
= _("Begin with the selected commit")
- if @commit
.network-graph.gl-bg-white.gl-overflow-scroll.gl-overflow-x-hidden{ data: { url: @url, commit_url: @commit_url, ref: @ref, commit_id: @commit.id } }
= gl_loading_icon(size: 'md', css_class: 'gl-mt-3')
.network-graph.gl-overflow-scroll.gl-overflow-x-hidden{ data: { url: @url, commit_url: @commit_url, ref: @ref, commit_id: @commit.id } }
= gl_loading_icon(size: 'md', css_class: 'gl-mt-5')

View File

@ -0,0 +1,9 @@
---
name: duo_seat_assignment_todo
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/507338
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/177019
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/514937
milestone: '17.10'
group: group::acquisition
type: gitlab_com_derisk
default_enabled: false

View File

@ -0,0 +1,9 @@
---
name: optimized_commit_storage
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/517497
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/181958
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/520259
milestone: '17.10'
group: group::source code
type: gitlab_com_derisk
default_enabled: false

View File

@ -6,7 +6,7 @@ class CreateAiDuoChatEvents < Gitlab::Database::Migration[2.2]
def up
# rubocop:disable Migration/Datetime -- "timestamp" is a column name
create_table :ai_duo_chat_events, # rubocop:disable Migration/EnsureFactoryForTable -- code_suggestion_event
create_table :ai_duo_chat_events,
options: 'PARTITION BY RANGE (timestamp)',
primary_key: [:id, :timestamp] do |t|
t.bigserial :id, null: false

View File

@ -43326,6 +43326,8 @@ Values for sorting timelogs.
| <a id="todoactionenumassigned"></a>`assigned` | User was assigned. |
| <a id="todoactionenumbuild_failed"></a>`build_failed` | Build triggered by the user failed. |
| <a id="todoactionenumdirectly_addressed"></a>`directly_addressed` | User was directly addressed. |
| <a id="todoactionenumduo_enterprise_access_granted"></a>`duo_enterprise_access_granted` | Access to Duo Enterprise has been granted to the user. |
| <a id="todoactionenumduo_pro_access_granted"></a>`duo_pro_access_granted` | Access to Duo Pro has been granted to the user. |
| <a id="todoactionenummarked"></a>`marked` | User added a to-do item. |
| <a id="todoactionenummember_access_requested"></a>`member_access_requested` | Group or project access requested from the user. |
| <a id="todoactionenummentioned"></a>`mentioned` | User was mentioned. |

View File

@ -103,15 +103,6 @@ updating the active and current stable releases only, with no backports. Factors
the very low likelihood of exploitation, the low impact of the vulnerability, the complexity of security fixes and
the eventual risk to stability. We always address high and critical security issues with a patch release.
In cases where a strategic user has a requirement to test a feature before it is
officially released, we can offer to create a Release Candidate (RC) version that
includes the specific feature. This should be needed only in extreme cases and can be requested for
consideration by raising an issue in the [release/tasks](https://gitlab.com/gitlab-org/release/tasks/-/issues/new?issuable_template=Backporting-request) issue tracker.
It is important to note that the Release Candidate contains other features and changes as
it is not possible to easily isolate a specific feature (similar reasons as noted above). The
Release Candidate is no different than any code that is deployed to GitLab.com or is publicly
accessible.
### Backporting to older releases
Backporting to more than one stable release is usually reserved for [security fixes](#patch-releases).

View File

@ -18,12 +18,6 @@ title: GitLab-managed Kubernetes resources
{{< /history >}}
{{< alert type="flag" >}}
The availability of this feature is controlled by a feature flag. For more information, see the history.
{{< /alert >}}
Use GitLab-managed Kubernetes resources to provision Kubernetes resources with environment templates. An environment template can:
- Create namespaces and service accounts automatically for new environments

View File

@ -317,7 +317,7 @@ Here are possible causes and solutions:
| Cause | Solution |
| ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| When a user account with the email address already exists in GitLab, but the user does not have the SAML identity tied to their account. | The user needs to [link their account](_index.md#user-access-and-management). |
| If a GitLab user account exists with the same email address, but the account is not associated with a SAML identity. | On GitLab.com, the user needs to [link their account](_index.md#user-access-and-management). On GitLab Self-Managed, administrators can configure the instance to [automatically link the SAML identity with the GitLab user account](../../../integration/saml.md#link-saml-identity-for-an-existing-user) when they first sign in. |
User accounts are created in one of the following ways:

View File

@ -25,7 +25,7 @@ module Gitlab
ALLOWED_ROUTER_RULE_ACTIONS = %w[classify proxy].freeze
# We do not expect a type for `proxy` rules
ROUTER_RULE_ACTIONS_WITHOUT_TYPE = %w[proxy].freeze
ALLOWED_ROUTER_RULE_TYPES = %w[FIRST_CELL SESSION_PREFIX].freeze
ALLOWED_ROUTER_RULE_TYPES = %w[FIRST_CELL SESSION_PREFIX CELL_ID].freeze
private

View File

@ -60260,6 +60260,9 @@ msgstr ""
msgid "Todos|For one hour"
msgstr ""
msgid "Todos|Getting started with GitLab Duo"
msgstr ""
msgid "Todos|Give yourself a pat on the back!"
msgstr ""
@ -60471,6 +60474,12 @@ msgstr ""
msgid "Todos|You"
msgstr ""
msgid "Todos|You now have access to AI-powered features. Boost your productivity with Code Suggestions and GitLab Duo Chat"
msgstr ""
msgid "Todos|You now have access to AI-powered features. Boost your productivity with Code Suggestions, GitLab Duo Chat, Vulnerability Explanation, and more"
msgstr ""
msgid "Todos|Your SSH key has expired"
msgstr ""

View File

@ -86,7 +86,7 @@ RSpec.describe 'Project Network Graph', :js, feature_category: :groups_and_proje
it 'filters select tag' do
switch_ref_to('v1.0.0')
expect(page).to have_css 'title', text: 'Graph · v1.0.0', visible: false
expect(page).to have_css 'title', text: 'Repository graph · v1.0.0', visible: false
page.within '.network-graph' do
expect(page).to have_content 'Change some files'
end

View File

@ -17,6 +17,8 @@ import {
TODO_ACTION_TYPE_UNMERGEABLE,
TODO_ACTION_TYPE_SSH_KEY_EXPIRED,
TODO_ACTION_TYPE_SSH_KEY_EXPIRING_SOON,
TODO_ACTION_TYPE_DUO_PRO_ACCESS_GRANTED,
TODO_ACTION_TYPE_DUO_ENTERPRISE_ACCESS_GRANTED,
} from '~/todos/constants';
import { SAML_HIDDEN_TODO } from '../mock_data';
@ -61,22 +63,24 @@ describe('TodoItemBody', () => {
describe('correct text for actionName', () => {
it.each`
actionName | text | showsAuthor
${TODO_ACTION_TYPE_ADDED_APPROVER} | ${'set you as an approver.'} | ${true}
${TODO_ACTION_TYPE_APPROVAL_REQUIRED} | ${'set you as an approver.'} | ${true}
${TODO_ACTION_TYPE_ASSIGNED} | ${'assigned you.'} | ${true}
${TODO_ACTION_TYPE_BUILD_FAILED} | ${'The pipeline failed.'} | ${false}
${TODO_ACTION_TYPE_DIRECTLY_ADDRESSED} | ${'mentioned you.'} | ${true}
${TODO_ACTION_TYPE_MARKED} | ${'added a to-do item'} | ${true}
${TODO_ACTION_TYPE_MEMBER_ACCESS_REQUESTED} | ${'has requested access to group Foo'} | ${true}
${TODO_ACTION_TYPE_MENTIONED} | ${'mentioned you.'} | ${true}
${TODO_ACTION_TYPE_MERGE_TRAIN_REMOVED} | ${'Removed from Merge Train.'} | ${false}
${TODO_ACTION_TYPE_OKR_CHECKIN_REQUESTED} | ${'requested an OKR update for Foo'} | ${true}
${TODO_ACTION_TYPE_REVIEW_REQUESTED} | ${'requested a review.'} | ${true}
${TODO_ACTION_TYPE_REVIEW_SUBMITTED} | ${'reviewed your merge request.'} | ${true}
${TODO_ACTION_TYPE_UNMERGEABLE} | ${'Could not merge.'} | ${false}
${TODO_ACTION_TYPE_SSH_KEY_EXPIRED} | ${'Your SSH key has expired.'} | ${false}
${TODO_ACTION_TYPE_SSH_KEY_EXPIRING_SOON} | ${'Your SSH key is expiring soon.'} | ${false}
actionName | text | showsAuthor
${TODO_ACTION_TYPE_ADDED_APPROVER} | ${'set you as an approver.'} | ${true}
${TODO_ACTION_TYPE_APPROVAL_REQUIRED} | ${'set you as an approver.'} | ${true}
${TODO_ACTION_TYPE_ASSIGNED} | ${'assigned you.'} | ${true}
${TODO_ACTION_TYPE_BUILD_FAILED} | ${'The pipeline failed.'} | ${false}
${TODO_ACTION_TYPE_DIRECTLY_ADDRESSED} | ${'mentioned you.'} | ${true}
${TODO_ACTION_TYPE_MARKED} | ${'added a to-do item'} | ${true}
${TODO_ACTION_TYPE_MEMBER_ACCESS_REQUESTED} | ${'has requested access to group Foo'} | ${true}
${TODO_ACTION_TYPE_MENTIONED} | ${'mentioned you.'} | ${true}
${TODO_ACTION_TYPE_MERGE_TRAIN_REMOVED} | ${'Removed from Merge Train.'} | ${false}
${TODO_ACTION_TYPE_OKR_CHECKIN_REQUESTED} | ${'requested an OKR update for Foo'} | ${true}
${TODO_ACTION_TYPE_REVIEW_REQUESTED} | ${'requested a review.'} | ${true}
${TODO_ACTION_TYPE_REVIEW_SUBMITTED} | ${'reviewed your merge request.'} | ${true}
${TODO_ACTION_TYPE_UNMERGEABLE} | ${'Could not merge.'} | ${false}
${TODO_ACTION_TYPE_SSH_KEY_EXPIRED} | ${'Your SSH key has expired.'} | ${false}
${TODO_ACTION_TYPE_SSH_KEY_EXPIRING_SOON} | ${'Your SSH key is expiring soon.'} | ${false}
${TODO_ACTION_TYPE_DUO_PRO_ACCESS_GRANTED} | ${'You now have access to AI-powered features. Boost your productivity with Code Suggestions and GitLab Duo Chat'} | ${false}
${TODO_ACTION_TYPE_DUO_ENTERPRISE_ACCESS_GRANTED} | ${'You now have access to AI-powered features. Boost your productivity with Code Suggestions, GitLab Duo Chat, Vulnerability Explanation, and more.'} | ${false}
`('renders "$text" for the "$actionName" action', ({ actionName, text, showsAuthor }) => {
createComponent({ action: actionName, memberAccessType: 'group' });
expect(wrapper.text()).toContain(text);
@ -98,6 +102,14 @@ describe('TodoItemBody', () => {
});
});
it.each([
TODO_ACTION_TYPE_DUO_ENTERPRISE_ACCESS_GRANTED,
TODO_ACTION_TYPE_DUO_PRO_ACCESS_GRANTED,
])('when todo action is `%s`, avatar is not shown', (action) => {
createComponent({ action });
expect(wrapper.findComponent(GlAvatarLink).exists()).toBe(false);
});
describe('when todo has a note', () => {
it('renders note text', () => {
createComponent({ note: { bodyFirstLineHtml: '<p>This is a note</p>' } });

View File

@ -10,6 +10,8 @@ import {
TODO_TARGET_TYPE_MERGE_REQUEST,
TODO_TARGET_TYPE_PIPELINE,
TODO_TARGET_TYPE_SSH_KEY,
TODO_ACTION_TYPE_DUO_PRO_ACCESS_GRANTED,
TODO_ACTION_TYPE_DUO_ENTERPRISE_ACCESS_GRANTED,
} from '~/todos/constants';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { DESIGN_TODO, MR_BUILD_FAILED_TODO } from '../mock_data';
@ -56,6 +58,11 @@ describe('TodoItemTitle', () => {
'Important issue Screenshot_2024-11-22_at_16.11.25.png · Flightjs / Flight #35',
DESIGN_TODO,
],
[
'to-do for duo pro access granted',
'Getting started with GitLab Duo',
{ ...mockToDo, action: TODO_ACTION_TYPE_DUO_PRO_ACCESS_GRANTED },
],
])(`renders %s as %s`, (_a, b, c) => {
createComponent(c);
expect(wrapper.findByTestId('todo-title').text()).toBe(b);
@ -85,4 +92,21 @@ describe('TodoItemTitle', () => {
}
});
});
describe('correct icon for action', () => {
it.each`
action | icon | showsIcon
${TODO_ACTION_TYPE_DUO_PRO_ACCESS_GRANTED} | ${'book'} | ${true}
${TODO_ACTION_TYPE_DUO_ENTERPRISE_ACCESS_GRANTED} | ${'book'} | ${true}
`('renders "$icon" for the "$action" action', ({ action, icon, showsIcon }) => {
createComponent({ ...mockToDo, action });
const glIcon = wrapper.findComponent(GlIcon);
expect(glIcon.exists()).toBe(showsIcon);
if (showsIcon) {
expect(glIcon.props('name')).toBe(icon);
}
});
});
});

View File

@ -557,6 +557,14 @@ EOS
let(:commit) { project.commit('video') }
it { expect(commit.cherry_pick_message(user)).to include("\n\n(cherry picked from commit 88790590ed1337ab189bccaa355f068481c90bec)") }
context 'when "optimized_commit_storage" feature flag is disabled' do
before do
stub_feature_flags(optimized_commit_storage: false)
end
it { expect(commit.cherry_pick_message(user)).to include("\n\n(cherry picked from commit 88790590ed1337ab189bccaa355f068481c90bec)") }
end
end
context 'of a merge commit' do

View File

@ -26,14 +26,17 @@ RSpec.describe MergeRequestDiffCommit, feature_category: :code_review_workflow d
it 'returns the same results as Commit#to_hash, except for parent_ids' do
commit_from_repo = project.repository.commit(subject.sha)
commit_from_repo_hash = commit_from_repo.to_hash.merge(parent_ids: [])
commit_from_repo_hash = commit_from_repo.to_hash.merge(parent_ids: [], message: '')
expect(subject.to_hash).to eq(commit_from_repo_hash)
end
end
describe '.create_bulk' do
subject { described_class.create_bulk(merge_request_diff_id, commits, skip_commit_data: skip_commit_data) }
let(:merge_request_diff_id) { merge_request.merge_request_diff.id }
let(:skip_commit_data) { false }
let(:commits) do
[
project.commit('5937ac0a7beb003549fc5fd26fc247adbce4a52e'),
@ -68,8 +71,6 @@ RSpec.describe MergeRequestDiffCommit, feature_category: :code_review_workflow d
]
end
subject { described_class.create_bulk(merge_request_diff_id, commits) }
it 'inserts the commits into the database en masse' do
expect(ApplicationRecord).to receive(:legacy_bulk_insert)
.with(described_class.table_name, rows)
@ -92,6 +93,19 @@ RSpec.describe MergeRequestDiffCommit, feature_category: :code_review_workflow d
expect(commit_row.committer).to eq(commit_user_row)
end
context 'when "skip_commit_data: true"' do
let(:skip_commit_data) { true }
it 'inserts the commits into the database en masse' do
rows_with_empty_messages = rows.map { |h| h.merge(message: '') }
expect(ApplicationRecord).to receive(:legacy_bulk_insert)
.with(described_class.table_name, rows_with_empty_messages)
subject
end
end
context 'with dates larger than the DB limit' do
let(:commits) do
# This commit's date is "Sun Aug 17 07:12:55 292278994 +0000"

View File

@ -57,6 +57,20 @@ RSpec.describe MergeRequestDiff, feature_category: :code_review_workflow do
it { expect(subject.start_commit_sha).to eq('0b4bc9a49b562e85de7cc9e834518ea6828729b9') }
it { expect(subject.patch_id_sha).to eq('f14ae956369247901117b8b7d237c9dc605898c5') }
it 'creates commits with empty messages' do
expect(subject.commits).to all(have_attributes(message: ''))
end
context 'when feature flag "optimized_commit_storage" is disabled' do
before do
stub_feature_flags(optimized_commit_storage: false)
end
it 'creates commits with messages' do
expect(subject.commits).to all(have_attributes(message: be_present))
end
end
it 'calls GraphqlTriggers.merge_request_diff_generated' do
merge_request = create(:merge_request, :skip_diff_creation)
@ -1244,12 +1258,32 @@ RSpec.describe MergeRequestDiff, feature_category: :code_review_workflow do
it 'returns first commit' do
expect(diff_with_commits.first_commit.sha).to eq(diff_with_commits.merge_request_diff_commits.last.sha)
end
context 'when "optimized_commit_storage" feature flag is disabled' do
before do
stub_feature_flags(optimized_commit_storage: false)
end
it 'returns first commit' do
expect(diff_with_commits.first_commit.sha).to eq(diff_with_commits.merge_request_diff_commits.last.sha)
end
end
end
describe '#last_commit' do
it 'returns last commit' do
expect(diff_with_commits.last_commit.sha).to eq(diff_with_commits.merge_request_diff_commits.first.sha)
end
context 'when "optimized_commit_storage" feature flag is disabled' do
before do
stub_feature_flags(optimized_commit_storage: false)
end
it 'returns last commit' do
expect(diff_with_commits.last_commit.sha).to eq(diff_with_commits.merge_request_diff_commits.first.sha)
end
end
end
describe '#includes_any_commits?' do

View File

@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe Todo, feature_category: :notifications do
let(:issue) { create(:issue) }
let_it_be(:user) { create(:user) }
describe 'relationships' do
it { is_expected.to belong_to(:author).class_name("User") }
@ -25,6 +26,41 @@ RSpec.describe Todo, feature_category: :notifications do
it { is_expected.to validate_presence_of(:user) }
it { is_expected.to validate_presence_of(:author) }
context "for project and/or group" do
using RSpec::Parameterized::TableSyntax
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project) }
subject { described_class.new(project: project, group: group) }
where(:project?, :group?) do
true | true
true | false
false | true
end
with_them do
it "are valid" do
subject.project = project? ? project : nil
subject.group = group? ? group : nil
subject.validate
expect(subject.errors[:project]).to be_empty
expect(subject.errors[:group]).to be_empty
end
end
it "are both are missing" do
subject.project = nil
subject.group = nil
subject.validate
expect(subject.errors.messages[:project].first).to eq("can't be blank")
expect(subject.errors.messages[:group].first).to eq("can't be blank")
end
end
context 'for commits' do
subject { described_class.new(target_type: 'Commit') }
@ -38,6 +74,24 @@ RSpec.describe Todo, feature_category: :notifications do
it { is_expected.to validate_presence_of(:target_id) }
it { is_expected.not_to validate_presence_of(:commit_id) }
end
context 'for parentless types' do
where(:action_type) do
[
[Todo::DUO_PRO_ACCESS_GRANTED],
[Todo::SSH_KEY_EXPIRED]
]
end
with_them do
context "for #{params[:action_type]} should not require a target" do
subject { described_class.new(target: user, action: action_type) }
it { is_expected.not_to validate_presence_of(:project) }
it { is_expected.not_to validate_presence_of(:group) }
end
end
end
end
describe '#body' do

View File

@ -1808,7 +1808,7 @@ RSpec.describe API::MergeRequests, :aggregate_failures, feature_category: :sourc
it 'returns a 200 when merge request is valid' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/commits", user)
commit = merge_request.commits.first
commit = merge_request.merge_request_diff.last_commit
expect_successful_response_with_paginated_array
expect(json_response.size).to eq(merge_request.commits.size)
@ -1817,14 +1817,15 @@ RSpec.describe API::MergeRequests, :aggregate_failures, feature_category: :sourc
expect(json_response.first['parent_ids']).to be_present
end
context 'when commits_from_gitaly feature flag is disabled' do
context 'when commits_from_gitaly and optimized_commit_storage feature flags are disabled' do
before do
stub_feature_flags(commits_from_gitaly: false)
stub_feature_flags(optimized_commit_storage: false)
end
it 'returns a 200 without parent_ids' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/commits", user)
commit = merge_request.commits.first
commit = merge_request.merge_request_diff.last_commit
expect_successful_response_with_paginated_array
expect(json_response.size).to eq(merge_request.commits.size)

View File

@ -131,6 +131,36 @@ RSpec.describe ApplicationController, type: :request, feature_category: :shared
end
end
context 'for classify action by SESSION_PREFIX' do
let(:headers) do
{
'X-Gitlab-Http-Router-Rule-Action' => 'classify',
'X-Gitlab-Http-Router-Rule-Type' => 'SESSION_PREFIX'
}
end
it 'increments the counter with labels' do
expect { perform_request }.to change {
http_router_rule_counter.get(rule_action: 'classify', rule_type: 'SESSION_PREFIX')
}.by(1)
end
end
context 'for classify action by CELL_ID' do
let(:headers) do
{
'X-Gitlab-Http-Router-Rule-Action' => 'classify',
'X-Gitlab-Http-Router-Rule-Type' => 'CELL_ID'
}
end
it 'increments the counter with labels' do
expect { perform_request }.to change {
http_router_rule_counter.get(rule_action: 'classify', rule_type: 'CELL_ID')
}.by(1)
end
end
context 'for proxy action' do
let(:headers) do
{

View File

@ -890,10 +890,10 @@ RSpec.describe MergeRequests::RefreshService, feature_category: :code_review_wor
target_project: @project
)
commits = draft_merge_request.commits
commits = draft_merge_request.commits(load_from_gitaly: true)
oldrev = commits.last.id
newrev = commits.first.id
draft_commit = draft_merge_request.commits.find(&:draft?)
draft_commit = commits.find(&:draft?)
refresh_service.execute(oldrev, newrev, 'refs/heads/wip')