Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
02f08e9e78
commit
fd7c75bf60
|
|
@ -497,5 +497,5 @@
|
|||
|
||||
.use-kube-context:
|
||||
before_script:
|
||||
- export KUBE_CONTEXT="gitlab-org/gitlab:review-apps"
|
||||
- export KUBE_CONTEXT="${CI_PROJECT_NAMESPACE}/gitlab:review-apps"
|
||||
- kubectl config use-context ${KUBE_CONTEXT}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
@import 'mixins_and_variables_and_functions';
|
||||
@import 'framework/buttons';
|
||||
|
||||
$tabs-holder-z-index: 250;
|
||||
$comparison-empty-state-height: 62px;
|
||||
|
|
@ -34,27 +33,6 @@ $comparison-empty-state-height: 62px;
|
|||
}
|
||||
}
|
||||
|
||||
.mr-state-widget {
|
||||
.accept-merge-holder {
|
||||
.accept-action {
|
||||
.accept-merge-request {
|
||||
&.ci-preparing,
|
||||
&.ci-pending,
|
||||
&.ci-running {
|
||||
@include btn-blue;
|
||||
}
|
||||
|
||||
&.ci-skipped,
|
||||
&.ci-failed,
|
||||
&.ci-canceled,
|
||||
&.ci-error {
|
||||
@include btn-red;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mr_source_commit,
|
||||
.mr_target_commit {
|
||||
margin-bottom: 0;
|
||||
|
|
@ -201,8 +179,8 @@ $comparison-empty-state-height: 62px;
|
|||
.table-holder {
|
||||
.ci-table {
|
||||
th {
|
||||
background-color: $white;
|
||||
color: $gl-text-color-secondary;
|
||||
background-color: var(--white, $white);
|
||||
color: var(--gl-gray-700, $gl-text-color-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -210,8 +188,8 @@ $comparison-empty-state-height: 62px;
|
|||
.merge-request-tabs-holder {
|
||||
top: $calc-application-header-height;
|
||||
z-index: $tabs-holder-z-index;
|
||||
background-color: $body-bg;
|
||||
border-bottom: 1px solid $border-color;
|
||||
background-color: var(--gray-10, $body-bg);
|
||||
border-bottom: 1px solid var(--border-color, $border-color);
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
position: sticky;
|
||||
|
|
@ -238,7 +216,7 @@ $comparison-empty-state-height: 62px;
|
|||
margin-right: auto;
|
||||
|
||||
.inner-page-scroll-tabs {
|
||||
background-color: $white;
|
||||
background-color: var(--white, $white);
|
||||
margin-left: -$gl-padding;
|
||||
padding-left: $gl-padding;
|
||||
}
|
||||
|
|
@ -307,7 +285,7 @@ $comparison-empty-state-height: 62px;
|
|||
opacity: 0.65;
|
||||
|
||||
&:hover {
|
||||
color: $gray-500;
|
||||
color: var(--gray-500, $gray-500);
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,14 @@ class DeviseMailer < Devise::Mailer
|
|||
super
|
||||
end
|
||||
|
||||
def email_changed(record, opts = {})
|
||||
if Gitlab.com?
|
||||
devise_mail(record, :email_changed_gitlab_com, opts)
|
||||
else
|
||||
devise_mail(record, :email_changed, opts)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def subject_for(key)
|
||||
|
|
|
|||
|
|
@ -19,17 +19,16 @@ module Emails
|
|||
end
|
||||
|
||||
def setup_review_email(review_id, recipient_id)
|
||||
review = Review.find_by_id(review_id)
|
||||
|
||||
@notes = review.notes
|
||||
@discussions = Discussion.build_discussions(review.discussion_ids, preload_note_diff_file: true)
|
||||
@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
|
||||
@merge_request = review.merge_request
|
||||
@author = @review.author
|
||||
@author_name = @review.author_name
|
||||
@project = @review.project
|
||||
@merge_request = @review.merge_request
|
||||
@target_url = project_merge_request_url(@project, @merge_request)
|
||||
@sent_notification = SentNotification.record(@merge_request, recipient_id, reply_key)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@ class DeviseMailerPreview < ActionMailer::Preview
|
|||
DeviseMailer.user_admin_approval(unsaved_user, {})
|
||||
end
|
||||
|
||||
def email_changed
|
||||
DeviseMailer.email_changed(unsaved_user, {})
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def unsaved_user
|
||||
|
|
|
|||
|
|
@ -284,6 +284,13 @@ class NotifyPreview < ActionMailer::Preview
|
|||
Notify.request_review_merge_request_email(user.id, merge_request.id, user.id).message
|
||||
end
|
||||
|
||||
def new_review_email
|
||||
review = Review.last
|
||||
mr_author = review.merge_request.author
|
||||
|
||||
Notify.new_review_email(mr_author.id, review.id).message
|
||||
end
|
||||
|
||||
def project_was_moved_email
|
||||
Notify.project_was_moved_email(project.id, user.id, "gitlab/gitlab").message
|
||||
end
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ class Deployment < ApplicationRecord
|
|||
|
||||
state_machine :status, initial: :created do
|
||||
event :run do
|
||||
transition created: :running
|
||||
transition [:created, :blocked] => :running
|
||||
end
|
||||
|
||||
event :block do
|
||||
|
|
@ -79,6 +79,7 @@ class Deployment < ApplicationRecord
|
|||
transition skipped: :created
|
||||
end
|
||||
|
||||
# Deprecated. To be removed when we remove `track_manual_deployments` feature flag.
|
||||
event :unblock do
|
||||
transition blocked: :created
|
||||
end
|
||||
|
|
@ -403,10 +404,16 @@ class Deployment < ApplicationRecord
|
|||
end
|
||||
|
||||
def sync_status_with(build)
|
||||
return false unless ::Deployment.statuses.include?(build.status)
|
||||
return false if build.status == self.status
|
||||
build_status = build.status
|
||||
|
||||
update_status!(build.status)
|
||||
if ::Feature.enabled?(:track_manual_deployments, build.project)
|
||||
build_status = 'blocked' if build_status == 'manual' # rubocop:disable Style/SoleNestedConditional
|
||||
end
|
||||
|
||||
return false unless ::Deployment.statuses.include?(build_status)
|
||||
return false if build_status == self.status
|
||||
|
||||
update_status!(build_status)
|
||||
rescue StandardError => e
|
||||
Gitlab::ErrorTracking.track_exception(
|
||||
StatusSyncError.new(e.message), deployment_id: self.id, build_id: build.id)
|
||||
|
|
|
|||
|
|
@ -59,6 +59,10 @@ module Ml
|
|||
numeric?(iid)
|
||||
end
|
||||
|
||||
def find_or_create(project, name, user)
|
||||
create_with(user: user).find_or_create_by(project: project, name: name)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def numeric?(value)
|
||||
|
|
|
|||
|
|
@ -21,5 +21,10 @@ module Ml
|
|||
errors.add(:default_experiment) unless default_experiment.name == name
|
||||
errors.add(:default_experiment) unless default_experiment.project_id == project_id
|
||||
end
|
||||
|
||||
def self.find_or_create(project, name, experiment)
|
||||
create_with(default_experiment: experiment)
|
||||
.find_or_create_by(project: project, name: name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18,6 +18,12 @@ module Ml
|
|||
|
||||
delegate :name, to: :model
|
||||
|
||||
class << self
|
||||
def find_or_create(model, version, package)
|
||||
create_with(package: package).find_or_create_by(project: model.project, model: model, version: version)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def valid_model?
|
||||
|
|
|
|||
|
|
@ -32,3 +32,5 @@ class Review < ApplicationRecord
|
|||
merge_request.user_mentions.where.not(note_id: nil)
|
||||
end
|
||||
end
|
||||
|
||||
Review.prepend_mod
|
||||
|
|
|
|||
|
|
@ -52,3 +52,5 @@ module Projects
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Projects::ImportExport::ProjectExportPresenter.prepend_mod_with('Projects::ImportExport::ProjectExportPresenter')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Ml
|
||||
class FindOrCreateExperimentService
|
||||
def initialize(project, experiment_name, user = nil)
|
||||
@project = project
|
||||
@name = experiment_name
|
||||
@user = user
|
||||
end
|
||||
|
||||
def execute
|
||||
Ml::Experiment.find_or_create(project, name, user)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :project, :name, :user
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Ml
|
||||
class FindOrCreateModelService
|
||||
def initialize(project, model_name)
|
||||
@project = project
|
||||
@name = model_name
|
||||
end
|
||||
|
||||
def execute
|
||||
Ml::Model.find_or_create(
|
||||
project,
|
||||
name,
|
||||
Ml::FindOrCreateExperimentService.new(project, name).execute
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :name, :project
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Ml
|
||||
class FindOrCreateModelVersionService
|
||||
def initialize(project, params = {})
|
||||
@project = project
|
||||
@name = params[:model_name]
|
||||
@version = params[:version]
|
||||
@package = params[:package]
|
||||
end
|
||||
|
||||
def execute
|
||||
model = Ml::FindOrCreateModelService.new(project, name).execute
|
||||
|
||||
Ml::ModelVersion.find_or_create(model, version, package)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :version, :name, :project, :package
|
||||
end
|
||||
end
|
||||
|
|
@ -723,9 +723,12 @@ class NotificationService
|
|||
# Notify users on new review in system
|
||||
def new_review(review)
|
||||
recipients = NotificationRecipients::BuildService.build_new_review_recipients(review)
|
||||
deliver_options = new_review_deliver_options(review)
|
||||
|
||||
recipients.each do |recipient|
|
||||
mailer.new_review_email(recipient.user.id, review.id).deliver_later
|
||||
mailer
|
||||
.new_review_email(recipient.user.id, review.id)
|
||||
.deliver_later(deliver_options)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -946,6 +949,11 @@ class NotificationService
|
|||
def warn_skipping_notifications(user, object)
|
||||
Gitlab::AppLogger.warn(message: "Skipping sending notifications", user: user.id, klass: object.class.to_s, object_id: object.id)
|
||||
end
|
||||
|
||||
def new_review_deliver_options(review)
|
||||
# Overridden in EE
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
||||
NotificationService.prepend_mod_with('NotificationService')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
= email_default_heading("Hello, #{@resource.name}!")
|
||||
|
||||
- if @resource.try(:unconfirmed_email?)
|
||||
%p
|
||||
We're contacting you to notify you that your email is being changed to #{@resource.reset.unconfirmed_email}.
|
||||
- else
|
||||
%p
|
||||
We're contacting you to notify you that your email has been changed to #{@resource.email}.
|
||||
|
||||
%p
|
||||
If you did not initiate this change, please contact your group owner immediately. If you have a Premium or Ultimate tier subscription, you can also contact GitLab support.
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
Hello, <%= @resource.name %>!
|
||||
|
||||
<% if @resource.try(:unconfirmed_email?) %>
|
||||
We're contacting you to notify you that your email is being changed to <%= @resource.reset.unconfirmed_email %>.
|
||||
<% else %>
|
||||
We're contacting you to notify you that your email has been changed to <%= @resource.email %>.
|
||||
<% end %>
|
||||
|
||||
If you did not initiate this change, please contact your group owner immediately. If you have a Premium or Ultimate tier subscription, you can also contact GitLab support.
|
||||
|
|
@ -22,3 +22,4 @@
|
|||
- 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; padding-bottom: 1em;", include_stylesheet_link: false, discussion: discussion, author: @author
|
||||
= render_if_exists 'notify/review_summary'
|
||||
|
|
|
|||
|
|
@ -12,3 +12,5 @@
|
|||
--
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= render_if_exists 'notify/review_summary' %>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: track_manual_deployments
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/125659
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/419039
|
||||
milestone: '16.3'
|
||||
type: development
|
||||
group: group::environments
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
- title: "Deprecate field `hasSolutions` from GraphQL VulnerabilityType"
|
||||
removal_milestone: "17.0"
|
||||
announcement_milestone: "16.3"
|
||||
breaking_change: true
|
||||
reporter: thiagocsf
|
||||
stage: Govern
|
||||
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/414895
|
||||
body: | # (required) Do not modify this line, instead modify the lines below.
|
||||
The GraphQL field `Vulnerability.hasSolutions` is deprecated and will be removed in GitLab 17.0.
|
||||
Use `Vulnerability.hasRemediations` instead.
|
||||
documentation_url: https://docs.gitlab.com/ee/api/graphql/reference/#vulnerability
|
||||
|
|
@ -182,8 +182,7 @@ To enable advanced search:
|
|||
[license](../../administration/license.md).
|
||||
|
||||
1. Configure the [advanced search settings](#advanced-search-configuration) for
|
||||
your Elasticsearch cluster. Do not enable **Search with Elasticsearch enabled**
|
||||
yet.
|
||||
your Elasticsearch cluster. Do not select the **Search with Elasticsearch enabled** checkbox yet.
|
||||
1. Index all data with a Rake task. The task creates an empty index if one does not already exist and
|
||||
enables Elasticsearch indexing if the indexing is not already enabled:
|
||||
|
||||
|
|
@ -202,7 +201,7 @@ To enable advanced search:
|
|||
1. On the Sidekiq dashboard, select **Queues** and wait for the `elastic_commit_indexer`
|
||||
and `elastic_wiki_indexer` queues to drop to `0`.
|
||||
These queues contain jobs to index code and wiki data for groups and projects.
|
||||
1. After indexing completes, enable **Search with Elasticsearch enabled** and select **Save changes**.
|
||||
1. After the indexing is complete, select the **Search with Elasticsearch enabled** checkbox, then select **Save changes**.
|
||||
|
||||
NOTE:
|
||||
When your Elasticsearch cluster is down while Elasticsearch is enabled,
|
||||
|
|
@ -221,7 +220,7 @@ To enable advanced search with **Index all projects**:
|
|||
1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
|
||||
1. Select **Admin Area**.
|
||||
1. On the left sidebar, select **Settings > Advanced Search**.
|
||||
1. Enable **Elasticsearch indexing** and select **Save changes**.
|
||||
1. Select the **Elasticsearch indexing** checkbox, then select **Save changes**.
|
||||
1. Select **Index all projects**.
|
||||
1. Optional. Select **Check progress** to see the status of background jobs.
|
||||
|
||||
|
|
@ -382,14 +381,12 @@ The index pattern `*` requires a few permissions for advanced search to work.
|
|||
|
||||
### Limit the number of namespaces and projects that can be indexed
|
||||
|
||||
If you check checkbox `Limit the number of namespaces and projects that can be indexed`
|
||||
under **Elasticsearch indexing restrictions** more options become available.
|
||||
When you select the **Limit the number of namespaces and projects that can be indexed**
|
||||
checkbox, you can specify namespaces and projects to index. If the namespace is a group,
|
||||
any subgroups and projects belonging to those subgroups are also indexed.
|
||||
|
||||

|
||||
|
||||
You can select namespaces and projects to index exclusively. If the namespace is a group, it includes
|
||||
any subgroups and projects belonging to those subgroups to be indexed as well.
|
||||
|
||||
Advanced search only provides cross-group code/commit search (global) if all name-spaces are indexed. In this particular scenario where only a subset of namespaces are indexed, a global search does not provide a code or commit scope. This is possible only in the scope of an indexed namespace. There is no way to code/commit search in multiple indexed namespaces (when only a subset of namespaces has been indexed). For example if two groups are indexed, there is no way to run a single code search on both. You can only run a code search on the first group and then on the second.
|
||||
|
||||
You can filter the selection dropdown list by writing part of the namespace or project name you're interested in.
|
||||
|
|
@ -792,7 +789,7 @@ Make sure to prepare for this task by having a
|
|||
bundle exec rake gitlab:elastic:clear_index_status RAILS_ENV=production
|
||||
```
|
||||
|
||||
1. [Enable **Elasticsearch indexing**](#enable-advanced-search).
|
||||
1. [Select the **Elasticsearch indexing** checkbox](#enable-advanced-search).
|
||||
1. Indexing large Git repositories can take a while. To speed up the process, you can [tune for indexing speed](https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-indexing-speed.html#tune-for-indexing-speed):
|
||||
|
||||
- You can temporarily disable [`refresh`](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-refresh.html), the operation responsible for making changes to an index available to search.
|
||||
|
|
@ -915,7 +912,7 @@ Make sure to prepare for this task by having a
|
|||
} }'
|
||||
```
|
||||
|
||||
1. After the indexing has completed, enable [**Search with Elasticsearch enabled**](#enable-advanced-search).
|
||||
1. After the indexing is complete, [select the **Search with Elasticsearch enabled** checkbox](#enable-advanced-search).
|
||||
|
||||
### Deleted documents
|
||||
|
||||
|
|
|
|||
|
|
@ -281,12 +281,12 @@ queue, or the index is somehow in a state where migrations just cannot
|
|||
proceed. It is always best to try to troubleshoot the root cause of the problem
|
||||
by [viewing the logs](#view-logs).
|
||||
|
||||
If there are no other options, then you always have the option of recreating the
|
||||
entire index from scratch. If you have a small GitLab installation, this can
|
||||
sometimes be a quick way to resolve a problem, but if you have a large GitLab
|
||||
installation, then this might take a very long time to complete. Until the
|
||||
index is fully recreated, your index does not serve correct search results,
|
||||
so you may want to disable **Search with Elasticsearch** while it is running.
|
||||
As a last resort, you can recreate the index from scratch. For small GitLab installations,
|
||||
recreating the index can be a quick way to resolve some issues. For large GitLab
|
||||
installations, however, this method might take a very long time. Your index
|
||||
does not show correct search results until the indexing is complete. You might
|
||||
want to clear the **Search with Elasticsearch enabled** checkbox
|
||||
while the indexing is running.
|
||||
|
||||
If you are sure you've read the above caveats and want to proceed, then you
|
||||
should run the following Rake task to recreate the entire index from scratch:
|
||||
|
|
|
|||
|
|
@ -174,6 +174,21 @@ The message field was removed from security reports schema in GitLab 16.0 and is
|
|||
|
||||
</div>
|
||||
|
||||
<div class="deprecation breaking-change" data-milestone="17.0">
|
||||
|
||||
### Deprecate field `hasSolutions` from GraphQL VulnerabilityType
|
||||
|
||||
<div class="deprecation-notes">
|
||||
- Announced in GitLab <span class="milestone">16.3</span>
|
||||
- Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change))
|
||||
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/414895).
|
||||
</div>
|
||||
|
||||
The GraphQL field `Vulnerability.hasSolutions` is deprecated and will be removed in GitLab 17.0.
|
||||
Use `Vulnerability.hasRemediations` instead.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation " data-milestone="17.0">
|
||||
|
||||
### Deprecate legacy shell escaping and quoting runner shell executor
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ module API
|
|||
expose :id, documentation: { type: :string, example: "1234" }
|
||||
expose :job_class_name, documentation: { type: :string, example: "CopyColumnUsingBackgroundMigrationJob" }
|
||||
expose :table_name, documentation: { type: :string, example: "events" }
|
||||
expose :column_name, documentation: { type: :string, example: "id" }
|
||||
expose :status_name, as: :status, override: true, documentation: { type: :string, example: "active" }
|
||||
expose :progress, documentation: { type: :float, example: 50 }
|
||||
expose :created_at, documentation: { type: :dateTime, example: "2022-11-28T16:26:39+02:00" }
|
||||
|
|
|
|||
|
|
@ -45170,6 +45170,9 @@ msgstr ""
|
|||
msgid "Summary generated by AI"
|
||||
msgstr ""
|
||||
|
||||
msgid "Summary generated by AI (Experiment)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Summary will be generated with the next push to this merge request and will appear here."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Manage', :requires_admin, :skip_live_env, product_group: :authentication_and_authorization do
|
||||
RSpec.describe 'Manage', :requires_admin, product_group: :authentication_and_authorization do
|
||||
describe '2FA' do
|
||||
let(:admin_api_client) { Runtime::API::Client.as_admin }
|
||||
let(:owner_api_client) { Runtime::API::Client.new(:gitlab, user: owner_user) }
|
||||
|
|
@ -14,6 +14,7 @@ module QA
|
|||
end
|
||||
|
||||
let(:sandbox_group) do
|
||||
Flow::Login.sign_in(as: owner_user)
|
||||
Resource::Sandbox.fabricate! do |sandbox_group|
|
||||
sandbox_group.path = "gitlab-qa-2fa-sandbox-group-#{SecureRandom.hex(8)}"
|
||||
sandbox_group.api_client = owner_api_client
|
||||
|
|
@ -43,12 +44,7 @@ module QA
|
|||
|
||||
it(
|
||||
'allows enforcing 2FA via UI and logging in with 2FA',
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347931',
|
||||
quarantine: {
|
||||
type: :bug,
|
||||
only: { condition: -> { !QA::Runtime::Env.super_sidebar_enabled? } },
|
||||
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/409336'
|
||||
}
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347931'
|
||||
) do
|
||||
enforce_two_factor_authentication_on_group(group)
|
||||
|
||||
|
|
|
|||
|
|
@ -150,10 +150,12 @@ RSpec.describe DeviseMailer do
|
|||
end
|
||||
|
||||
describe '#email_changed' do
|
||||
subject { described_class.email_changed(user, {}) }
|
||||
|
||||
let(:content_saas) { 'If you did not initiate this change, please contact your group owner immediately. If you have a Premium or Ultimate tier subscription, you can also contact GitLab support.' }
|
||||
let(:content_self_managed) { 'If you did not initiate this change, please contact your administrator immediately.' }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
subject { described_class.email_changed(user, {}) }
|
||||
|
||||
it_behaves_like 'an email sent from GitLab'
|
||||
|
||||
it 'is sent to the user' do
|
||||
|
|
@ -168,6 +170,18 @@ RSpec.describe DeviseMailer do
|
|||
is_expected.to have_body_text /Hello, #{user.name}!/
|
||||
end
|
||||
|
||||
context 'when self-managed' do
|
||||
it 'has the expected content of self managed instance' do
|
||||
is_expected.to have_body_text content_self_managed
|
||||
end
|
||||
end
|
||||
|
||||
context 'when saas', :saas do
|
||||
it 'has the expected content of saas instance' do
|
||||
is_expected.to have_body_text content_saas
|
||||
end
|
||||
end
|
||||
|
||||
context "email contains updated id" do
|
||||
before do
|
||||
user.update!(email: "new_email@test.com")
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ RSpec.describe 'Mailer previews' do
|
|||
let_it_be(:issue) { create(:issue, project: project, milestone: milestone) }
|
||||
let_it_be(:remote_mirror) { create(:remote_mirror, project: project) }
|
||||
let_it_be(:member) { create(:project_member, :maintainer, project: project, created_by: user) }
|
||||
let_it_be(:review) { create(:review, project: project, merge_request: merge_request, author: user) }
|
||||
|
||||
Gitlab.ee do
|
||||
let_it_be(:epic) { create(:epic, group: group) }
|
||||
|
|
|
|||
|
|
@ -1215,12 +1215,14 @@ RSpec.describe Deployment, feature_category: :continuous_delivery do
|
|||
let(:ci_build) { create(:ci_build, project: project, status: build_status) }
|
||||
|
||||
shared_examples_for 'synchronizing deployment' do
|
||||
let(:expected_deployment_status) { build_status.to_s }
|
||||
|
||||
it 'changes deployment status' do
|
||||
expect(Gitlab::ErrorTracking).not_to receive(:track_exception)
|
||||
|
||||
is_expected.to eq(true)
|
||||
|
||||
expect(deployment.status).to eq(build_status.to_s)
|
||||
expect(deployment.status).to eq(expected_deployment_status)
|
||||
expect(deployment.errors).to be_empty
|
||||
end
|
||||
end
|
||||
|
|
@ -1259,6 +1261,22 @@ RSpec.describe Deployment, feature_category: :continuous_delivery do
|
|||
it_behaves_like 'ignoring build'
|
||||
end
|
||||
|
||||
context 'with manual build' do
|
||||
let(:build_status) { :manual }
|
||||
|
||||
it_behaves_like 'synchronizing deployment' do
|
||||
let(:expected_deployment_status) { 'blocked' }
|
||||
end
|
||||
|
||||
context 'when track_manual_deployments feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(track_manual_deployments: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'ignoring build'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with running build' do
|
||||
let(:build_status) { :running }
|
||||
|
||||
|
|
@ -1289,6 +1307,22 @@ RSpec.describe Deployment, feature_category: :continuous_delivery do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with manual build' do
|
||||
let(:build_status) { :manual }
|
||||
|
||||
it_behaves_like 'gracefully handling error' do
|
||||
let(:error_message) { %{Status cannot transition via \"block\"} }
|
||||
end
|
||||
|
||||
context 'when track_manual_deployments feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(track_manual_deployments: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'ignoring build'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with running build' do
|
||||
let(:build_status) { :running }
|
||||
|
||||
|
|
@ -1319,6 +1353,22 @@ RSpec.describe Deployment, feature_category: :continuous_delivery do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with manual build' do
|
||||
let(:build_status) { :manual }
|
||||
|
||||
it_behaves_like 'gracefully handling error' do
|
||||
let(:error_message) { %{Status cannot transition via \"block\"} }
|
||||
end
|
||||
|
||||
context 'when track_manual_deployments feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(track_manual_deployments: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'ignoring build'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with running build' do
|
||||
let(:build_status) { :running }
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,45 @@ RSpec.describe Ml::Experiment, feature_category: :mlops do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.find_or_create' do
|
||||
let(:name) { exp.name }
|
||||
let(:project) { exp.project }
|
||||
|
||||
subject(:find_or_create) { described_class.find_or_create(project, name, exp.user) }
|
||||
|
||||
context 'when experiments exists' do
|
||||
it 'fetches existing experiment', :aggregate_failures do
|
||||
expect { find_or_create }.not_to change { Ml::Experiment.count }
|
||||
|
||||
expect(find_or_create).to eq(exp)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when experiments does not exist' do
|
||||
let(:name) { 'a new experiment' }
|
||||
|
||||
it 'creates the experiment', :aggregate_failures do
|
||||
expect { find_or_create }.to change { Ml::Experiment.count }.by(1)
|
||||
|
||||
expect(find_or_create.name).to eq(name)
|
||||
expect(find_or_create.user).to eq(exp.user)
|
||||
expect(find_or_create.project).to eq(project)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when experiment name exists but project is different' do
|
||||
let(:project) { create(:project) }
|
||||
|
||||
it 'creates a model', :aggregate_failures do
|
||||
expect { find_or_create }.to change { Ml::Experiment.count }.by(1)
|
||||
|
||||
expect(find_or_create.name).to eq(name)
|
||||
expect(find_or_create.user).to eq(exp.user)
|
||||
expect(find_or_create.project).to eq(project)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#with_candidate_count' do
|
||||
let_it_be(:exp3) do
|
||||
create(:ml_experiments, project: exp.project).tap do |e|
|
||||
|
|
|
|||
|
|
@ -3,6 +3,13 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Ml::Model, feature_category: :mlops do
|
||||
let_it_be(:base_project) { create(:project) }
|
||||
let_it_be(:existing_model) { create(:ml_models, name: 'an_existing_model', project: base_project) }
|
||||
let_it_be(:valid_name) { 'a_valid_name' }
|
||||
let_it_be(:default_experiment) { create(:ml_experiments, name: valid_name, project: base_project) }
|
||||
|
||||
let(:project) { base_project }
|
||||
|
||||
describe 'associations' do
|
||||
it { is_expected.to belong_to(:project) }
|
||||
it { is_expected.to have_one(:default_experiment) }
|
||||
|
|
@ -12,11 +19,6 @@ RSpec.describe Ml::Model, feature_category: :mlops do
|
|||
describe '#valid?' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:existing_model) { create(:ml_models, name: 'an_existing_model', project: project) }
|
||||
let_it_be(:valid_name) { 'a_valid_name' }
|
||||
let_it_be(:default_experiment) { create(:ml_experiments, name: valid_name, project: project) }
|
||||
|
||||
let(:name) { valid_name }
|
||||
|
||||
subject(:errors) do
|
||||
|
|
@ -59,4 +61,46 @@ RSpec.describe Ml::Model, feature_category: :mlops do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.find_or_create' do
|
||||
subject(:find_or_create) { described_class.find_or_create(project, name, experiment) }
|
||||
|
||||
let(:name) { existing_model.name }
|
||||
let(:project) { existing_model.project }
|
||||
let(:experiment) { default_experiment }
|
||||
|
||||
context 'when model name does not exist in the project' do
|
||||
let(:name) { 'new_model' }
|
||||
let(:experiment) { build(:ml_experiments, name: name, project: project) }
|
||||
|
||||
it 'creates a model', :aggregate_failures do
|
||||
expect { find_or_create }.to change { Ml::Model.count }.by(1)
|
||||
|
||||
expect(find_or_create.name).to eq(name)
|
||||
expect(find_or_create.project).to eq(project)
|
||||
expect(find_or_create.default_experiment).to eq(experiment)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when model name exists but project is different' do
|
||||
let(:project) { create(:project) }
|
||||
let(:experiment) { build(:ml_experiments, name: name, project: project) }
|
||||
|
||||
it 'creates a model', :aggregate_failures do
|
||||
expect { find_or_create }.to change { Ml::Model.count }.by(1)
|
||||
|
||||
expect(find_or_create.name).to eq(name)
|
||||
expect(find_or_create.project).to eq(project)
|
||||
expect(find_or_create.default_experiment).to eq(experiment)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when model exists' do
|
||||
it 'fetches existing model', :aggregate_failures do
|
||||
expect { find_or_create }.not_to change { Ml::Model.count }
|
||||
|
||||
expect(find_or_create).to eq(existing_model)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ RSpec.describe Ml::ModelVersion, feature_category: :mlops do
|
|||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let_it_be(:base_project) { create(:project) }
|
||||
let_it_be(:model) { create(:ml_models, project: base_project) }
|
||||
|
||||
describe 'associations' do
|
||||
it { is_expected.to belong_to(:project) }
|
||||
|
|
@ -15,7 +16,6 @@ RSpec.describe Ml::ModelVersion, feature_category: :mlops do
|
|||
|
||||
describe 'validation' do
|
||||
let_it_be(:valid_version) { 'valid_version' }
|
||||
let_it_be(:model) { create(:ml_models, project: base_project) }
|
||||
let_it_be(:valid_package) do
|
||||
build_stubbed(:ml_model_package, project: base_project, version: valid_version, name: model.name)
|
||||
end
|
||||
|
|
@ -87,4 +87,34 @@ RSpec.describe Ml::ModelVersion, feature_category: :mlops do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.find_or_create' do
|
||||
let_it_be(:existing_model_version) { create(:ml_model_versions, model: model, version: 'abc') }
|
||||
|
||||
let(:version) { existing_model_version.version }
|
||||
let(:package) { nil }
|
||||
|
||||
subject(:find_or_create) { described_class.find_or_create(model, version, package) }
|
||||
|
||||
context 'if model version exists' do
|
||||
it 'returns the model version', :aggregate_failures do
|
||||
expect { find_or_create }.not_to change { Ml::ModelVersion.count }
|
||||
is_expected.to eq(existing_model_version)
|
||||
end
|
||||
end
|
||||
|
||||
context 'if model version does not exist' do
|
||||
let(:version) { 'new_version' }
|
||||
let(:package) { create(:ml_model_package, project: model.project, name: model.name, version: version) }
|
||||
|
||||
it 'creates another model version', :aggregate_failures do
|
||||
expect { find_or_create }.to change { Ml::ModelVersion.count }.by(1)
|
||||
model_version = find_or_create
|
||||
|
||||
expect(model_version.version).to eq(version)
|
||||
expect(model_version.model).to eq(model)
|
||||
expect(model_version.package).to eq(package)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ RSpec.describe API::Admin::BatchedBackgroundMigrations, feature_category: :datab
|
|||
expect(json_response.first['id']).to eq(migration.id)
|
||||
expect(json_response.first['job_class_name']).to eq(migration.job_class_name)
|
||||
expect(json_response.first['table_name']).to eq(migration.table_name)
|
||||
expect(json_response.first['column_name']).to eq(migration.column_name)
|
||||
expect(json_response.first['status']).to eq(migration.status_name.to_s)
|
||||
expect(json_response.first['progress']).to be_zero
|
||||
end
|
||||
|
|
@ -151,6 +152,7 @@ RSpec.describe API::Admin::BatchedBackgroundMigrations, feature_category: :datab
|
|||
expect(json_response.first['id']).to eq(ci_database_migration.id)
|
||||
expect(json_response.first['job_class_name']).to eq(ci_database_migration.job_class_name)
|
||||
expect(json_response.first['table_name']).to eq(ci_database_migration.table_name)
|
||||
expect(json_response.first['column_name']).to eq(ci_database_migration.column_name)
|
||||
expect(json_response.first['status']).to eq(ci_database_migration.status_name.to_s)
|
||||
expect(json_response.first['progress']).to be_zero
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ::Ml::FindOrCreateExperimentService, feature_category: :mlops do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:user) { project.first_owner }
|
||||
let_it_be(:existing_experiment) { create(:ml_experiments, project: project, user: user) }
|
||||
|
||||
let(:name) { 'new_experiment' }
|
||||
|
||||
subject(:new_experiment) { described_class.new(project, name, user).execute }
|
||||
|
||||
describe '#execute' do
|
||||
it 'creates an experiment using Ml::Experiment.find_or_create', :aggregate_failures do
|
||||
expect(Ml::Experiment).to receive(:find_or_create).and_call_original
|
||||
|
||||
expect(new_experiment.name).to eq('new_experiment')
|
||||
expect(new_experiment.project).to eq(project)
|
||||
expect(new_experiment.user).to eq(user)
|
||||
end
|
||||
|
||||
context 'when experiment already exists' do
|
||||
let(:name) { existing_experiment.name }
|
||||
|
||||
it 'fetches existing experiment', :aggregate_failures do
|
||||
expect { new_experiment }.not_to change { Ml::Experiment.count }
|
||||
|
||||
expect(new_experiment).to eq(existing_experiment)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ::Ml::FindOrCreateModelService, feature_category: :mlops do
|
||||
let_it_be(:existing_model) { create(:ml_models) }
|
||||
let_it_be(:another_project) { create(:project) }
|
||||
|
||||
subject(:create_model) { described_class.new(project, name).execute }
|
||||
|
||||
describe '#execute' do
|
||||
context 'when model name does not exist in the project' do
|
||||
let(:name) { 'new_model' }
|
||||
let(:project) { existing_model.project }
|
||||
|
||||
it 'creates a model', :aggregate_failures do
|
||||
expect { create_model }.to change { Ml::Model.count }.by(1)
|
||||
|
||||
expect(create_model.name).to eq(name)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when model name exists but project is different' do
|
||||
let(:name) { existing_model.name }
|
||||
let(:project) { another_project }
|
||||
|
||||
it 'creates a model', :aggregate_failures do
|
||||
expect { create_model }.to change { Ml::Model.count }.by(1)
|
||||
|
||||
expect(create_model.name).to eq(name)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when model with name exists' do
|
||||
let(:name) { existing_model.name }
|
||||
let(:project) { existing_model.project }
|
||||
|
||||
it 'fetches existing model', :aggregate_failures do
|
||||
expect { create_model }.to change { Ml::Model.count }.by(0)
|
||||
|
||||
expect(create_model).to eq(existing_model)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ::Ml::FindOrCreateModelVersionService, feature_category: :mlops do
|
||||
let_it_be(:existing_version) { create(:ml_model_versions) }
|
||||
let_it_be(:another_project) { create(:project) }
|
||||
|
||||
let(:package) { nil }
|
||||
|
||||
let(:params) do
|
||||
{
|
||||
model_name: name,
|
||||
version: version,
|
||||
package: package
|
||||
}
|
||||
end
|
||||
|
||||
subject(:model_version) { described_class.new(project, params).execute }
|
||||
|
||||
describe '#execute' do
|
||||
context 'when model version exists' do
|
||||
let(:name) { existing_version.name }
|
||||
let(:version) { existing_version.version }
|
||||
let(:project) { existing_version.project }
|
||||
|
||||
it 'returns existing model version', :aggregate_failures do
|
||||
expect { model_version }.to change { Ml::ModelVersion.count }.by(0)
|
||||
expect(model_version).to eq(existing_version)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when model version does not exist' do
|
||||
let(:project) { existing_version.project }
|
||||
let(:name) { 'a_new_model' }
|
||||
let(:version) { 'a_new_version' }
|
||||
|
||||
let(:package) { create(:ml_model_package, project: project, name: name, version: version) }
|
||||
|
||||
it 'creates a new model version', :aggregate_failures do
|
||||
expect { model_version }.to change { Ml::ModelVersion.count }
|
||||
|
||||
expect(model_version.name).to eq(name)
|
||||
expect(model_version.version).to eq(version)
|
||||
expect(model_version.package).to eq(package)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue