Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b312c3d3b4
commit
a2ee63d924
|
@ -1 +1 @@
|
|||
b85367529ca34c8c423b94b7486c44e109ed553f
|
||||
bee8517ab043ff98c283a5f191e68e2bd75eb9de
|
||||
|
|
|
@ -30,7 +30,6 @@ import AlertSidebar from './alert_sidebar.vue';
|
|||
import AlertMetrics from './alert_metrics.vue';
|
||||
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
|
||||
import AlertSummaryRow from './alert_summary_row.vue';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
|
||||
const containerEl = document.querySelector('.page-with-contextual-sidebar');
|
||||
|
||||
|
@ -77,7 +76,6 @@ export default {
|
|||
SystemNote,
|
||||
AlertMetrics,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
inject: {
|
||||
projectPath: {
|
||||
default: '',
|
||||
|
@ -150,13 +148,10 @@ export default {
|
|||
},
|
||||
},
|
||||
environmentName() {
|
||||
return this.shouldDisplayEnvironment && this.alert?.environment?.name;
|
||||
return this.alert?.environment?.name;
|
||||
},
|
||||
environmentPath() {
|
||||
return this.shouldDisplayEnvironment && this.alert?.environment?.path;
|
||||
},
|
||||
shouldDisplayEnvironment() {
|
||||
return this.glFeatures.exposeEnvironmentPathInAlertDetails;
|
||||
return this.alert?.environment?.path;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
|
|
|
@ -7,7 +7,6 @@ import {
|
|||
convertToSentenceCase,
|
||||
splitCamelCase,
|
||||
} from '~/lib/utils/text_utility';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
|
||||
const thClass = 'gl-bg-transparent! gl-border-1! gl-border-b-solid! gl-border-gray-200!';
|
||||
const tdClass = 'gl-border-gray-100! gl-p-5!';
|
||||
|
@ -25,6 +24,7 @@ const allowedFields = [
|
|||
'endedAt',
|
||||
'details',
|
||||
'hosts',
|
||||
'environment',
|
||||
];
|
||||
|
||||
export default {
|
||||
|
@ -32,7 +32,6 @@ export default {
|
|||
GlLoadingIcon,
|
||||
GlTable,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
props: {
|
||||
alert: {
|
||||
type: Object,
|
||||
|
@ -60,9 +59,6 @@ export default {
|
|||
},
|
||||
],
|
||||
computed: {
|
||||
flaggedAllowedFields() {
|
||||
return this.shouldDisplayEnvironment ? [...allowedFields, 'environment'] : allowedFields;
|
||||
},
|
||||
items() {
|
||||
if (!this.alert) {
|
||||
return [];
|
||||
|
@ -84,13 +80,10 @@ export default {
|
|||
[],
|
||||
);
|
||||
},
|
||||
shouldDisplayEnvironment() {
|
||||
return this.glFeatures.exposeEnvironmentPathInAlertDetails;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isAllowed(fieldName) {
|
||||
return this.flaggedAllowedFields.includes(fieldName);
|
||||
return allowedFields.includes(fieldName);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -128,7 +128,7 @@ body.modal-open {
|
|||
}
|
||||
|
||||
.issues-import-modal,
|
||||
.issues-export-modal {
|
||||
.issuable-export-modal {
|
||||
.modal-header {
|
||||
justify-content: flex-start;
|
||||
|
||||
|
|
|
@ -10,6 +10,5 @@ class Projects::AlertManagementController < Projects::ApplicationController
|
|||
|
||||
def details
|
||||
@alert_id = params[:id]
|
||||
push_frontend_feature_flag(:expose_environment_path_in_alert_details, @project)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
|||
include SourcegraphDecorator
|
||||
include DiffHelper
|
||||
|
||||
skip_before_action :merge_request, only: [:index, :bulk_update]
|
||||
skip_before_action :merge_request, only: [:index, :bulk_update, :export_csv]
|
||||
before_action :apply_diff_view_cookie!, only: [:show]
|
||||
before_action :whitelist_query_limiting, only: [:assign_related_issues, :update]
|
||||
before_action :authorize_update_issuable!, only: [:close, :edit, :update, :remove_wip, :sort]
|
||||
|
@ -318,6 +318,16 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
|||
super
|
||||
end
|
||||
|
||||
def export_csv
|
||||
return render_404 unless Feature.enabled?(:export_merge_requests_as_csv, project)
|
||||
|
||||
IssuableExportCsvWorker.perform_async(:merge_request, current_user.id, project.id, finder_options.to_h) # rubocop:disable CodeReuse/Worker
|
||||
|
||||
index_path = project_merge_requests_path(project)
|
||||
message = _('Your CSV export has started. It will be emailed to %{email} when complete.') % { email: current_user.notification_email }
|
||||
redirect_to(index_path, notice: message)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
alias_method :subscribable_resource, :merge_request
|
||||
|
|
|
@ -25,7 +25,7 @@ module Packages
|
|||
.including_build_info
|
||||
.including_project_route
|
||||
.including_tags
|
||||
.for_projects(group_projects_visible_to_current_user)
|
||||
.for_projects(group_projects_visible_to_current_user.select(:id))
|
||||
.processed
|
||||
.has_version
|
||||
.sort_by_attribute("#{params[:order_by]}_#{params[:sort]}")
|
||||
|
@ -36,11 +36,14 @@ module Packages
|
|||
end
|
||||
|
||||
def group_projects_visible_to_current_user
|
||||
# according to project_policy.rb
|
||||
# access to packages is ruled by:
|
||||
# - project is public or the current user has access to it with at least the reporter level
|
||||
# - the repository feature is available to the current_user
|
||||
::Project
|
||||
.in_namespace(groups)
|
||||
.public_or_visible_to_user(current_user, Gitlab::Access::REPORTER)
|
||||
.with_project_feature
|
||||
.select { |project| Ability.allowed?(current_user, :read_package, project) }
|
||||
.with_feature_available_for_user(:repository, current_user)
|
||||
end
|
||||
|
||||
def package_type
|
||||
|
|
|
@ -18,9 +18,8 @@ module Types
|
|||
field :state, GraphQL::STRING_TYPE, null: false,
|
||||
description: 'State of the environment, for example: available/stopped'
|
||||
|
||||
field :path, GraphQL::STRING_TYPE, null: true,
|
||||
description: 'The path to the environment. Will always return null ' \
|
||||
'if `expose_environment_path_in_alert_details` feature flag is disabled'
|
||||
field :path, GraphQL::STRING_TYPE, null: false,
|
||||
description: 'The path to the environment.'
|
||||
|
||||
field :metrics_dashboard, Types::Metrics::DashboardType, null: true,
|
||||
description: 'Metrics dashboard schema for the environment',
|
||||
|
|
|
@ -41,6 +41,9 @@ module Types
|
|||
field :assignees, Types::UserType.connection_type, null: true,
|
||||
description: 'Assignees of the issue'
|
||||
|
||||
field :updated_by, Types::UserType, null: true,
|
||||
description: 'User that last updated the issue'
|
||||
|
||||
field :labels, Types::LabelType.connection_type, null: true,
|
||||
description: 'Labels of the issue'
|
||||
field :milestone, Types::MilestoneType, null: true,
|
||||
|
@ -114,6 +117,10 @@ module Types
|
|||
Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.author_id).find
|
||||
end
|
||||
|
||||
def updated_by
|
||||
Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.updated_by_id).find
|
||||
end
|
||||
|
||||
def milestone
|
||||
Gitlab::Graphql::Loaders::BatchModelLoader.new(Milestone, object.milestone_id).find
|
||||
end
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
class AuditEvent < ApplicationRecord
|
||||
include CreatedAtFilterable
|
||||
include IgnorableColumns
|
||||
include BulkInsertSafe
|
||||
include EachBatch
|
||||
|
||||
|
@ -14,8 +13,6 @@ class AuditEvent < ApplicationRecord
|
|||
:target_id
|
||||
].freeze
|
||||
|
||||
ignore_column :type, remove_with: '13.6', remove_after: '2020-11-22'
|
||||
|
||||
serialize :details, Hash # rubocop:disable Cop/ActiveRecordSerialize
|
||||
|
||||
belongs_to :user, foreign_key: :author_id
|
||||
|
@ -37,14 +34,6 @@ class AuditEvent < ApplicationRecord
|
|||
# https://gitlab.com/groups/gitlab-org/-/epics/2765
|
||||
after_validation :parallel_persist
|
||||
|
||||
# Note: After loading records, do not attempt to type cast objects it finds.
|
||||
# We are in the process of deprecating STI (i.e. SecurityEvent) out of AuditEvent.
|
||||
#
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/216845
|
||||
def self.inheritance_column
|
||||
:_type_disabled
|
||||
end
|
||||
|
||||
def self.order_by(method)
|
||||
case method.to_s
|
||||
when 'created_asc'
|
||||
|
|
|
@ -6,8 +6,6 @@ class EnvironmentPresenter < Gitlab::View::Presenter::Delegated
|
|||
presents :environment
|
||||
|
||||
def path
|
||||
if Feature.enabled?(:expose_environment_path_in_alert_details, project)
|
||||
project_environment_path(project, self)
|
||||
end
|
||||
project_environment_path(project, self)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,6 +14,7 @@ module Ci
|
|||
Gitlab::Ci::Pipeline::Chain::Config::Process,
|
||||
Gitlab::Ci::Pipeline::Chain::RemoveUnwantedChatJobs,
|
||||
Gitlab::Ci::Pipeline::Chain::Skip,
|
||||
Gitlab::Ci::Pipeline::Chain::SeedBlock,
|
||||
Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules,
|
||||
Gitlab::Ci::Pipeline::Chain::Seed,
|
||||
Gitlab::Ci::Pipeline::Chain::Limit::Size,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
- breadcrumb_title _("Details")
|
||||
- page_title _("Groups")
|
||||
- @content_class = "limit-container-width" unless fluid_layout
|
||||
|
||||
- if show_invite_banner?(@group)
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
.btn-group
|
||||
- if show_export_button
|
||||
= render 'projects/issues/export_csv/button'
|
||||
= render 'shared/issuable/csv_export/button', issuable_type: 'issues'
|
||||
|
||||
- if show_import_button
|
||||
= render 'projects/issues/import_csv/button'
|
||||
|
@ -23,7 +23,7 @@
|
|||
id: "new_issue_link"
|
||||
|
||||
- if show_export_button
|
||||
= render 'projects/issues/export_csv/modal'
|
||||
= render 'shared/issuable/csv_export/modal', issuable_type: 'issues'
|
||||
|
||||
- if show_import_button
|
||||
= render 'projects/issues/import_csv/modal'
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
- if current_user
|
||||
.issues-export-modal.modal
|
||||
.modal-dialog
|
||||
.modal-content{ data: { qa_selector: 'export_issues_modal' } }
|
||||
.modal-header
|
||||
%h3
|
||||
= _('Export issues')
|
||||
.svg-content.import-export-svg-container
|
||||
= image_tag 'illustrations/export-import.svg', alt: _('Import/Export illustration'), class: 'illustration'
|
||||
%a.close{ href: '#', 'data-dismiss' => 'modal' }
|
||||
= sprite_icon('close', css_class: 'gl-icon')
|
||||
.modal-body
|
||||
- issues_count = issuables_count_for_state(:issues, params[:state])
|
||||
- unless issues_count == -1 # The count timed out
|
||||
.modal-subheader
|
||||
= sprite_icon('check', css_class: 'gl-text-green-400')
|
||||
%strong.gl-ml-3
|
||||
= n_('%d issue selected', '%d issues selected', issues_count) % issues_count
|
||||
.modal-text
|
||||
= html_escape(_('The CSV export will be created in the background. Once finished, it will be sent to %{strong_open}%{email}%{strong_close} in an attachment.')) % { email: @current_user.notification_email, strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
|
||||
.modal-footer
|
||||
= link_to _('Export issues'), export_csv_project_issues_path(@project, request.query_parameters), method: :post, class: 'btn gl-button btn-success float-left', title: _('Export issues'), data: { track_label: "export_issues_csv", track_event: "click_button", track_value: "", qa_selector: "export_issues_button" }
|
|
@ -1,5 +1,12 @@
|
|||
- if Feature.enabled?(:export_merge_requests_as_csv, @project)
|
||||
.btn-group
|
||||
= render 'shared/issuable/csv_export/button', issuable_type: 'merge-requests'
|
||||
|
||||
- if @can_bulk_update
|
||||
= button_tag "Edit merge requests", class: "gl-button btn gl-mr-3 js-bulk-update-toggle"
|
||||
- if merge_project
|
||||
= link_to new_merge_request_path, class: "gl-button btn btn-success", title: "New merge request" do
|
||||
New merge request
|
||||
|
||||
- if Feature.enabled?(:export_merge_requests_as_csv, @project)
|
||||
= render 'shared/issuable/csv_export/modal', issuable_type: 'merge_requests'
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
- breadcrumb_title _("Details")
|
||||
- page_title _("Projects")
|
||||
- @content_class = "limit-container-width" unless fluid_layout
|
||||
|
||||
= content_for :meta_tags do
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
- if current_user
|
||||
%button.csv_download_link.btn.gl-button.has-tooltip{ title: _('Export as CSV'),
|
||||
data: { toggle: 'modal', target: '.issues-export-modal', qa_selector: 'export_as_csv_button' } }
|
||||
data: { toggle: 'modal', target: ".#{issuable_type}-export-modal", qa_selector: 'export_as_csv_button' } }
|
||||
= sprite_icon('export')
|
|
@ -0,0 +1,29 @@
|
|||
- class_name = "#{issuable_type.dasherize}-export-modal"
|
||||
- if current_user
|
||||
.modal.issuable-export-modal{ class: class_name }
|
||||
.modal-dialog
|
||||
.modal-content{ data: { qa_selector: "export_issuable_modal" } }
|
||||
.modal-header
|
||||
%h3
|
||||
= _("Export %{issuable_type}" % { issuable_type: issuable_type.humanize(capitalize: false) })
|
||||
.svg-content.import-export-svg-container
|
||||
= image_tag 'illustrations/export-import.svg', role: "presentation", class: 'illustration'
|
||||
%button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') }
|
||||
= sprite_icon('close', css_class: 'gl-icon')
|
||||
.modal-body
|
||||
- issuable_count = issuables_count_for_state(issuable_type.to_sym, params[:state])
|
||||
- unless issuable_count == -1 # The count timed out
|
||||
.modal-subheader
|
||||
= sprite_icon('check', css_class: 'gl-icon gl-color-green-400')
|
||||
%strong.gl-ml-3
|
||||
- if issuable_type.eql?('merge_requests')
|
||||
= n_("%{count} merge request selected", "%{count} merge requests selected", issuable_count) % { count: issuable_count }
|
||||
- else
|
||||
= n_("%{count} issue selected", "%{count} issues selected", issuable_count) % { count: issuable_count }
|
||||
.modal-text
|
||||
= html_escape(_('The CSV export will be created in the background. Once finished, it will be sent to %{strong_open}%{email}%{strong_close} in an attachment.')) % { email: @current_user.notification_email, strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
|
||||
.modal-footer
|
||||
- if issuable_type.eql?('merge_requests')
|
||||
= link_to _("Export merge requests"), export_csv_project_merge_requests_path(@project, request.query_parameters), method: :post, class: 'btn gl-button btn-success', data: { track_label: "export_merge_requests_csv", track_event: "click_button", track_value: "" }
|
||||
- else
|
||||
= link_to _('Export issues'), export_csv_project_issues_path(@project, request.query_parameters), method: :post, class: 'btn gl-button btn-success', data: { track_label: "export_issues_csv", track_event: "click_button", track_value: "", qa_selector: "export_issues_button" }
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Show the environment link on alert details page
|
||||
merge_request: 44130
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix an N+1 issue in Packages::GroupPackagesFinder
|
||||
merge_request: 45875
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove page_title from single project and group pages
|
||||
merge_request: 46223
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Disallow /autocomplete/projects route in robots.txt
|
||||
merge_request: 46115
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Expose issue updated by on GraphQL
|
||||
merge_request: 46015
|
||||
author:
|
||||
type: added
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
name: expose_environment_path_in_alert_details
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43414
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/258638
|
||||
name: ci_seed_block_run_before_workflow_rules
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45674
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/270439
|
||||
type: development
|
||||
group: group::progressive delivery
|
||||
group: group::pipeline authoring
|
||||
default_enabled: false
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
name: export_merge_requests_as_csv
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45130
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/267129
|
||||
type: development
|
||||
group: group::compliance
|
||||
default_enabled: false
|
|
@ -47,6 +47,7 @@ resources :merge_requests, concerns: :awardable, except: [:new, :create, :show],
|
|||
collection do
|
||||
get :diff_for_path
|
||||
post :bulk_update
|
||||
post :export_csv
|
||||
end
|
||||
|
||||
resources :discussions, only: [:show], constraints: { id: /\h{40}/ } do
|
||||
|
|
|
@ -460,16 +460,12 @@ The LDAP sync process:
|
|||
|
||||
### Adjusting LDAP user sync schedule **(STARTER ONLY)**
|
||||
|
||||
NOTE: **Note:**
|
||||
These are cron formatted values. You can use a crontab generator to create
|
||||
these values, for example <http://www.crontabgenerator.com/>.
|
||||
|
||||
By default, GitLab runs a worker once per day at 01:30 a.m. server time to
|
||||
check and update GitLab users against LDAP.
|
||||
|
||||
You can manually configure LDAP user sync times by setting the
|
||||
following configuration values, in cron format. If needed, you can
|
||||
use a [crontab generator](http://crontabgenerator.com).
|
||||
use a [crontab generator](http://www.crontabgenerator.com).
|
||||
The example below shows how to set LDAP user
|
||||
sync to run once every 12 hours at the top of the hour.
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ Consul nodes communicate using the raft protocol. If the current leader goes
|
|||
offline, there needs to be a leader election. A leader node must exist to facilitate
|
||||
synchronization across the cluster. If too many nodes go offline at the same time,
|
||||
the cluster will lose quorum and not elect a leader due to
|
||||
[broken consensus](https://www.consul.io/docs/internals/consensus.html).
|
||||
[broken consensus](https://www.consul.io/docs/architecture/consensus).
|
||||
|
||||
Consult the [troubleshooting section](#troubleshooting-consul) if the cluster is not
|
||||
able to recover after the upgrade. The [outage recovery](#outage-recovery) may
|
||||
|
|
|
@ -323,7 +323,7 @@ Any `Operation not permitted` errors means you should investigate your NFS serve
|
|||
If the traffic between your NFS server and NFS client(s) is subject to port filtering
|
||||
by a firewall, then you will need to reconfigure that firewall to allow NFS communication.
|
||||
|
||||
[This guide from TDLP](http://tldp.org/HOWTO/NFS-HOWTO/security.html#FIREWALLS)
|
||||
[This guide from TDLP](https://tldp.org/HOWTO/NFS-HOWTO/security.html#FIREWALLS)
|
||||
covers the basics of using NFS in a firewalled environment. Additionally, we encourage you to
|
||||
search for and review the specific documentation for your operating system or distribution and your firewall software.
|
||||
|
||||
|
|
|
@ -6231,10 +6231,9 @@ type Environment {
|
|||
name: String!
|
||||
|
||||
"""
|
||||
The path to the environment. Will always return null if
|
||||
`expose_environment_path_in_alert_details` feature flag is disabled
|
||||
The path to the environment.
|
||||
"""
|
||||
path: String
|
||||
path: String!
|
||||
|
||||
"""
|
||||
State of the environment, for example: available/stopped
|
||||
|
@ -7214,6 +7213,11 @@ type EpicIssue implements CurrentUserTodos & Noteable {
|
|||
"""
|
||||
updatedAt: Time!
|
||||
|
||||
"""
|
||||
User that last updated the issue
|
||||
"""
|
||||
updatedBy: User
|
||||
|
||||
"""
|
||||
Number of upvotes the issue has received
|
||||
"""
|
||||
|
@ -9467,6 +9471,11 @@ type Issue implements CurrentUserTodos & Noteable {
|
|||
"""
|
||||
updatedAt: Time!
|
||||
|
||||
"""
|
||||
User that last updated the issue
|
||||
"""
|
||||
updatedBy: User
|
||||
|
||||
"""
|
||||
Number of upvotes the issue has received
|
||||
"""
|
||||
|
|
|
@ -17214,14 +17214,18 @@
|
|||
},
|
||||
{
|
||||
"name": "path",
|
||||
"description": "The path to the environment. Will always return null if `expose_environment_path_in_alert_details` feature flag is disabled",
|
||||
"description": "The path to the environment.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -19865,6 +19869,20 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "updatedBy",
|
||||
"description": "User that last updated the issue",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "User",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "upvotes",
|
||||
"description": "Number of upvotes the issue has received",
|
||||
|
@ -25765,6 +25783,20 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "updatedBy",
|
||||
"description": "User that last updated the issue",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "User",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "upvotes",
|
||||
"description": "Number of upvotes the issue has received",
|
||||
|
|
|
@ -1020,7 +1020,7 @@ Describes where code is deployed for a project.
|
|||
| `latestOpenedMostSevereAlert` | AlertManagementAlert | The most severe open alert for the environment. If multiple alerts have equal severity, the most recent is returned |
|
||||
| `metricsDashboard` | MetricsDashboard | Metrics dashboard schema for the environment |
|
||||
| `name` | String! | Human-readable name of the environment |
|
||||
| `path` | String | The path to the environment. Will always return null if `expose_environment_path_in_alert_details` feature flag is disabled |
|
||||
| `path` | String! | The path to the environment. |
|
||||
| `state` | String! | State of the environment, for example: available/stopped |
|
||||
|
||||
### Epic
|
||||
|
@ -1149,6 +1149,7 @@ Relationship between an epic and an issue.
|
|||
| `totalTimeSpent` | Int! | Total time reported as spent on the issue |
|
||||
| `type` | IssueType | Type of the issue |
|
||||
| `updatedAt` | Time! | Timestamp of when the issue was last updated |
|
||||
| `updatedBy` | User | User that last updated the issue |
|
||||
| `upvotes` | Int! | Number of upvotes the issue has received |
|
||||
| `userNotesCount` | Int! | Number of user notes of the issue |
|
||||
| `userPermissions` | IssuePermissions! | Permissions for the current user on the resource |
|
||||
|
@ -1342,6 +1343,7 @@ Represents a recorded measurement (object count) for the Admins.
|
|||
| `totalTimeSpent` | Int! | Total time reported as spent on the issue |
|
||||
| `type` | IssueType | Type of the issue |
|
||||
| `updatedAt` | Time! | Timestamp of when the issue was last updated |
|
||||
| `updatedBy` | User | User that last updated the issue |
|
||||
| `upvotes` | Int! | Number of upvotes the issue has received |
|
||||
| `userNotesCount` | Int! | Number of user notes of the issue |
|
||||
| `userPermissions` | IssuePermissions! | Permissions for the current user on the resource |
|
||||
|
|
|
@ -22,7 +22,7 @@ cannot be used to authenticate with GitHub as an external CI/CD repository.
|
|||
## Connect with Personal Access Token
|
||||
|
||||
Personal access tokens can only be used to connect GitHub.com
|
||||
repositories to GitLab, and the GitHub user must have the [owner role](https://docs.github.com/en/github/getting-started-with-github/access-permissions-on-github).
|
||||
repositories to GitLab, and the GitHub user must have the [owner role](https://docs.github.com/en/free-pro-team@latest/github/getting-started-with-github/access-permissions-on-github).
|
||||
|
||||
To perform a one-off authorization with GitHub to grant GitLab access your
|
||||
repositories:
|
||||
|
|
|
@ -90,7 +90,7 @@ GitLab Runner then executes job scripts as the `gitlab-runner` user.
|
|||
1. You can now use `docker` command (and **install** `docker-compose` if needed).
|
||||
|
||||
By adding `gitlab-runner` to the `docker` group you are effectively granting `gitlab-runner` full root permissions.
|
||||
For more information please read [On Docker security: `docker` group considered harmful](https://www.andreas-jung.com/contents/on-docker-security-docker-group-considered-harmful).
|
||||
For more information please read [On Docker security: `docker` group considered harmful](https://blog.zopyx.com/on-docker-security-docker-group-considered-harmful/).
|
||||
|
||||
### Use Docker-in-Docker workflow with Docker executor
|
||||
|
||||
|
|
|
@ -298,7 +298,7 @@ project.
|
|||
- mix ecto.migrate
|
||||
```
|
||||
|
||||
This ensures that [rebar3](https://www.rebar3.org) and [hex](https://hex.pm) are both installed
|
||||
This ensures that [rebar3](https://rebar3.org) and [hex](https://hex.pm) are both installed
|
||||
before attempting to fetch the dependencies that are required to run the tests. Next, the `postgres` db
|
||||
is created and migrated with `ecto`, to ensure it's up-to-date.
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ type: index, howto
|
|||
|
||||
If you are currently using CircleCI, you can migrate your CI/CD pipelines to [GitLab CI/CD](../introduction/index.md),
|
||||
and start making use of all its powerful features. Check out our
|
||||
[CircleCI vs GitLab](https://about.gitlab.com/devops-tools/circle-ci-vs-gitlab.html)
|
||||
[CircleCI vs GitLab](https://about.gitlab.com/devops-tools/circle-ci-vs-gitlab/)
|
||||
comparison to see what's different.
|
||||
|
||||
We have collected several resources that you may find useful before starting to migrate.
|
||||
|
|
|
@ -4,7 +4,7 @@ The GitLab product is made up of several service components that run as independ
|
|||
|
||||
## Integration phases
|
||||
|
||||
The following outline re-uses the [maturity metric](https://about.gitlab.com/direction/maturity) naming as an example of the various phases of integrating a component. These are only loosely coupled to a components actual maturity, and are intended as a guide for implementation order (for example, a component does not need to be enabled by default to be Lovable, and being enabled by default does not on its own cause a component to be Lovable).
|
||||
The following outline re-uses the [maturity metric](https://about.gitlab.com/direction/maturity/) naming as an example of the various phases of integrating a component. These are only loosely coupled to a components actual maturity, and are intended as a guide for implementation order (for example, a component does not need to be enabled by default to be Lovable, and being enabled by default does not on its own cause a component to be Lovable).
|
||||
|
||||
- Proposed
|
||||
- [Proposing a new component](#proposing-a-new-component)
|
||||
|
|
|
@ -12,7 +12,7 @@ files.
|
|||
|
||||
For broader information about the documentation, see the [Documentation guidelines](index.md).
|
||||
|
||||
For guidelines specific to text in the GitLab interface, see the Pajamas [Content](https://design.gitlab.com/content/error-messages) section.
|
||||
For guidelines specific to text in the GitLab interface, see the Pajamas [Content](https://design.gitlab.com/content/error-messages/) section.
|
||||
|
||||
For information on how to validate styles locally or by using GitLab CI/CD, see [Testing](index.md#testing).
|
||||
|
||||
|
@ -25,7 +25,7 @@ If you can't find what you need:
|
|||
- View the GitLab Handbook for [writing style guidelines](https://about.gitlab.com/handbook/communication/#writing-style-guidelines) that apply to all GitLab content.
|
||||
- Refer to one of the following:
|
||||
|
||||
- [Microsoft Style Guide](https://docs.microsoft.com/en-us/style-guide/).
|
||||
- [Microsoft Style Guide](https://docs.microsoft.com/en-us/style-guide/welcome/).
|
||||
- [Google Developer Documentation Style Guide](https://developers.google.com/style).
|
||||
|
||||
If you have questions about style, mention `@tw-style` in an issue or merge request, or, if you have access to the GitLab Slack workspace, use `#docs-process`.
|
||||
|
@ -740,7 +740,7 @@ This is a list of available features:
|
|||
|
||||
- Don't add commas (`,`) or semicolons (`;`) to the ends of list items.
|
||||
- Only add periods to the end of a list item if the item consists of a complete
|
||||
sentence. The [definition of full sentence](https://www2.le.ac.uk/offices/ld/all-resources/writing/grammar/grammar-guides/sentence)
|
||||
sentence. The [definition of full sentence](https://www.le.ac.uk/oerresources/ssds/grammarguides/page_02.htm)
|
||||
is: _"a complete sentence always contains a verb, expresses a complete idea, and makes sense standing alone"_.
|
||||
- Be consistent throughout the list: if the majority of the items do not end in
|
||||
a period, do not end any of the items in a period, even if they consist of a
|
||||
|
@ -1693,7 +1693,7 @@ own line and surrounded by blank lines.
|
|||
- `> [Introduced](<link-to-issue>) in GitLab 11.3.`.
|
||||
|
||||
- If the feature is only available in GitLab Enterprise Edition, mention
|
||||
the [paid tier](https://about.gitlab.com/handbook/marketing/product-marketing/#tiers)
|
||||
the [paid tier](https://about.gitlab.com/handbook/marketing/strategic-marketing/tiers/)
|
||||
the feature is available in:
|
||||
- `> [Introduced](<link-to-issue>) in [GitLab Starter](https://about.gitlab.com/pricing/) 11.3.`.
|
||||
|
||||
|
|
|
@ -219,46 +219,11 @@ the correct runbook:
|
|||
|
||||
## View the environment that generated the alert
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/232492) in GitLab 13.5.
|
||||
> - It's [deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
|
||||
> - It's disabled on GitLab.com.
|
||||
> - It's not recommended for production use.
|
||||
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-environment-link-in-alert-details). **(CORE ONLY)**
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/232492) in GitLab 13.5
|
||||
behind a feature flag, disabled by default.
|
||||
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/232492) in GitLab 13.6.
|
||||
|
||||
CAUTION: **Warning:**
|
||||
This feature might not be available to you. Check the **version history** note above for details.
|
||||
|
||||
The environment information and the link are displayed in the [Alert Details tab](#alert-details-tab).
|
||||
|
||||
### Enable or disable Environment Link in Alert Details **(CORE ONLY)**
|
||||
|
||||
Viewing the environment is under development and not ready for production use. It is
|
||||
deployed behind a feature flag that is **disabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
|
||||
can enable it.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:expose_environment_path_in_alert_details)
|
||||
```
|
||||
|
||||
To enable for just a particular project:
|
||||
|
||||
```ruby
|
||||
project = Project.find_by_full_path('your-group/your-project')
|
||||
Feature.enable(:expose_environment_path_in_alert_details, project)
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:expose_environment_path_in_alert_details)
|
||||
```
|
||||
|
||||
To disable for just a particular project:
|
||||
|
||||
```ruby
|
||||
project = Project.find_by_full_path('your-group/your-project')
|
||||
Feature.disable(:expose_environment_path_in_alert_details, project)
|
||||
```
|
||||
|
|
|
@ -62,6 +62,10 @@ module Gitlab
|
|||
def self.manual_bridges_enabled?(project)
|
||||
::Feature.enabled?(:ci_manual_bridges, project, default_enabled: true)
|
||||
end
|
||||
|
||||
def self.seed_block_run_before_workflow_rules_enabled?(project)
|
||||
::Feature.enabled?(:ci_seed_block_run_before_workflow_rules, project, default_enabled: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,10 +19,12 @@ module Gitlab
|
|||
# Build to prevent erroring out on ambiguous refs.
|
||||
pipeline.protected = @command.protected_ref?
|
||||
|
||||
##
|
||||
# Populate pipeline with block argument of CreatePipelineService#execute.
|
||||
#
|
||||
@command.seeds_block&.call(pipeline)
|
||||
unless ::Gitlab::Ci::Features.seed_block_run_before_workflow_rules_enabled?(project)
|
||||
##
|
||||
# Populate pipeline with block argument of CreatePipelineService#execute.
|
||||
#
|
||||
@command.seeds_block&.call(pipeline)
|
||||
end
|
||||
|
||||
##
|
||||
# Gather all runtime build/stage errors
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Ci
|
||||
module Pipeline
|
||||
module Chain
|
||||
class SeedBlock < Chain::Base
|
||||
include Chain::Helpers
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
def perform!
|
||||
return unless ::Gitlab::Ci::Features.seed_block_run_before_workflow_rules_enabled?(project)
|
||||
|
||||
##
|
||||
# Populate pipeline with block argument of CreatePipelineService#execute.
|
||||
#
|
||||
@command.seeds_block&.call(pipeline)
|
||||
|
||||
raise "Pipeline cannot be persisted by `seeds_block`" if pipeline.persisted?
|
||||
end
|
||||
|
||||
def break?
|
||||
return false unless ::Gitlab::Ci::Features.seed_block_run_before_workflow_rules_enabled?(project)
|
||||
|
||||
pipeline.errors.any?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -220,11 +220,6 @@ msgid_plural "%d issues in this group"
|
|||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "%d issue selected"
|
||||
msgid_plural "%d issues selected"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "%d issue successfully imported with the label"
|
||||
msgid_plural "%d issues successfully imported with the label"
|
||||
msgstr[0] ""
|
||||
|
@ -412,6 +407,16 @@ msgstr ""
|
|||
msgid "%{count} files touched"
|
||||
msgstr ""
|
||||
|
||||
msgid "%{count} issue selected"
|
||||
msgid_plural "%{count} issues selected"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "%{count} merge request selected"
|
||||
msgid_plural "%{count} merge requests selected"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "%{count} more"
|
||||
msgstr ""
|
||||
|
||||
|
@ -10870,6 +10875,9 @@ msgstr ""
|
|||
msgid "Export issues"
|
||||
msgstr ""
|
||||
|
||||
msgid "Export merge requests"
|
||||
msgstr ""
|
||||
|
||||
msgid "Export project"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -42,8 +42,8 @@
|
|||
"@babel/plugin-syntax-import-meta": "^7.10.1",
|
||||
"@babel/preset-env": "^7.10.1",
|
||||
"@gitlab/at.js": "1.5.5",
|
||||
"@gitlab/svgs": "1.173.0",
|
||||
"@gitlab/ui": "21.35.2",
|
||||
"@gitlab/svgs": "1.174.0",
|
||||
"@gitlab/ui": "21.38.0",
|
||||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "^6.0.3-3",
|
||||
"@rails/ujs": "^6.0.3-2",
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
# Global routes
|
||||
User-Agent: *
|
||||
Disallow: /autocomplete/users
|
||||
Disallow: /autocomplete/projects
|
||||
Disallow: /search
|
||||
Disallow: /admin
|
||||
Disallow: /profile
|
||||
|
|
|
@ -15,13 +15,13 @@ module QA
|
|||
element :avatar_counter_content
|
||||
end
|
||||
|
||||
view 'app/views/projects/issues/export_csv/_button.html.haml' do
|
||||
view 'app/views/shared/issuable/csv_export/_button.html.haml' do
|
||||
element :export_as_csv_button
|
||||
end
|
||||
|
||||
view 'app/views/projects/issues/export_csv/_modal.html.haml' do
|
||||
view 'app/views/shared/issuable/csv_export/_modal.html.haml' do
|
||||
element :export_issues_button
|
||||
element :export_issues_modal
|
||||
element :export_issuable_modal
|
||||
end
|
||||
|
||||
view 'app/views/projects/issues/import_csv/_button.html.haml' do
|
||||
|
@ -64,7 +64,7 @@ module QA
|
|||
end
|
||||
|
||||
def export_issues_modal
|
||||
find_element(:export_issues_modal)
|
||||
find_element(:export_issuable_modal)
|
||||
end
|
||||
|
||||
def go_to_jira_import_form
|
||||
|
|
|
@ -1994,4 +1994,37 @@ RSpec.describe Projects::MergeRequestsController do
|
|||
expect(assigns(:noteable)).not_to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST export_csv' do
|
||||
subject { post :export_csv, params: { namespace_id: project.namespace, project_id: project } }
|
||||
|
||||
before do
|
||||
stub_feature_flags(export_merge_requests_as_csv: project)
|
||||
end
|
||||
|
||||
it 'redirects to the merge request index' do
|
||||
subject
|
||||
|
||||
expect(response).to redirect_to(project_merge_requests_path(project))
|
||||
expect(response.flash[:notice]).to match(/\AYour CSV export has started/i)
|
||||
end
|
||||
|
||||
it 'enqueues an IssuableExportCsvWorker worker' do
|
||||
expect(IssuableExportCsvWorker).to receive(:perform_async).with(:merge_request, user.id, project.id, anything)
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
context 'feature is disabled' do
|
||||
before do
|
||||
stub_feature_flags(export_merge_requests_as_csv: false)
|
||||
end
|
||||
|
||||
it 'expects a 404 response' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -41,21 +41,6 @@ RSpec.describe 'Alert management', :js do
|
|||
expect(page).to have_content(environment.name)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when expose_environment_path_in_alert_details feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(expose_environment_path_in_alert_details: false)
|
||||
end
|
||||
|
||||
it 'does not show the environment name' do
|
||||
visit(details_project_alert_management_path(project, alert))
|
||||
|
||||
within('.alert-management-details-table') do
|
||||
expect(page).to have_content(alert.title)
|
||||
expect(page).not_to have_content(environment.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Merge Requests > Exports as CSV', :js do
|
||||
let!(:project) { create(:project, :public, :repository) }
|
||||
let!(:user) { project.creator }
|
||||
let!(:open_mr) { create(:merge_request, title: 'Bugfix1', source_project: project, target_project: project, source_branch: 'bugfix1') }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
subject { page.find('.nav-controls') }
|
||||
|
||||
context 'feature is not enabled' do
|
||||
before do
|
||||
stub_feature_flags(export_merge_requests_as_csv: false)
|
||||
visit(project_merge_requests_path(project))
|
||||
end
|
||||
|
||||
it { is_expected.not_to have_button('Export as CSV') }
|
||||
end
|
||||
|
||||
context 'feature is enabled for a project' do
|
||||
before do
|
||||
stub_feature_flags(export_merge_requests_as_csv: project)
|
||||
visit(project_merge_requests_path(project))
|
||||
end
|
||||
|
||||
it { is_expected.to have_button('Export as CSV') }
|
||||
|
||||
context 'button is clicked' do
|
||||
before do
|
||||
click_button('Export as CSV')
|
||||
end
|
||||
|
||||
it 'shows a success message' do
|
||||
click_link('Export merge requests')
|
||||
|
||||
expect(page).to have_content 'Your CSV export has started.'
|
||||
expect(page).to have_content "It will be emailed to #{user.email} when complete"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,13 +2,16 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Packages::GroupPackagesFinder do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project) { create(:project, namespace: group) }
|
||||
let(:another_group) { create(:group) }
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be_with_reload(:group) { create(:group) }
|
||||
let_it_be_with_reload(:project) { create(:project, namespace: group, builds_access_level: ProjectFeature::PRIVATE, merge_requests_access_level: ProjectFeature::PRIVATE) }
|
||||
|
||||
let(:add_user_to_group) { true }
|
||||
|
||||
before do
|
||||
group.add_developer(user)
|
||||
group.add_developer(user) if add_user_to_group
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
|
@ -27,16 +30,16 @@ RSpec.describe Packages::GroupPackagesFinder do
|
|||
end
|
||||
|
||||
context 'group has packages' do
|
||||
let!(:package1) { create(:maven_package, project: project) }
|
||||
let!(:package2) { create(:maven_package, project: project) }
|
||||
let!(:package3) { create(:maven_package) }
|
||||
let_it_be(:package1) { create(:maven_package, project: project) }
|
||||
let_it_be(:package2) { create(:maven_package, project: project) }
|
||||
let_it_be(:package3) { create(:maven_package) }
|
||||
|
||||
it { is_expected.to match_array([package1, package2]) }
|
||||
|
||||
context 'subgroup has packages' do
|
||||
let(:subgroup) { create(:group, parent: group) }
|
||||
let(:subproject) { create(:project, namespace: subgroup) }
|
||||
let!(:package4) { create(:npm_package, project: subproject) }
|
||||
let_it_be_with_reload(:subgroup) { create(:group, parent: group) }
|
||||
let_it_be_with_reload(:subproject) { create(:project, namespace: subgroup, builds_access_level: ProjectFeature::PRIVATE, merge_requests_access_level: ProjectFeature::PRIVATE) }
|
||||
let_it_be(:package4) { create(:npm_package, project: subproject) }
|
||||
|
||||
it { is_expected.to match_array([package1, package2, package4]) }
|
||||
|
||||
|
@ -45,16 +48,87 @@ RSpec.describe Packages::GroupPackagesFinder do
|
|||
|
||||
it { is_expected.to match_array([package1, package2]) }
|
||||
end
|
||||
|
||||
context 'permissions' do
|
||||
let(:add_user_to_group) { false }
|
||||
|
||||
where(:role, :project_visibility, :repository_visibility, :packages_returned) do
|
||||
:anonymous | :public | :enabled | :all
|
||||
:guest | :public | :enabled | :all
|
||||
:reporter | :public | :enabled | :all
|
||||
:developer | :public | :enabled | :all
|
||||
:maintainer | :public | :enabled | :all
|
||||
:anonymous | :public | :private | :none
|
||||
:guest | :public | :private | :all
|
||||
:reporter | :public | :private | :all
|
||||
:developer | :public | :private | :all
|
||||
:maintainer | :public | :private | :all
|
||||
:anonymous | :private | :enabled | :none
|
||||
:guest | :private | :enabled | :none
|
||||
:reporter | :private | :enabled | :all
|
||||
:developer | :private | :enabled | :all
|
||||
:maintainer | :private | :enabled | :all
|
||||
:anonymous | :private | :private | :none
|
||||
:guest | :private | :private | :none
|
||||
:reporter | :private | :private | :all
|
||||
:developer | :private | :private | :all
|
||||
:maintainer | :private | :private | :all
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:expected_packages) do
|
||||
case packages_returned
|
||||
when :all
|
||||
[package1, package2, package4]
|
||||
when :none
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
subgroup.update!(visibility: project_visibility.to_s)
|
||||
group.update!(visibility: project_visibility.to_s)
|
||||
project.update!(
|
||||
visibility: project_visibility.to_s,
|
||||
repository_access_level: repository_visibility.to_s
|
||||
)
|
||||
subproject.update!(
|
||||
visibility: project_visibility.to_s,
|
||||
repository_access_level: repository_visibility.to_s
|
||||
)
|
||||
|
||||
unless role == :anonymous
|
||||
project.add_user(user, role)
|
||||
subproject.add_user(user, role)
|
||||
end
|
||||
end
|
||||
|
||||
it { is_expected.to match_array(expected_packages) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'avoid N+1 query' do
|
||||
it 'avoids N+1 database queries' do
|
||||
count = ActiveRecord::QueryRecorder.new { subject }
|
||||
.count
|
||||
|
||||
Packages::Package.package_types.keys.each do |package_type|
|
||||
create("#{package_type}_package", project: create(:project, namespace: subgroup))
|
||||
end
|
||||
|
||||
expect { described_class.new(user, group, params).execute }.not_to exceed_query_limit(count)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are processing packages' do
|
||||
let!(:package4) { create(:nuget_package, project: project, name: Packages::Nuget::CreatePackageService::TEMPORARY_PACKAGE_NAME) }
|
||||
let_it_be(:package4) { create(:nuget_package, project: project, name: Packages::Nuget::CreatePackageService::TEMPORARY_PACKAGE_NAME) }
|
||||
|
||||
it { is_expected.to match_array([package1, package2]) }
|
||||
end
|
||||
|
||||
context 'does not include packages without version number' do
|
||||
let!(:package_without_version) { create(:maven_package, project: project, version: nil) }
|
||||
let_it_be(:package_without_version) { create(:maven_package, project: project, version: nil) }
|
||||
|
||||
it { is_expected.not_to include(package_without_version) }
|
||||
end
|
||||
|
@ -80,7 +154,7 @@ RSpec.describe Packages::GroupPackagesFinder do
|
|||
end
|
||||
|
||||
context 'group has package of all types' do
|
||||
package_types.each { |pt| let!("package_#{pt}") { create("#{pt}_package", project: project) } }
|
||||
package_types.each { |pt| let_it_be("package_#{pt}") { create("#{pt}_package", project: project) } }
|
||||
|
||||
package_types.each do |package_type|
|
||||
it_behaves_like 'with package type', package_type
|
||||
|
@ -98,7 +172,7 @@ RSpec.describe Packages::GroupPackagesFinder do
|
|||
end
|
||||
|
||||
context 'package type is nil' do
|
||||
let!(:package1) { create(:maven_package, project: project) }
|
||||
let_it_be(:package1) { create(:maven_package, project: project) }
|
||||
|
||||
subject { described_class.new(user, group, package_type: nil).execute }
|
||||
|
||||
|
@ -110,47 +184,5 @@ RSpec.describe Packages::GroupPackagesFinder do
|
|||
|
||||
it { expect { subject }.to raise_exception(described_class::InvalidPackageTypeError) }
|
||||
end
|
||||
|
||||
context 'when project is public' do
|
||||
let_it_be(:other_user) { create(:user) }
|
||||
let(:finder) { described_class.new(other_user, group) }
|
||||
|
||||
before do
|
||||
project.update!(visibility_level: ProjectFeature::ENABLED)
|
||||
end
|
||||
|
||||
context 'when packages are public' do
|
||||
before do
|
||||
project.project_feature.update!(
|
||||
builds_access_level: ProjectFeature::PRIVATE,
|
||||
merge_requests_access_level: ProjectFeature::PRIVATE,
|
||||
repository_access_level: ProjectFeature::ENABLED)
|
||||
end
|
||||
|
||||
it 'returns group packages' do
|
||||
package1 = create(:maven_package, project: project)
|
||||
package2 = create(:maven_package, project: project)
|
||||
create(:maven_package)
|
||||
|
||||
expect(finder.execute).to match_array([package1, package2])
|
||||
end
|
||||
end
|
||||
|
||||
context 'packages are members only' do
|
||||
before do
|
||||
project.project_feature.update!(
|
||||
builds_access_level: ProjectFeature::PRIVATE,
|
||||
merge_requests_access_level: ProjectFeature::PRIVATE,
|
||||
repository_access_level: ProjectFeature::PRIVATE)
|
||||
|
||||
create(:maven_package, project: project)
|
||||
create(:maven_package)
|
||||
end
|
||||
|
||||
it 'filters out the project if the user doesn\'t have permission' do
|
||||
expect(finder.execute).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,11 +20,7 @@ const environmentName = 'Production';
|
|||
const environmentPath = '/fake/path';
|
||||
|
||||
describe('AlertDetails', () => {
|
||||
let environmentData = {
|
||||
name: environmentName,
|
||||
path: environmentPath,
|
||||
};
|
||||
let glFeatures = { exposeEnvironmentPathInAlertDetails: false };
|
||||
let environmentData = { name: environmentName, path: environmentPath };
|
||||
let mock;
|
||||
let wrapper;
|
||||
const projectPath = 'root/alerts';
|
||||
|
@ -40,7 +36,6 @@ describe('AlertDetails', () => {
|
|||
projectPath,
|
||||
projectIssuesPath,
|
||||
projectId,
|
||||
glFeatures,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -159,33 +154,21 @@ describe('AlertDetails', () => {
|
|||
});
|
||||
|
||||
describe('environment fields', () => {
|
||||
describe('when exposeEnvironmentPathInAlertDetails is disabled', () => {
|
||||
beforeEach(mountComponent);
|
||||
it('should show the environment name with a link to the path', () => {
|
||||
mountComponent();
|
||||
const path = findEnvironmentPath();
|
||||
|
||||
it('should not show the environment', () => {
|
||||
expect(findEnvironmentName().exists()).toBe(false);
|
||||
expect(findEnvironmentPath().exists()).toBe(false);
|
||||
});
|
||||
expect(findEnvironmentName().exists()).toBe(false);
|
||||
expect(path.text()).toBe(environmentName);
|
||||
expect(path.attributes('href')).toBe(environmentPath);
|
||||
});
|
||||
|
||||
describe('when exposeEnvironmentPathInAlertDetails is enabled', () => {
|
||||
beforeEach(() => {
|
||||
glFeatures = { exposeEnvironmentPathInAlertDetails: true };
|
||||
mountComponent();
|
||||
});
|
||||
it('should only show the environment name if the path is not provided', () => {
|
||||
environmentData = { name: environmentName, path: null };
|
||||
mountComponent();
|
||||
|
||||
it('should show the environment name with link to path', () => {
|
||||
expect(findEnvironmentName().exists()).toBe(false);
|
||||
expect(findEnvironmentPath().text()).toBe(environmentName);
|
||||
expect(findEnvironmentPath().attributes('href')).toBe(environmentPath);
|
||||
});
|
||||
|
||||
it('should only show the environment name if the path is not provided', () => {
|
||||
environmentData = { name: environmentName, path: null };
|
||||
mountComponent();
|
||||
expect(findEnvironmentPath().exists()).toBe(false);
|
||||
expect(findEnvironmentName().text()).toBe(environmentName);
|
||||
});
|
||||
expect(findEnvironmentPath().exists()).toBe(false);
|
||||
expect(findEnvironmentName().text()).toBe(environmentName);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -195,6 +178,7 @@ describe('AlertDetails', () => {
|
|||
mountComponent({
|
||||
data: { alert: { ...mockAlert, issueIid }, sidebarStatus: false },
|
||||
});
|
||||
|
||||
expect(findViewIncidentBtn().exists()).toBe(true);
|
||||
expect(findViewIncidentBtn().attributes('href')).toBe(
|
||||
joinPaths(projectIssuesPath, issueIid),
|
||||
|
@ -220,8 +204,8 @@ describe('AlertDetails', () => {
|
|||
jest
|
||||
.spyOn(wrapper.vm.$apollo, 'mutate')
|
||||
.mockResolvedValue({ data: { createAlertIssue: { issue: { iid: issueIid } } } });
|
||||
|
||||
findCreateIncidentBtn().trigger('click');
|
||||
|
||||
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
|
||||
mutation: createIssueMutation,
|
||||
variables: {
|
||||
|
@ -251,6 +235,7 @@ describe('AlertDetails', () => {
|
|||
beforeEach(() => {
|
||||
mountComponent({ data: { alert: mockAlert } });
|
||||
});
|
||||
|
||||
it('should display a table of raw alert details data', () => {
|
||||
expect(findDetailsTable().exists()).toBe(true);
|
||||
});
|
||||
|
|
|
@ -23,14 +23,10 @@ const environmentPath = '/fake/path';
|
|||
|
||||
describe('AlertDetails', () => {
|
||||
let environmentData = { name: environmentName, path: environmentPath };
|
||||
let glFeatures = { exposeEnvironmentPathInAlertDetails: false };
|
||||
let wrapper;
|
||||
|
||||
function mountComponent(propsData = {}) {
|
||||
wrapper = mount(AlertDetailsTable, {
|
||||
provide: {
|
||||
glFeatures,
|
||||
},
|
||||
propsData: {
|
||||
alert: {
|
||||
...mockAlert,
|
||||
|
@ -97,34 +93,19 @@ describe('AlertDetails', () => {
|
|||
expect(findTableField(fields, 'Severity').exists()).toBe(true);
|
||||
expect(findTableField(fields, 'Status').exists()).toBe(true);
|
||||
expect(findTableField(fields, 'Hosts').exists()).toBe(true);
|
||||
expect(findTableField(fields, 'Environment').exists()).toBe(false);
|
||||
expect(findTableField(fields, 'Environment').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('should not show disallowed and flaggedAllowed alert fields', () => {
|
||||
it('should not show disallowed alert fields', () => {
|
||||
const fields = findTableKeys();
|
||||
|
||||
expect(findTableField(fields, 'Typename').exists()).toBe(false);
|
||||
expect(findTableField(fields, 'Todos').exists()).toBe(false);
|
||||
expect(findTableField(fields, 'Notes').exists()).toBe(false);
|
||||
expect(findTableField(fields, 'Assignees').exists()).toBe(false);
|
||||
expect(findTableField(fields, 'Environment').exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when exposeEnvironmentPathInAlertDetails is enabled', () => {
|
||||
beforeEach(() => {
|
||||
glFeatures = { exposeEnvironmentPathInAlertDetails: true };
|
||||
mountComponent();
|
||||
});
|
||||
|
||||
it('should show flaggedAllowed alert fields', () => {
|
||||
const fields = findTableKeys();
|
||||
|
||||
expect(findTableField(fields, 'Environment').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('should display only the name for the environment', () => {
|
||||
expect(findTableFieldValueByKey('Iid').text()).toBe('1527542');
|
||||
expect(findTableFieldValueByKey('Environment').text()).toBe(environmentName);
|
||||
});
|
||||
|
||||
|
|
|
@ -44,18 +44,12 @@ RSpec.describe GitlabSchema.types['Environment'] do
|
|||
expect(subject['data']['project']['environment']['name']).to eq(environment.name)
|
||||
end
|
||||
|
||||
it 'returns the path when the feature is enabled' do
|
||||
it 'returns the path to the environment' do
|
||||
expect(subject['data']['project']['environment']['path']).to eq(
|
||||
Gitlab::Routing.url_helpers.project_environment_path(project, environment)
|
||||
)
|
||||
end
|
||||
|
||||
it 'does not return the path when the feature is disabled' do
|
||||
stub_feature_flags(expose_environment_path_in_alert_details: false)
|
||||
|
||||
expect(subject['data']['project']['environment']['path']).to be_nil
|
||||
end
|
||||
|
||||
context 'when query alert data for the environment' do
|
||||
let_it_be(:query) do
|
||||
%(
|
||||
|
|
|
@ -14,7 +14,7 @@ RSpec.describe GitlabSchema.types['Issue'] do
|
|||
specify { expect(described_class.interfaces).to include(Types::CurrentUserTodos) }
|
||||
|
||||
it 'has specific fields' do
|
||||
fields = %i[id iid title description state reference author assignees participants labels milestone due_date
|
||||
fields = %i[id iid title description state reference author assignees updated_by participants labels milestone due_date
|
||||
confidential discussion_locked upvotes downvotes user_notes_count web_path web_url relative_position
|
||||
subscribed time_estimate total_time_spent human_time_estimate human_total_time_spent closed_at created_at updated_at task_completion_status
|
||||
designs design_collection alert_management_alert severity current_user_todos]
|
||||
|
|
|
@ -22,6 +22,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Populate do
|
|||
[
|
||||
Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command),
|
||||
Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command),
|
||||
Gitlab::Ci::Pipeline::Chain::SeedBlock.new(pipeline, command),
|
||||
Gitlab::Ci::Pipeline::Chain::Seed.new(pipeline, command)
|
||||
]
|
||||
end
|
||||
|
@ -180,23 +181,21 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Populate do
|
|||
->(pipeline) { pipeline.variables.create!(key: 'VAR', value: '123') }
|
||||
end
|
||||
|
||||
it 'wastes pipeline iid' do
|
||||
expect { run_chain }.to raise_error(ActiveRecord::RecordNotSaved)
|
||||
|
||||
last_iid = InternalId.ci_pipelines
|
||||
.where(project_id: project.id)
|
||||
.last.last_value
|
||||
|
||||
expect(last_iid).to be > 0
|
||||
it 'raises error' do
|
||||
expect { run_chain }.to raise_error(ActiveRecord::RecordNotSaved,
|
||||
'You cannot call create unless the parent is saved')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline gets persisted during the process' do
|
||||
let(:pipeline) { create(:ci_pipeline, project: project) }
|
||||
before do
|
||||
dependencies.each(&:perform!)
|
||||
pipeline.save!
|
||||
end
|
||||
|
||||
it 'raises error' do
|
||||
expect { run_chain }.to raise_error(described_class::PopulateError)
|
||||
expect { step.perform! }.to raise_error(described_class::PopulateError)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Ci::Pipeline::Chain::SeedBlock do
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:user) { create(:user, developer_projects: [project]) }
|
||||
let(:seeds_block) { }
|
||||
|
||||
let(:command) do
|
||||
Gitlab::Ci::Pipeline::Chain::Command.new(
|
||||
project: project,
|
||||
current_user: user,
|
||||
origin_ref: 'master',
|
||||
seeds_block: seeds_block)
|
||||
end
|
||||
|
||||
let(:pipeline) { build(:ci_pipeline, project: project) }
|
||||
|
||||
describe '#perform!' do
|
||||
before do
|
||||
stub_ci_pipeline_yaml_file(YAML.dump(config))
|
||||
end
|
||||
|
||||
subject(:run_chain) do
|
||||
[
|
||||
Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command),
|
||||
Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command)
|
||||
].map(&:perform!)
|
||||
|
||||
described_class.new(pipeline, command).perform!
|
||||
end
|
||||
|
||||
let(:config) do
|
||||
{ rspec: { script: 'rake' } }
|
||||
end
|
||||
|
||||
context 'when there is not seeds_block' do
|
||||
it 'does nothing' do
|
||||
expect { run_chain }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is seeds_block' do
|
||||
let(:seeds_block) do
|
||||
->(pipeline) { pipeline.variables.build(key: 'VAR', value: '123') }
|
||||
end
|
||||
|
||||
it 'executes the block' do
|
||||
run_chain
|
||||
|
||||
expect(pipeline.variables.size).to eq(1)
|
||||
end
|
||||
|
||||
context 'when FF ci_seed_block_run_before_workflow_rules is disabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_seed_block_run_before_workflow_rules: false)
|
||||
end
|
||||
|
||||
it 'does not execute the block' do
|
||||
run_chain
|
||||
|
||||
expect(pipeline.variables.size).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the seeds_block tries to save the pipelie' do
|
||||
let(:seeds_block) do
|
||||
->(pipeline) { pipeline.save! }
|
||||
end
|
||||
|
||||
it 'raises error' do
|
||||
expect { run_chain }.to raise_error('Pipeline cannot be persisted by `seeds_block`')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,22 +5,14 @@ require 'spec_helper'
|
|||
RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:user) { create(:user, developer_projects: [project]) }
|
||||
let(:seeds_block) { }
|
||||
|
||||
let(:command) do
|
||||
Gitlab::Ci::Pipeline::Chain::Command.new(
|
||||
project: project,
|
||||
current_user: user,
|
||||
origin_ref: 'master',
|
||||
seeds_block: nil)
|
||||
end
|
||||
|
||||
def run_chain(pipeline, command)
|
||||
[
|
||||
Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command),
|
||||
Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command)
|
||||
].map(&:perform!)
|
||||
|
||||
described_class.new(pipeline, command).perform!
|
||||
seeds_block: seeds_block)
|
||||
end
|
||||
|
||||
let(:pipeline) { build(:ci_pipeline, project: project) }
|
||||
|
@ -28,22 +20,36 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
|
|||
describe '#perform!' do
|
||||
before do
|
||||
stub_ci_pipeline_yaml_file(YAML.dump(config))
|
||||
run_chain(pipeline, command)
|
||||
end
|
||||
|
||||
let(:config) do
|
||||
{ rspec: { script: 'rake' } }
|
||||
end
|
||||
|
||||
subject(:run_chain) do
|
||||
[
|
||||
Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command),
|
||||
Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command)
|
||||
].map(&:perform!)
|
||||
|
||||
described_class.new(pipeline, command).perform!
|
||||
end
|
||||
|
||||
it 'allocates next IID' do
|
||||
run_chain
|
||||
|
||||
expect(pipeline.iid).to be_present
|
||||
end
|
||||
|
||||
it 'ensures ci_ref' do
|
||||
run_chain
|
||||
|
||||
expect(pipeline.ci_ref).to be_present
|
||||
end
|
||||
|
||||
it 'sets the seeds in the command object' do
|
||||
run_chain
|
||||
|
||||
expect(command.stage_seeds).to all(be_a Gitlab::Ci::Pipeline::Seed::Base)
|
||||
expect(command.stage_seeds.count).to eq 1
|
||||
end
|
||||
|
@ -58,6 +64,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
|
|||
end
|
||||
|
||||
it 'correctly fabricates a stage seeds object' do
|
||||
run_chain
|
||||
|
||||
seeds = command.stage_seeds
|
||||
expect(seeds.size).to eq 2
|
||||
expect(seeds.first.attributes[:name]).to eq 'test'
|
||||
|
@ -81,6 +89,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
|
|||
end
|
||||
|
||||
it 'returns stage seeds only assigned to master' do
|
||||
run_chain
|
||||
|
||||
seeds = command.stage_seeds
|
||||
|
||||
expect(seeds.size).to eq 1
|
||||
|
@ -100,6 +110,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
|
|||
end
|
||||
|
||||
it 'returns stage seeds only assigned to schedules' do
|
||||
run_chain
|
||||
|
||||
seeds = command.stage_seeds
|
||||
|
||||
expect(seeds.size).to eq 1
|
||||
|
@ -127,6 +139,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
|
|||
let(:pipeline) { build(:ci_pipeline, project: project) }
|
||||
|
||||
it 'returns seeds for kubernetes dependent job' do
|
||||
run_chain
|
||||
|
||||
seeds = command.stage_seeds
|
||||
|
||||
expect(seeds.size).to eq 2
|
||||
|
@ -138,6 +152,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
|
|||
|
||||
context 'when kubernetes is not active' do
|
||||
it 'does not return seeds for kubernetes dependent job' do
|
||||
run_chain
|
||||
|
||||
seeds = command.stage_seeds
|
||||
|
||||
expect(seeds.size).to eq 1
|
||||
|
@ -155,11 +171,39 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
|
|||
end
|
||||
|
||||
it 'returns stage seeds only when variables expression is truthy' do
|
||||
run_chain
|
||||
|
||||
seeds = command.stage_seeds
|
||||
|
||||
expect(seeds.size).to eq 1
|
||||
expect(seeds.dig(0, 0, :name)).to eq 'unit'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is seeds_block' do
|
||||
let(:seeds_block) do
|
||||
->(pipeline) { pipeline.variables.build(key: 'VAR', value: '123') }
|
||||
end
|
||||
|
||||
context 'when FF ci_seed_block_run_before_workflow_rules is enabled' do
|
||||
it 'does not execute the block' do
|
||||
run_chain
|
||||
|
||||
expect(pipeline.variables.size).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when FF ci_seed_block_run_before_workflow_rules is disabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_seed_block_run_before_workflow_rules: false)
|
||||
end
|
||||
|
||||
it 'executes the block' do
|
||||
run_chain
|
||||
|
||||
expect(pipeline.variables.size).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -591,9 +591,21 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
|
|||
|
||||
describe '.system_usage_data_monthly' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let!(:ud) { build(:usage_data) }
|
||||
|
||||
before do
|
||||
project = create(:project)
|
||||
env = create(:environment)
|
||||
create(:package, project: project, created_at: 3.days.ago)
|
||||
create(:package, created_at: 2.months.ago, project: project)
|
||||
|
||||
[3, 31].each do |n|
|
||||
deployment_options = { created_at: n.days.ago, project: env.project, environment: env }
|
||||
create(:deployment, :failed, deployment_options)
|
||||
create(:deployment, :success, deployment_options)
|
||||
create(:project_snippet, project: project, created_at: n.days.ago)
|
||||
create(:personal_snippet, created_at: n.days.ago)
|
||||
end
|
||||
|
||||
stub_application_setting(self_monitoring_project: project)
|
||||
|
||||
for_defined_days_back do
|
||||
|
@ -609,10 +621,10 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
|
|||
expect(counts_monthly[:deployments]).to eq(2)
|
||||
expect(counts_monthly[:successful_deployments]).to eq(1)
|
||||
expect(counts_monthly[:failed_deployments]).to eq(1)
|
||||
expect(counts_monthly[:snippets]).to eq(3)
|
||||
expect(counts_monthly[:snippets]).to eq(2)
|
||||
expect(counts_monthly[:personal_snippets]).to eq(1)
|
||||
expect(counts_monthly[:project_snippets]).to eq(2)
|
||||
expect(counts_monthly[:packages]).to eq(3)
|
||||
expect(counts_monthly[:project_snippets]).to eq(1)
|
||||
expect(counts_monthly[:packages]).to eq(1)
|
||||
expect(counts_monthly[:promoted_issues]).to eq(1)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -71,14 +71,15 @@ RSpec.describe 'Query.issue(id)' do
|
|||
end
|
||||
|
||||
context 'selecting multiple fields' do
|
||||
let(:issue_fields) { %w(title description) }
|
||||
let(:issue_fields) { ['title', 'description', 'updatedBy { username }'] }
|
||||
|
||||
it 'returns the Issue with the specified fields' do
|
||||
post_graphql(query, current_user: current_user)
|
||||
|
||||
expect(issue_data.keys).to eq( %w(title description) )
|
||||
expect(issue_data.keys).to eq( %w(title description updatedBy) )
|
||||
expect(issue_data['title']).to eq(issue.title)
|
||||
expect(issue_data['description']).to eq(issue.description)
|
||||
expect(issue_data['updatedBy']['username']).to eq(issue.author.username)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ RSpec.describe 'Robots.txt Requests', :aggregate_failures do
|
|||
it 'blocks the requests' do
|
||||
requests = [
|
||||
'/autocomplete/users',
|
||||
'/autocomplete/projects',
|
||||
'/search',
|
||||
'/admin',
|
||||
'/profile',
|
||||
|
|
|
@ -581,5 +581,40 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when downstream pipeline has workflow rule' do
|
||||
before do
|
||||
stub_ci_pipeline_yaml_file(config)
|
||||
end
|
||||
|
||||
let(:config) do
|
||||
<<-EOY
|
||||
workflow:
|
||||
rules:
|
||||
- if: $my_var
|
||||
|
||||
regular-job:
|
||||
script: 'echo Hello, World!'
|
||||
EOY
|
||||
end
|
||||
|
||||
context 'when passing the required variable' do
|
||||
before do
|
||||
bridge.yaml_variables = [{ key: 'my_var', value: 'var', public: true }]
|
||||
end
|
||||
|
||||
it 'creates the pipeline' do
|
||||
expect { service.execute(bridge) }.to change(downstream_project.ci_pipelines, :count).by(1)
|
||||
|
||||
expect(bridge.reload).to be_success
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not passing the required variable' do
|
||||
it 'does not create the pipeline' do
|
||||
expect { service.execute(bridge) }.not_to change(downstream_project.ci_pipelines, :count)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -41,7 +41,9 @@ RSpec.describe Ci::CreatePipelineService do
|
|||
save_on_errors: save_on_errors,
|
||||
trigger_request: trigger_request,
|
||||
merge_request: merge_request,
|
||||
external_pull_request: external_pull_request)
|
||||
external_pull_request: external_pull_request) do |pipeline|
|
||||
yield(pipeline) if block_given?
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/ParameterLists
|
||||
|
||||
|
@ -2274,6 +2276,207 @@ RSpec.describe Ci::CreatePipelineService do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with workflow rules with persisted variables' do
|
||||
let(:config) do
|
||||
<<-EOY
|
||||
workflow:
|
||||
rules:
|
||||
- if: $CI_COMMIT_REF_NAME == "master"
|
||||
|
||||
regular-job:
|
||||
script: 'echo Hello, World!'
|
||||
EOY
|
||||
end
|
||||
|
||||
context 'with matches' do
|
||||
it 'creates a pipeline' do
|
||||
expect(pipeline).to be_persisted
|
||||
expect(build_names).to contain_exactly('regular-job')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with no matches' do
|
||||
let(:ref_name) { 'refs/heads/feature' }
|
||||
|
||||
it 'does not create a pipeline' do
|
||||
expect(pipeline).not_to be_persisted
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with workflow rules with pipeline variables' do
|
||||
let(:pipeline) do
|
||||
execute_service(variables_attributes: variables_attributes)
|
||||
end
|
||||
|
||||
let(:config) do
|
||||
<<-EOY
|
||||
workflow:
|
||||
rules:
|
||||
- if: $SOME_VARIABLE
|
||||
|
||||
regular-job:
|
||||
script: 'echo Hello, World!'
|
||||
EOY
|
||||
end
|
||||
|
||||
context 'with matches' do
|
||||
let(:variables_attributes) do
|
||||
[{ key: 'SOME_VARIABLE', secret_value: 'SOME_VAR' }]
|
||||
end
|
||||
|
||||
it 'creates a pipeline' do
|
||||
expect(pipeline).to be_persisted
|
||||
expect(build_names).to contain_exactly('regular-job')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with no matches' do
|
||||
let(:variables_attributes) { {} }
|
||||
|
||||
it 'does not create a pipeline' do
|
||||
expect(pipeline).not_to be_persisted
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with workflow rules with trigger variables' do
|
||||
let(:pipeline) do
|
||||
execute_service do |pipeline|
|
||||
pipeline.variables.build(variables)
|
||||
end
|
||||
end
|
||||
|
||||
let(:config) do
|
||||
<<-EOY
|
||||
workflow:
|
||||
rules:
|
||||
- if: $SOME_VARIABLE
|
||||
|
||||
regular-job:
|
||||
script: 'echo Hello, World!'
|
||||
EOY
|
||||
end
|
||||
|
||||
context 'with matches' do
|
||||
let(:variables) do
|
||||
[{ key: 'SOME_VARIABLE', secret_value: 'SOME_VAR' }]
|
||||
end
|
||||
|
||||
it 'creates a pipeline' do
|
||||
expect(pipeline).to be_persisted
|
||||
expect(build_names).to contain_exactly('regular-job')
|
||||
end
|
||||
|
||||
context 'when FF ci_seed_block_run_before_workflow_rules is disabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_seed_block_run_before_workflow_rules: false)
|
||||
end
|
||||
|
||||
it 'does not a pipeline' do
|
||||
expect(pipeline).not_to be_persisted
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a job requires the same variable' do
|
||||
let(:config) do
|
||||
<<-EOY
|
||||
workflow:
|
||||
rules:
|
||||
- if: $SOME_VARIABLE
|
||||
|
||||
build:
|
||||
stage: build
|
||||
script: 'echo build'
|
||||
rules:
|
||||
- if: $SOME_VARIABLE
|
||||
|
||||
test1:
|
||||
stage: test
|
||||
script: 'echo test1'
|
||||
needs: [build]
|
||||
|
||||
test2:
|
||||
stage: test
|
||||
script: 'echo test2'
|
||||
EOY
|
||||
end
|
||||
|
||||
it 'creates a pipeline' do
|
||||
expect(pipeline).to be_persisted
|
||||
expect(build_names).to contain_exactly('build', 'test1', 'test2')
|
||||
end
|
||||
|
||||
context 'when FF ci_seed_block_run_before_workflow_rules is disabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_seed_block_run_before_workflow_rules: false)
|
||||
end
|
||||
|
||||
it 'does not a pipeline' do
|
||||
expect(pipeline).not_to be_persisted
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with no matches' do
|
||||
let(:variables) { {} }
|
||||
|
||||
it 'does not create a pipeline' do
|
||||
expect(pipeline).not_to be_persisted
|
||||
end
|
||||
|
||||
context 'when FF ci_seed_block_run_before_workflow_rules is disabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_seed_block_run_before_workflow_rules: false)
|
||||
end
|
||||
|
||||
it 'does not create a pipeline' do
|
||||
expect(pipeline).not_to be_persisted
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a job requires the same variable' do
|
||||
let(:config) do
|
||||
<<-EOY
|
||||
workflow:
|
||||
rules:
|
||||
- if: $SOME_VARIABLE
|
||||
|
||||
build:
|
||||
stage: build
|
||||
script: 'echo build'
|
||||
rules:
|
||||
- if: $SOME_VARIABLE
|
||||
|
||||
test1:
|
||||
stage: test
|
||||
script: 'echo test1'
|
||||
needs: [build]
|
||||
|
||||
test2:
|
||||
stage: test
|
||||
script: 'echo test2'
|
||||
EOY
|
||||
end
|
||||
|
||||
it 'does not create a pipeline' do
|
||||
expect(pipeline).not_to be_persisted
|
||||
end
|
||||
|
||||
context 'when FF ci_seed_block_run_before_workflow_rules is disabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_seed_block_run_before_workflow_rules: false)
|
||||
end
|
||||
|
||||
it 'does not create a pipeline' do
|
||||
expect(pipeline).not_to be_persisted
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
16
yarn.lock
16
yarn.lock
|
@ -861,15 +861,15 @@
|
|||
eslint-plugin-vue "^6.2.1"
|
||||
vue-eslint-parser "^7.0.0"
|
||||
|
||||
"@gitlab/svgs@1.173.0":
|
||||
version "1.173.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.173.0.tgz#4030cdbb8efb7386941169fc9cefda1bae75b282"
|
||||
integrity sha512-EmfoLgh0Jnz7i28zHROnwvpOcdPaavAXJEpSg8JfbYw9KcWlJyr4Zm5V6h38dNU5AcUFG1/qjUtFbfgmyBx4gA==
|
||||
"@gitlab/svgs@1.174.0":
|
||||
version "1.174.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.174.0.tgz#954b4d908a6188a2fcc45f00f748beeb23f054b0"
|
||||
integrity sha512-CgnZvO2miZkWxANhFdaK+2S4qRgkrMRE3vh3Xxwc+hIV9ki9KavlAAez9MNIs0Um/SJ1UpfmqKoM/dMyZX7K/w==
|
||||
|
||||
"@gitlab/ui@21.35.2":
|
||||
version "21.35.2"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-21.35.2.tgz#2409b3f2979f470b4072e42e32393f024a70630e"
|
||||
integrity sha512-2/kdx7wXVC59UJjk2tC+HViLhDGVm0Jj/FeIQLHeJYVn0R2ZaEYAkevjyXRN33TuOcRHnInQjVXwSclCdQ7K5A==
|
||||
"@gitlab/ui@21.38.0":
|
||||
version "21.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-21.38.0.tgz#ff67908cdb4cb5efb84138842a181640d75b1524"
|
||||
integrity sha512-T1Bvts25s+OS+v5/z6rL24whjGjO8b4KOBZy1+bp3UcQFLC/+gwJAimtiCsILPojlf7WJAPDK1KqJiDciy8gWg==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.0.0"
|
||||
"@gitlab/vue-toasted" "^1.3.0"
|
||||
|
|
Loading…
Reference in New Issue