Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-05-05 12:10:33 +00:00
parent 1c568d834d
commit 023e050d82
91 changed files with 919 additions and 327 deletions

View File

@ -2,11 +2,15 @@
require 'gitlab-dangerfiles'
Gitlab::Dangerfiles.import_plugins(danger)
danger.import_plugin('danger/plugins/*.rb')
gitlab_dangerfiles = Gitlab::Dangerfiles::Engine.new(self)
gitlab_dangerfiles.import_plugins
return if helper.release_automation?
danger.import_plugin('danger/plugins/*.rb')
gitlab_dangerfiles.import_dangerfiles
project_helper.rule_names.each do |rule|
danger.import_dangerfile(path: File.join('danger', rule))
end

View File

@ -403,7 +403,7 @@ group :development, :test do
end
group :development, :test, :danger do
gem 'gitlab-dangerfiles', '~> 1.1.1', require: false
gem 'gitlab-dangerfiles', '~> 2.0.0', require: false
end
group :development, :test, :coverage do

View File

@ -447,7 +447,7 @@ GEM
terminal-table (~> 1.5, >= 1.5.1)
gitlab-chronic (0.10.5)
numerizer (~> 0.2)
gitlab-dangerfiles (1.1.1)
gitlab-dangerfiles (2.0.0)
danger-gitlab
gitlab-experiment (0.5.3)
activesupport (>= 3.0)
@ -1449,7 +1449,7 @@ DEPENDENCIES
gitaly (~> 13.11.0.pre.rc1)
github-markup (~> 1.7.0)
gitlab-chronic (~> 0.10.5)
gitlab-dangerfiles (~> 1.1.1)
gitlab-dangerfiles (~> 2.0.0)
gitlab-experiment (~> 0.5.3)
gitlab-fog-azure-rm (~> 1.0.1)
gitlab-fog-google (~> 1.13)

View File

@ -282,10 +282,12 @@ export default {
</template>
<template #default>
<gl-datepicker
v-if="!isLoading"
ref="datePicker"
class="gl-relative"
:default-date="parsedDate"
show-clear-button
autocomplete="off"
@input="setDate"
@clear="setDate(null)"
/>

View File

@ -13,14 +13,6 @@ module Analytics
end
end
def project_analytics_navbar_links(project, current_user)
[
cycle_analytics_navbar_link(project, current_user),
repository_analytics_navbar_link(project, current_user),
ci_cd_analytics_navbar_link(project, current_user)
].compact
end
def group_analytics_navbar_links(group, current_user)
[]
end
@ -30,39 +22,6 @@ module Analytics
def navbar_sub_item(args)
NavbarSubItem.new(**args)
end
def cycle_analytics_navbar_link(project, current_user)
return unless project_nav_tab?(:cycle_analytics)
navbar_sub_item(
title: _('Value Stream'),
path: 'cycle_analytics#show',
link: project_cycle_analytics_path(project),
link_to_options: { class: 'shortcuts-project-cycle-analytics' }
)
end
def repository_analytics_navbar_link(project, current_user)
return if project.empty_repo?
navbar_sub_item(
title: _('Repository'),
path: 'graphs#charts',
link: charts_project_graph_path(project, current_ref),
link_to_options: { class: 'shortcuts-repository-charts' }
)
end
def ci_cd_analytics_navbar_link(project, current_user)
return unless project_nav_tab?(:pipelines)
return unless project.feature_available?(:builds, current_user) || !project.empty_repo?
navbar_sub_item(
title: _('CI/CD'),
path: 'pipelines#charts',
link: charts_project_pipelines_path(project)
)
end
end
end

View File

@ -83,5 +83,4 @@ module AppearancesHelper
end
end
AppearancesHelper.prepend_if_ee('EE::AppearancesHelper')
AppearancesHelper.prepend_if_jh('JH::AppearancesHelper')
AppearancesHelper.prepend_mod

View File

@ -409,5 +409,4 @@ module ApplicationHelper
end
end
ApplicationHelper.prepend_if_ee('EE::ApplicationHelper')
ApplicationHelper.prepend_if_jh('JH::ApplicationHelper')
ApplicationHelper.prepend_mod

View File

@ -10,6 +10,7 @@ module Ci
Gitlab::Ci::Pipeline::Chain::Build::Associations,
Gitlab::Ci::Pipeline::Chain::Validate::Abilities,
Gitlab::Ci::Pipeline::Chain::Validate::Repository,
Gitlab::Ci::Pipeline::Chain::Validate::SecurityOrchestrationPolicy,
Gitlab::Ci::Pipeline::Chain::Config::Content,
Gitlab::Ci::Pipeline::Chain::Config::Process,
Gitlab::Ci::Pipeline::Chain::RemoveUnwantedChatJobs,

View File

@ -28,6 +28,13 @@ module Ci
def create_pipeline!
build_pipeline.tap do |pipeline|
pipeline.stages << terminal_stage_seed(pipeline).to_resource
if Feature.enabled?(:ci_pipeline_ensure_iid_on_save, pipeline.project, default_enabled: :yaml)
# Project iid must be called outside a transaction, so we ensure it is set here
# otherwise it may be set within the save! which it will lock the InternalId row for the whole transaction
pipeline.ensure_project_iid!
end
pipeline.save!
Ci::ProcessPipelineService

View File

@ -1,6 +1,3 @@
- if project_nav_tab? :analytics
= render 'layouts/nav/sidebar/analytics_links', links: project_analytics_navbar_links(@project, current_user)
- if project_nav_tab?(:confluence)
- confluence_url = project_wikis_confluence_path(@project)
= nav_link do

View File

@ -0,0 +1,5 @@
---
title: Implement variables for pipeline workflow rules
merge_request: 59970
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Disable autocomplete for due date in issue sidebar to prevent triggering updates on Chrome
merge_request: 60973
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Include other cols in index for pending builds
merge_request: 60997
author:
type: performance

View File

@ -0,0 +1,5 @@
---
title: Fix changelog Dangerfile to convert MR IID to a string before comparison
merge_request: 60899
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Added type field into Issues API
merge_request: 59648
author: Raimund Hook @stingrayza
type: added

View File

@ -0,0 +1,8 @@
---
name: ci_pipeline_ensure_iid_on_save
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59341
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/327662
milestone: '13.12'
type: development
group: group::code review
default_enabled: false

View File

@ -0,0 +1,8 @@
---
name: ci_pipeline_ensure_iid_on_skip
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59342
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/327661
milestone: '13.12'
type: development
group: group::code review
default_enabled: false

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/300997
milestone: '13.11'
type: development
group: group::pipeline authoring
default_enabled: false
default_enabled: true

View File

@ -0,0 +1,8 @@
---
name: escalation_policies_mvc
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60524
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/329347
milestone: '13.12'
type: development
group: group::monitor
default_enabled: false

View File

@ -3,42 +3,50 @@
require 'active_support/inflector'
module InjectEnterpriseEditionModule
def prepend_if_ee(constant, with_descendants: false)
return unless Gitlab.ee?
prepend_module(constant.constantize, with_descendants)
def prepend_if_ee(constant_with_prefix, namespace: Object, with_descendants: false)
prepend_mod_for(
constant_without_prefix(constant_with_prefix),
namespace: namespace,
with_descendants: with_descendants)
end
def extend_if_ee(constant)
extend(constant.constantize) if Gitlab.ee?
def extend_if_ee(constant_with_prefix, namespace: Object)
each_extension_for(
constant_without_prefix(constant_with_prefix),
namespace,
&method(:extend))
end
def include_if_ee(constant)
include(constant.constantize) if Gitlab.ee?
def include_if_ee(constant_with_prefix, namespace: Object)
each_extension_for(
constant_without_prefix(constant_with_prefix),
namespace,
&method(:include))
end
def prepend_ee_mod(with_descendants: false)
return unless Gitlab.ee?
prepend_module(ee_module, with_descendants)
def prepend_mod(with_descendants: false)
prepend_mod_for(name, with_descendants: with_descendants)
end
alias_method :prepend_ee_mod, :prepend_mod
def extend_ee_mod
extend(ee_module) if Gitlab.ee?
def extend_mod
each_extension_for(name, Object, &method(:extend))
end
alias_method :extend_ee_mod, :extend_mod
def include_ee_mod
include(ee_module) if Gitlab.ee?
end
def prepend_if_jh(constant, with_descendants: false)
return unless Gitlab.jh?
prepend_module(constant.constantize, with_descendants)
def include_mod
each_extension_for(name, Object, &method(:include))
end
alias_method :include_ee_mod, :include_mod
private
def prepend_mod_for(constant_name, namespace: Object, with_descendants: false)
each_extension_for(constant_name, namespace) do |constant|
prepend_module(constant, with_descendants)
end
end
def prepend_module(mod, with_descendants)
prepend(mod)
@ -47,8 +55,34 @@ module InjectEnterpriseEditionModule
end
end
def ee_module
::EE.const_get(name, false)
def each_extension_for(constant_name, namespace)
Gitlab.extensions.each do |extension_name|
extension_namespace =
const_get_maybe_false(namespace, extension_name.upcase)
extension_module =
const_get_maybe_false(extension_namespace, constant_name)
yield(extension_module) if extension_module
end
end
def const_get_maybe_false(mod, name)
# We're still heavily relying on Rails autoloading instead of zeitwerk,
# therefore this check: `mod.const_defined?(name, false)`
# Is not reliable, which may return false while it's defined.
# After we moved everything over to zeitwerk we can avoid rescuing
# NameError and just check if const_defined?
# mod && mod.const_defined?(name, false) && mod.const_get(name, false)
mod && mod.const_get(name, false)
rescue NameError
false
end
def constant_without_prefix(constant_with_prefix)
constant_with_prefix
.delete_prefix('::') # TODO: Some calling sites are passing this prefix
.delete_prefix('EE::')
end
end

View File

@ -38,16 +38,17 @@ end
def check_changelog_yaml(path)
raw_file = File.read(path)
yaml = YAML.safe_load(raw_file)
yaml_merge_request = yaml["merge_request"].to_s
fail "`title` should be set, in #{helper.html_link(path)}! #{SEE_DOC}" if yaml["title"].nil?
fail "`type` should be set, in #{helper.html_link(path)}! #{SEE_DOC}" if yaml["type"].nil?
return if helper.security_mr?
return if helper.mr_iid.to_s.empty?
return if helper.mr_iid.empty?
cherry_pick_against_stable_branch = helper.cherry_pick_mr? && helper.stable_branch?
if yaml["merge_request"].nil?
if yaml_merge_request.empty?
mr_line = raw_file.lines.find_index("merge_request:\n")
if mr_line
@ -55,7 +56,7 @@ def check_changelog_yaml(path)
else
message "Consider setting `merge_request` to #{helper.mr_iid} in #{helper.html_link(path)}. #{SEE_DOC}"
end
elsif yaml["merge_request"] != helper.mr_iid && !cherry_pick_against_stable_branch
elsif yaml_merge_request != helper.mr_iid && !cherry_pick_against_stable_branch
fail "Merge request ID was not set to #{helper.mr_iid}! #{SEE_DOC}"
end
rescue Psych::Exception

View File

@ -1,19 +0,0 @@
# frozen_string_literal: true
# FIXME: git.info_for_file raises the following error
# /usr/local/bundle/gems/git-1.4.0/lib/git/lib.rb:956:in `command': (Danger::DSLError)
# [!] Invalid `Dangerfile` file:
# [!] Invalid `Dangerfile` file: git '--git-dir=/builds/gitlab-org/gitlab/.git' '--work-tree=/builds/gitlab-org/gitlab' cat-file '-t' '' 2>&1:fatal: Not a valid object name
# This seems to be the same as https://github.com/danger/danger/issues/535.
# locale_files_updated = git.modified_files.select { |path| path.start_with?('locale') }
# locale_files_updated.each do |locale_file_updated|
# git_stats = git.info_for_file(locale_file_updated)
# message "Git stats for #{locale_file_updated}: #{git_stats[:insertions]} insertions, #{git_stats[:deletions]} insertions"
# end
if git.lines_of_code > 2_000
warn "This merge request is definitely too big (#{git.lines_of_code} lines changed), please split it into multiple merge requests."
elsif git.lines_of_code > 500
warn "This merge request is quite big (#{git.lines_of_code} lines changed), please consider splitting it into multiple merge requests."
end

View File

@ -55,7 +55,7 @@ if gitlab.mr_labels.include?('database') || db_paths_to_review.any?
markdown(DB_MESSAGE)
markdown(DB_FILES_MESSAGE + helper.markdown_list(db_paths_to_review)) if db_paths_to_review.any?
unless helper.has_database_scoped_labels?(gitlab.mr_labels)
unless helper.has_database_scoped_labels?
gitlab.api.update_merge_request(gitlab.mr_json['project_id'],
gitlab.mr_json['iid'],
add_labels: 'database::review pending')

View File

@ -13,7 +13,7 @@ group: "%<group>s"
SUGGEST_COMMENT
def check_feature_flag_yaml(feature_flag)
mr_group_label = helper.group_label(gitlab.mr_labels)
mr_group_label = helper.group_label
if feature_flag.group.nil?
message_for_feature_flag_missing_group!(feature_flag: feature_flag, mr_group_label: mr_group_label)

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class CreatePartialCoveringIndexForPendingBuilds < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
EXISTING_INDEX = 'index_ci_builds_runner_id_pending'
NEW_INDEX = 'index_ci_builds_runner_id_pending_covering'
def up
execute "CREATE INDEX CONCURRENTLY #{NEW_INDEX} ON ci_builds (runner_id, id) INCLUDE (project_id) WHERE status = 'pending' AND type = 'Ci::Build'" unless index_exists_by_name?(:ci_builds, NEW_INDEX)
remove_concurrent_index_by_name :ci_builds, EXISTING_INDEX
end
def down
add_concurrent_index :ci_builds, :runner_id, where: "status = 'pending' AND type = 'Ci::Build'", name: EXISTING_INDEX
remove_concurrent_index_by_name :ci_builds, NEW_INDEX
end
end

View File

@ -0,0 +1 @@
ccf7bce753adabfd7dd8a68fd49853514367f11af42879cae1b6aa28e4ebe94c

View File

@ -22389,7 +22389,7 @@ CREATE INDEX index_ci_builds_on_user_id_and_created_at_and_type_eq_ci_build ON c
CREATE INDEX index_ci_builds_project_id_and_status_for_live_jobs_partial2 ON ci_builds USING btree (project_id, status) WHERE (((type)::text = 'Ci::Build'::text) AND ((status)::text = ANY (ARRAY[('running'::character varying)::text, ('pending'::character varying)::text, ('created'::character varying)::text])));
CREATE INDEX index_ci_builds_runner_id_pending ON ci_builds USING btree (runner_id) WHERE (((status)::text = 'pending'::text) AND ((type)::text = 'Ci::Build'::text));
CREATE INDEX index_ci_builds_runner_id_pending_covering ON ci_builds USING btree (runner_id, id) INCLUDE (project_id) WHERE (((status)::text = 'pending'::text) AND ((type)::text = 'Ci::Build'::text));
CREATE INDEX index_ci_builds_runner_id_running ON ci_builds USING btree (runner_id) WHERE (((status)::text = 'running'::text) AND ((type)::text = 'Ci::Build'::text));

View File

@ -127,6 +127,7 @@ Example response:
"avatar_url" : null,
"username" : "root"
},
"type" : "ISSUE",
"updated_at" : "2016-01-04T15:31:51.081Z",
"closed_at" : null,
"closed_by" : null,
@ -331,6 +332,7 @@ Example response:
"id" : 9,
"name" : "Dr. Luella Kovacek"
},
"type" : "ISSUE",
"labels" : ["foo", "bar"],
"upvotes": 4,
"downvotes": 0,
@ -531,6 +533,7 @@ Example response:
"id" : 9,
"name" : "Dr. Luella Kovacek"
},
"type" : "ISSUE",
"labels" : ["foo", "bar"],
"upvotes": 4,
"downvotes": 0,
@ -699,6 +702,7 @@ Example response:
"id": 9,
"name": "Dr. Luella Kovacek"
},
"type": "ISSUE",
"labels": [],
"upvotes": 4,
"downvotes": 0,
@ -863,6 +867,7 @@ Example response:
"id" : 9,
"name" : "Dr. Luella Kovacek"
},
"type" : "ISSUE",
"labels" : [],
"upvotes": 4,
"downvotes": 0,
@ -1002,6 +1007,7 @@ Example response:
"state" : "opened",
"assignees" : [],
"assignee" : null,
"type" : "ISSUE",
"labels" : [
"bug"
],
@ -1375,6 +1381,7 @@ Example response:
"avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon",
"web_url": "https://gitlab.example.com/axel.block"
},
"type" : "ISSUE",
"author": {
"name": "Kris Steuber",
"username": "solon.cremin",
@ -1518,6 +1525,7 @@ Example response:
"avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon",
"web_url": "https://gitlab.example.com/axel.block"
},
"type" : "ISSUE",
"author": {
"name": "Kris Steuber",
"username": "solon.cremin",
@ -1651,6 +1659,7 @@ Example response:
"avatar_url": "http://www.gravatar.com/avatar/3e6f06a86cf27fa8b56f3f74f7615987?s=80&d=identicon",
"web_url": "https://gitlab.example.com/keyon"
},
"type" : "ISSUE",
"closed_at": null,
"closed_by": null,
"author": {
@ -1756,6 +1765,7 @@ Example response:
"avatar_url": "http://www.gravatar.com/avatar/a7fa515d53450023c83d62986d0658a8?s=80&d=identicon",
"web_url": "https://gitlab.example.com/francisca"
},
"type" : "ISSUE",
"author": {
"name": "Maxie Medhurst",
"username": "craig_rutherford",

View File

@ -226,13 +226,15 @@ If your rules match both branch pipelines and merge request pipelines,
#### `workflow:rules:variables`
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/294232) in GitLab 13.11.
> - 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-workflowrulesvariables). **(CORE ONLY)**
> - [Deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/300997) in GitLab 13.12.
> - Enabled on GitLab.com.
> - Recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-workflowrulesvariables). **(FREE SELF)**
WARNING:
This feature might not be available to you. Check the **version history** note above for details.
There can be
[risks when disabling released features](../../user/feature_flags.md#risks-when-disabling-released-features).
Refer to this feature's version history for more details.
You can use [`variables`](#variables) in `workflow:rules:` to define variables for specific pipeline conditions.
@ -285,12 +287,12 @@ When the branch is something else:
- job1's `DEPLOY_VARIABLE` is `job1-default-deploy`.
- job2's `DEPLOY_VARIABLE` is `default-deploy`.
##### Enable or disable workflow:rules:variables **(CORE ONLY)**
##### Enable or disable workflow:rules:variables **(FREE SELF)**
rules:variables is under development and not ready for production use.
It is deployed behind a feature flag that is **disabled by default**.
workflow:rules:variables is under development but ready for production use.
It is deployed behind a feature flag that is **enabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
can enable it.
can opt to disable it.
To enable it:

View File

@ -0,0 +1,80 @@
---
stage: none
group: unassigned
info: https://gitlab.com/gitlab-jh/gitlab
---
# Guidelines for reviewing JiHu (JH) Edition related merge requests
We have two kinds of changes related to JH:
- Inside `jh/`
- This is beyond EE repository and not the intention for this documentation.
- Outside `jh/`
- These will have to sit in EE repository, so reviewers and maintainers for
EE repository will have to review and maintain. This includes codes like
`Gitlab.jh?`, and how it attempts to load codes under `jh/` just like we
have codes which will load codes under `ee/`.
- This documentation intended to guide how those codes should look like, so
we'll have better understanding what are the changes needed for looking up
codes under `jh/`.
- We will generalize this so both EE and JH can share the same mechanism,
then we wouldn't have to treat them differently.
If needed, review the corresponding JH merge request located at [JH repository](https://gitlab.com/gitlab-jh/gitlab)
## Act as EE when `jh/` does not exist
- In the case of EE repository, `jh/` does not exist so it should just act like EE (or CE when the license is absent)
- In the case of JH repository, `jh/` does exist but `EE_ONLY` environment variable can be set to force it run under EE mode.
- In the case of JH repository, `jh/` does exist but `FOSS_ONLY` environment variable can be set to force it run under CE mode.
## CI pipelines in a JH context
EE repository does not have `jh/` directory therefore there is no way to run
JH pipelines in the EE repository. All JH tests should go to [JH repository](https://gitlab.com/gitlab-jh/gitlab).
The top-level JH CI configuration is located at `jh/.gitlab-ci.yml` (which
does not exist in EE repository) and it'll include EE CI configurations
accordingly. Sometimes it's needed to update the EE CI configurations for JH
to customize more easily.
### JH features based on CE or EE features
For features that build on existing CE/EE features, a module in the `JH`
namespace injected in the CE/EE class/module is needed. This aligns with
what we're doing with EE features.
See [EE features based on CE features](ee_features.md#ee-features-based-on-ce-features) for more details.
For example, to prepend a module into the `User` class you would use
the following approach:
```ruby
class User < ActiveRecord::Base
# ... lots of code here ...
end
User.prepend_mod
```
Under EE, `User.prepend_mod` will attempt to:
- Load EE module
Under JH, `User.prepend_mod` will attempt to:
- Load EE module, and:
- Load JH module
Do not use methods such as `prepend`, `extend`, and `include`. Instead, use
`prepend_mod`, `extend_mod`, or `include_mod`. These methods will try to find
the relevant EE and JH modules by the name of the receiver module.
If reviewing the corresponding JH file is needed, it should be found at
[JH repository](https://gitlab.com/gitlab-jh/gitlab).
### General guidance for writing JH extensions
See [Guidelines for implementing Enterprise Edition features](ee_features.md)
for general guidance.

View File

@ -122,21 +122,16 @@ older format is still supported, allowing existing aliases or contacts to contin
To link directly to the new issue page with prefilled fields, use query
string parameters in a URL. You can embed a URL in an external
HTML page, or create issues with certain
HTML page to create issues with certain
fields prefilled.
The title, description, description template, and confidential fields can be prefilled
using this method. You cannot pre-fill both the description and description template
fields in the same URL because a description template also populates the description
field.
| Field | URL Parameter Name | Notes |
|----------------------|-----------------------|-------------------------------------------------------|
| title | `issue[title]` | |
| description | `issue[description]` | |
| description template | `issuable_template` | |
| issue type | `issue[issue_type]` | Either `incident` or `issue` |
| confidential | `issue[confidential]` | Parameter value must be `true` to set to confidential |
| description | `issue[description]` | Cannot be used at the same time as `issuable_template`. |
| description template | `issuable_template` | Cannot be used at the same time as `issue[description]`. |
| issue type | `issue[issue_type]` | Either `incident` or `issue`. |
| confidential | `issue[confidential]` | Parameter value must be `true` to set to confidential. |
Follow these examples to form your new issue URL with prefilled fields.

View File

@ -3,6 +3,10 @@
module API
module Entities
class IssueBasic < IssuableEntity
format_with(:upcase) do |item|
item.upcase if item.respond_to?(:upcase)
end
expose :closed_at
expose :closed_by, using: Entities::UserBasic
@ -16,6 +20,10 @@ module API
expose :milestone, using: Entities::Milestone
expose :assignees, :author, using: Entities::UserBasic
expose :issue_type,
as: :type,
format_with: :upcase,
documentation: { type: "String", desc: "One of #{Issue.issue_types.keys.map(&:upcase)}" }
expose :assignee, using: ::API::Entities::UserBasic do |issue|
issue.assignees.first

View File

@ -92,6 +92,16 @@ module Gitlab
Rails.env.development? || Rails.env.test?
end
def self.extensions
if jh?
%w[ee jh]
elsif ee?
%w[ee]
else
%w[]
end
end
def self.ee?
@is_ee ||=
# We use this method when the Rails environment is not loaded. This

View File

@ -11,7 +11,16 @@ module Gitlab
def perform!
if skipped?
@pipeline.skip if @command.save_incompleted
if @command.save_incompleted
if Feature.enabled?(:ci_pipeline_ensure_iid_on_skip, @pipeline.project, default_enabled: :yaml)
# Project iid must be called outside a transaction, so we ensure it is set here
# otherwise it may be set within the state transition transaction of the skip call
# which it will lock the InternalId row for the whole transaction
@pipeline.ensure_project_iid!
end
@pipeline.skip
end
end
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
module Gitlab
module Ci
module Pipeline
module Chain
module Validate
class SecurityOrchestrationPolicy < Chain::Base
include Chain::Helpers
def perform!
# no-op
end
def break?
false
end
end
end
end
end
end
end
Gitlab::Ci::Pipeline::Chain::Validate::SecurityOrchestrationPolicy.prepend_if_ee('EE::Gitlab::Ci::Pipeline::Chain::Validate::SecurityOrchestrationPolicy')

View File

@ -99,11 +99,19 @@ module Gitlab
def group_config_file
Rails.root.join('lib/gitlab/import_export/group/import_export.yml')
end
def group_wiki_repo_bundle_filename(group_id)
"#{group_id}.wiki.bundle"
end
def group_wiki_repo_bundle_path(shared, filename)
File.join(shared.export_path, 'repositories', filename)
end
def group_wiki_repo_bundle_full_path(shared, group_id)
group_wiki_repo_bundle_path(shared, group_wiki_repo_bundle_filename(group_id))
end
end
end
Gitlab::ImportExport.prepend_if_ee('EE::Gitlab::ImportExport')
# The methods in `Gitlab::ImportExport::GroupHelper` should be available as both
# instance and class methods.
Gitlab::ImportExport.extend_if_ee('Gitlab::ImportExport::GroupHelper')
Gitlab::ImportExport.prepend_mod

View File

@ -12,5 +12,5 @@ module Gitlab
end
end
Gitlab::SubscriptionPortal.prepend_if_jh('JH::Gitlab::SubscriptionPortal')
Gitlab::SubscriptionPortal.prepend_mod
Gitlab::SubscriptionPortal::SUBSCRIPTIONS_URL = Gitlab::SubscriptionPortal.subscriptions_url.freeze

View File

@ -0,0 +1,89 @@
# frozen_string_literal: true
module Sidebars
module Projects
module Menus
class AnalyticsMenu < ::Sidebars::Menu
include Gitlab::Utils::StrongMemoize
override :configure_menu_items
def configure_menu_items
return false unless can?(context.current_user, :read_analytics, context.project)
add_item(ci_cd_analytics_menu_item)
add_item(repository_analytics_menu_item)
add_item(cycle_analytics_menu_item)
true
end
override :link
def link
return cycle_analytics_menu_item.link if cycle_analytics_menu_item
items.first.link
end
override :extra_container_html_options
def extra_container_html_options
{
class: 'shortcuts-analytics'
}
end
override :title
def title
_('Analytics')
end
override :sprite_icon
def sprite_icon
'chart'
end
private
def ci_cd_analytics_menu_item
return if context.project.empty_repo?
return unless context.project.feature_available?(:builds, context.current_user)
return unless can?(context.current_user, :read_build, context.project)
::Sidebars::MenuItem.new(
title: _('CI/CD'),
link: charts_project_pipelines_path(context.project),
active_routes: { path: 'pipelines#charts' },
item_id: :ci_cd_analytics
)
end
def repository_analytics_menu_item
return if context.project.empty_repo?
::Sidebars::MenuItem.new(
title: _('Repository'),
link: charts_project_graph_path(context.project, context.current_ref),
container_html_options: { class: 'shortcuts-repository-charts' },
active_routes: { path: 'graphs#charts' },
item_id: :repository_analytics
)
end
def cycle_analytics_menu_item
strong_memoize(:cycle_analytics_menu_item) do
next unless can?(context.current_user, :read_cycle_analytics, context.project)
::Sidebars::MenuItem.new(
title: _('Value Stream'),
link: project_cycle_analytics_path(context.project),
container_html_options: { class: 'shortcuts-project-cycle-analytics' },
active_routes: { path: 'cycle_analytics#show' },
item_id: :cycle_analytics
)
end
end
end
end
end
end
Sidebars::Projects::Menus::AnalyticsMenu.prepend_if_ee('EE::Sidebars::Projects::Menus::AnalyticsMenu')

View File

@ -18,6 +18,7 @@ module Sidebars
add_menu(Sidebars::Projects::Menus::SecurityComplianceMenu.new(context))
add_menu(Sidebars::Projects::Menus::OperationsMenu.new(context))
add_menu(Sidebars::Projects::Menus::PackagesRegistriesMenu.new(context))
add_menu(Sidebars::Projects::Menus::AnalyticsMenu.new(context))
end
override :render_raw_menus_partial

View File

@ -6689,6 +6689,9 @@ msgstr ""
msgid "CloudLicense|Buy subscription"
msgstr ""
msgid "CloudLicense|Enter activation code"
msgstr ""
msgid "CloudLicense|Free trial"
msgstr ""
@ -6698,9 +6701,6 @@ msgstr ""
msgid "CloudLicense|I agree that my use of the GitLab Software is subject to the Subscription Agreement located at the %{linkStart}Terms of Service%{linkEnd}, unless otherwise agreed to in writing with GitLab."
msgstr ""
msgid "CloudLicense|Learn how to %{linkStart}activate your subscription%{linkEnd}."
msgstr ""
msgid "CloudLicense|Maximum users"
msgstr ""
@ -12971,6 +12971,21 @@ msgstr ""
msgid "Errors:"
msgstr ""
msgid "Escalation Policies"
msgstr ""
msgid "Escalation policies"
msgstr ""
msgid "EscalationPolicies|Add an escalation policy"
msgstr ""
msgid "EscalationPolicies|Create an escalation policy in GitLab"
msgstr ""
msgid "EscalationPolicies|Set up escalation policies to define who is paged, and when, in the event the first users paged don't respond."
msgstr ""
msgid "Estimate"
msgstr ""
@ -16486,6 +16501,9 @@ msgstr ""
msgid "If you want to re-enable two-factor authentication, visit the %{settings_link_to} page."
msgstr ""
msgid "If you've purchased or renewed your subscription and have an activation code, please enter it below to start the activation process."
msgstr ""
msgid "If your HTTP repository is not publicly accessible, add your credentials."
msgstr ""
@ -31070,6 +31088,9 @@ msgstr ""
msgid "SuperSonics|Last Sync"
msgstr ""
msgid "SuperSonics|Learn how to %{linkStart}activate your subscription%{linkEnd}."
msgstr ""
msgid "SuperSonics|Licensed to"
msgstr ""
@ -32267,9 +32288,6 @@ msgstr ""
msgid "There are running deployments on the environment. Please retry later."
msgstr ""
msgid "There is a connectivity issue"
msgstr ""
msgid "There is a halted Elasticsearch migration"
msgstr ""
@ -38715,6 +38733,12 @@ msgstr ""
msgid "satisfied"
msgstr ""
msgid "scan-execution-policy: policy not applied, %{policy_path} file is invalid"
msgstr ""
msgid "scan-execution-policy: policy not applied, %{policy_path} file is missing"
msgstr ""
msgid "security Reports|There was an error creating the merge request"
msgstr ""

View File

@ -107,4 +107,4 @@ module QA
end
end
QA::Page::Admin::Menu.prepend_if_ee('QA::EE::Page::Admin::Menu')
QA::Page::Admin::Menu.prepend_if_ee('Page::Admin::Menu', namespace: QA)

View File

@ -20,4 +20,4 @@ module QA
end
end
QA::Page::Admin::Overview::Groups::Edit.prepend_if_ee('QA::EE::Page::Admin::Overview::Groups::Edit')
QA::Page::Admin::Overview::Groups::Edit.prepend_if_ee('Page::Admin::Overview::Groups::Edit', namespace: QA)

View File

@ -137,4 +137,4 @@ module QA
end
end
QA::Page::Component::IssueBoard::Show.prepend_if_ee('QA::EE::Page::Component::IssueBoard::Show')
QA::Page::Component::IssueBoard::Show.prepend_if_ee('Page::Component::IssueBoard::Show', namespace: QA)

View File

@ -45,4 +45,4 @@ module QA
end
end
QA::Page::Dashboard::Projects.prepend_if_ee('QA::EE::Page::Dashboard::Projects')
QA::Page::Dashboard::Projects.prepend_if_ee('Page::Dashboard::Projects', namespace: QA)

View File

@ -62,4 +62,4 @@ module QA
end
end
QA::Page::File::Show.prepend_if_ee('QA::EE::Page::File::Show')
QA::Page::File::Show.prepend_if_ee('Page::File::Show', namespace: QA)

View File

@ -83,4 +83,4 @@ module QA
end
end
QA::Page::Group::Menu.prepend_if_ee('QA::EE::Page::Group::Menu')
QA::Page::Group::Menu.prepend_if_ee('Page::Group::Menu', namespace: QA)

View File

@ -129,4 +129,4 @@ module QA
end
end
QA::Page::Group::Settings::General.prepend_if_ee('QA::EE::Page::Group::Settings::General')
QA::Page::Group::Settings::General.prepend_if_ee('Page::Group::Settings::General', namespace: QA)

View File

@ -175,4 +175,4 @@ module QA
end
end
QA::Page::Main::Menu.prepend_if_ee('QA::EE::Page::Main::Menu')
QA::Page::Main::Menu.prepend_if_ee('Page::Main::Menu', namespace: QA)

View File

@ -41,4 +41,4 @@ module QA
end
end
QA::Page::MergeRequest::New.prepend_if_ee('QA::EE::Page::MergeRequest::New')
QA::Page::MergeRequest::New.prepend_if_ee('Page::MergeRequest::New', namespace: QA)

View File

@ -390,4 +390,4 @@ module QA
end
end
QA::Page::MergeRequest::Show.prepend_if_ee('QA::EE::Page::MergeRequest::Show')
QA::Page::MergeRequest::Show.prepend_if_ee('Page::MergeRequest::Show', namespace: QA)

View File

@ -30,4 +30,4 @@ module QA
end
end
QA::Page::Milestone::Show.prepend_if_ee('QA::EE::Page::Milestone::Show')
QA::Page::Milestone::Show.prepend_if_ee('Page::Milestone::Show', namespace: QA)

View File

@ -56,4 +56,4 @@ module QA
end
end
QA::Page::Profile::Menu.prepend_if_ee('QA::EE::Page::Profile::Menu')
QA::Page::Profile::Menu.prepend_if_ee('Page::Profile::Menu', namespace: QA)

View File

@ -85,4 +85,4 @@ module QA
end
end
QA::Page::Project::Issue::Index.prepend_if_ee('QA::EE::Page::Project::Issue::Index')
QA::Page::Project::Issue::Index.prepend_if_ee('Page::Project::Issue::Index', namespace: QA)

View File

@ -70,4 +70,4 @@ module QA
end
end
QA::Page::Project::Issue::Show.prepend_if_ee('QA::EE::Page::Project::Issue::Show')
QA::Page::Project::Issue::Show.prepend_if_ee('Page::Project::Issue::Show', namespace: QA)

View File

@ -75,4 +75,4 @@ module QA
end
end
QA::Page::Project::Job::Show.prepend_if_ee('QA::EE::Page::Project::Job::Show')
QA::Page::Project::Job::Show.prepend_if_ee('Page::Project::Job::Show', namespace: QA)

View File

@ -56,4 +56,4 @@ module QA
end
end
QA::Page::Project::Menu.prepend_if_ee('QA::EE::Page::Project::Menu')
QA::Page::Project::Menu.prepend_if_ee('Page::Project::Menu', namespace: QA)

View File

@ -72,4 +72,4 @@ module QA
end
end
QA::Page::Project::New.prepend_if_ee('QA::EE::Page::Project::New')
QA::Page::Project::New.prepend_if_ee('Page::Project::New', namespace: QA)

View File

@ -134,4 +134,4 @@ module QA
end
end
QA::Page::Project::Operations::Metrics::Show.prepend_if_ee('QA::EE::Page::Project::Operations::Metrics::Show')
QA::Page::Project::Operations::Metrics::Show.prepend_if_ee('Page::Project::Operations::Metrics::Show', namespace: QA)

View File

@ -27,4 +27,4 @@ module QA
end
end
QA::Page::Project::Packages::Index.prepend_if_ee('QA::EE::Page::Project::Packages::Index')
QA::Page::Project::Packages::Index.prepend_if_ee('Page::Project::Packages::Index', namespace: QA)

View File

@ -67,4 +67,4 @@ module QA
end
end
QA::Page::Project::Pipeline::Index.prepend_if_ee('QA::EE::Page::Project::Pipeline::Index')
QA::Page::Project::Pipeline::Index.prepend_if_ee('Page::Project::Pipeline::Index', namespace: QA)

View File

@ -117,4 +117,4 @@ module QA
end
end
QA::Page::Project::Pipeline::Show.prepend_if_ee('QA::EE::Page::Project::Pipeline::Show')
QA::Page::Project::Pipeline::Show.prepend_if_ee('Page::Project::Pipeline::Show', namespace: QA)

View File

@ -43,4 +43,4 @@ module QA
end
end
QA::Page::Project::Settings::CICD.prepend_if_ee("QA::EE::Page::Project::Settings::CICD")
QA::Page::Project::Settings::CICD.prepend_if_ee("Page::Project::Settings::CICD", namespace: QA)

View File

@ -23,4 +23,4 @@ module QA
end
end
QA::Page::Project::Settings::Integrations.prepend_if_ee('QA::EE::Page::Project::Settings::Integrations')
QA::Page::Project::Settings::Integrations.prepend_if_ee('Page::Project::Settings::Integrations', namespace: QA)

View File

@ -57,4 +57,4 @@ module QA
end
end
QA::Page::Project::Settings::Main.prepend_if_ee("QA::EE::Page::Project::Settings::Main")
QA::Page::Project::Settings::Main.prepend_if_ee("Page::Project::Settings::Main", namespace: QA)

View File

@ -38,4 +38,4 @@ module QA
end
end
QA::Page::Project::Settings::MergeRequest.prepend_if_ee("QA::EE::Page::Project::Settings::MergeRequest")
QA::Page::Project::Settings::MergeRequest.prepend_if_ee("Page::Project::Settings::MergeRequest", namespace: QA)

View File

@ -129,4 +129,4 @@ module QA
end
end
QA::Page::Project::Settings::MirroringRepositories.prepend_if_ee('QA::EE::Page::Project::Settings::MirroringRepositories')
QA::Page::Project::Settings::MirroringRepositories.prepend_if_ee('Page::Project::Settings::MirroringRepositories', namespace: QA)

View File

@ -69,4 +69,4 @@ module QA
end
end
QA::Page::Project::Settings::ProtectedBranches.prepend_if_ee('QA::EE::Page::Project::Settings::ProtectedBranches')
QA::Page::Project::Settings::ProtectedBranches.prepend_if_ee('Page::Project::Settings::ProtectedBranches', namespace: QA)

View File

@ -43,4 +43,4 @@ module QA
end
end
QA::Page::Project::Settings::ProtectedTags.prepend_if_ee('QA::EE::Page::Project::Settings::ProtectedTags')
QA::Page::Project::Settings::ProtectedTags.prepend_if_ee('Page::Project::Settings::ProtectedTags', namespace: QA)

View File

@ -62,4 +62,4 @@ module QA
end
end
QA::Page::Project::Settings::Repository.prepend_if_ee('QA::EE::Page::Project::Settings::Repository')
QA::Page::Project::Settings::Repository.prepend_if_ee('Page::Project::Settings::Repository', namespace: QA)

View File

@ -180,4 +180,4 @@ module QA
end
end
QA::Page::Project::Show.prepend_if_ee('QA::EE::Page::Project::Show')
QA::Page::Project::Show.prepend_if_ee('Page::Project::Show', namespace: QA)

View File

@ -26,4 +26,4 @@ module QA
end
end
QA::Page::Project::Snippet::Index.prepend_if_ee('QA::EE::Page::Project::Snippet::Index')
QA::Page::Project::Snippet::Index.prepend_if_ee('Page::Project::Snippet::Index', namespace: QA)

View File

@ -311,4 +311,4 @@ module QA
end
end
QA::Page::Project::WebIDE::Edit.prepend_if_ee('QA::EE::Page::Component::WebIDE::WebTerminalPanel')
QA::Page::Project::WebIDE::Edit.prepend_if_ee('Page::Component::WebIDE::WebTerminalPanel', namespace: QA)

View File

@ -14,4 +14,4 @@ module QA
end
end
QA::Page::Project::Wiki::Show.prepend_if_ee('QA::EE::Page::Project::Wiki::Show')
QA::Page::Project::Wiki::Show.prepend_if_ee('Page::Project::Wiki::Show', namespace: QA)

View File

@ -21,4 +21,4 @@ module QA
end
end
QA::Page::Registration::Welcome.prepend_if_ee('QA::EE::Page::Registration::Welcome')
QA::Page::Registration::Welcome.prepend_if_ee('Page::Registration::Welcome', namespace: QA)

View File

@ -403,4 +403,4 @@ module QA
end
end
QA::Runtime::Env.extend_if_ee('QA::EE::Runtime::Env')
QA::Runtime::Env.extend_if_ee('Runtime::Env', namespace: QA)

View File

@ -58,4 +58,4 @@ module QA
end
end
QA::Scenario::Test::Sanity::Selectors.prepend_if_ee('QA::EE::Scenario::Test::Sanity::Selectors')
QA::Scenario::Test::Sanity::Selectors.prepend_if_ee('Scenario::Test::Sanity::Selectors', namespace: QA)

View File

@ -1,96 +0,0 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'rubygems'
require 'fog/aws'
class SyncReports
ACTIONS = %w[get put].freeze
attr_reader :options
def initialize(options)
@options = options
perform_sync!
end
private
def perform_sync!
case options[:action]
when 'get'
get_reports!
when 'put'
put_reports!
end
end
def get_reports!
options[:report_paths].each { |report_path| get_report!(report_path) }
end
def put_reports!
options[:report_paths].each { |report_path| put_report!(report_path) }
end
def get_report!(report_path)
file = bucket.files.get(report_path)
if file.respond_to?(:body)
File.write(report_path, file.body)
puts "#{report_path} was retrieved from S3."
else
puts "#{report_path} does not seem to exist on S3."
end
end
def put_report!(report_path)
bucket.files.create(
key: report_path,
body: File.open(report_path),
public: true
)
puts "#{report_path} was uploaded to S3."
end
def bucket
@bucket ||= storage.directories.get(options[:bucket])
end
def storage
@storage ||=
Fog::Storage.new(
provider: 'AWS',
aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
)
end
end
def usage!(error: 'action')
print "\n[ERROR]: "
case error
when 'action'
puts "Please specify an action as first argument: #{SyncReports::ACTIONS.join(', ')}\n\n"
when 'bucket'
puts "Please specify a bucket as second argument!\n\n"
when 'files'
puts "Please specify one or more file paths as third argument!\n\n"
end
puts "Usage: #{__FILE__} [get|put] bucket report_path ...\n\n"
puts "Note: the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment "\
"variables need to be set\n\n"
exit 1
end
if $0 == __FILE__
action = ARGV.shift
usage!(error: 'action') unless SyncReports::ACTIONS.include?(action)
bucket = ARGV.shift
usage!(error: 'bucket') unless bucket
usage!(error: 'files') unless ARGV.any?
SyncReports.new(action: action, bucket: bucket, report_paths: ARGV)
end

View File

@ -0,0 +1,141 @@
# frozen_string_literal: true
require 'fast_spec_helper'
RSpec.describe InjectEnterpriseEditionModule do
let(:extension_name) { 'FF' }
let(:extension_namespace) { Module.new }
let(:fish_name) { 'Fish' }
let(:fish_class) { Class.new }
let(:fish_extension) { Module.new }
before do
# Make sure we're not relying on which mode we're running under
allow(Gitlab).to receive(:extensions).and_return([extension_name.downcase])
# Test on an imagined extension and imagined class
stub_const(fish_name, fish_class) # Fish
allow(fish_class).to receive(:name).and_return(fish_name)
end
shared_examples 'expand the extension with' do |method|
context 'when extension namespace is set at top-level' do
before do
stub_const(extension_name, extension_namespace) # FF
extension_namespace.const_set(fish_name, fish_extension) # FF::Fish
end
it "calls #{method} with the extension module" do
expect(fish_class).to receive(method).with(fish_extension)
fish_class.__send__("#{method}_if_ee", fish_name)
end
it "ignores EE prefix and calls #{method} with the extension module" do
expect(fish_class).to receive(method).with(fish_extension)
fish_class.__send__("#{method}_if_ee", "EE::#{fish_name}")
end
it "ignores ::EE prefix and calls #{method} with the extension module" do
expect(fish_class).to receive(method).with(fish_extension)
fish_class.__send__("#{method}_if_ee", "::EE::#{fish_name}")
end
end
context 'when extension namespace is set at another namespace' do
let(:another_namespace) { Module.new } # QA
before do
another_namespace.const_set(extension_name, extension_namespace) # QA::FF
extension_namespace.const_set(fish_name, fish_extension) # QA::FF::Fish
end
it "calls #{method} with the extension module from the additional namespace" do
expect(fish_class).to receive(method).with(fish_extension)
fish_class.__send__("#{method}_if_ee", fish_name, namespace: another_namespace)
end
end
context 'when extension namespace exists but not the extension' do
before do
stub_const(extension_name, extension_namespace) # FF
end
it "does not call #{method}" do
expect(fish_class).not_to receive(method).with(fish_extension)
fish_class.__send__("#{method}_if_ee", fish_name)
end
end
context 'when extension namespace does not exist' do
it "does not call #{method}" do
expect(fish_class).not_to receive(method).with(fish_extension)
fish_class.__send__("#{method}_if_ee", fish_name)
end
end
end
shared_examples 'expand the assumed extension with' do |method|
context 'when extension namespace is set at top-level' do
before do
stub_const(extension_name, extension_namespace) # FF
extension_namespace.const_set(fish_name, fish_extension) # FF::Fish
end
it "calls #{method} with the extension module" do
expect(fish_class).to receive(method).with(fish_extension)
fish_class.__send__("#{method}_mod")
end
end
context 'when extension namespace exists but not the extension' do
before do
stub_const(extension_name, extension_namespace) # FF
end
it "does not call #{method}" do
expect(fish_class).not_to receive(method).with(fish_extension)
fish_class.__send__("#{method}_mod")
end
end
context 'when extension namespace does not exist' do
it "does not call #{method}" do
expect(fish_class).not_to receive(method).with(fish_extension)
fish_class.__send__("#{method}_mod")
end
end
end
describe '#prepend_if_ee' do
it_behaves_like 'expand the extension with', :prepend
end
describe '#extend_if_ee' do
it_behaves_like 'expand the extension with', :extend
end
describe '#include_if_ee' do
it_behaves_like 'expand the extension with', :include
end
describe '#prepend_mod' do
it_behaves_like 'expand the assumed extension with', :prepend
end
describe '#extend_mod' do
it_behaves_like 'expand the assumed extension with', :extend
end
describe '#include_mod' do
it_behaves_like 'expand the assumed extension with', :include
end
end

View File

@ -5,17 +5,14 @@ require 'spec_helper'
RSpec.describe 'Issue Sidebar' do
include MobileHelpers
let(:group) { create(:group, :nested) }
let(:project) { create(:project, :public, namespace: group) }
let!(:user) { create(:user) }
let!(:label) { create(:label, project: project, title: 'bug') }
let(:issue) { create(:labeled_issue, project: project, labels: [label]) }
let!(:xss_label) { create(:label, project: project, title: '&lt;script&gt;alert("xss");&lt;&#x2F;script&gt;') }
let!(:milestone_expired) { create(:milestone, project: project, due_date: 5.days.ago) }
let!(:milestone_no_duedate) { create(:milestone, project: project, title: 'Foo - No due date') }
let!(:milestone1) { create(:milestone, project: project, title: 'Milestone-1', due_date: 20.days.from_now) }
let!(:milestone2) { create(:milestone, project: project, title: 'Milestone-2', due_date: 15.days.from_now) }
let!(:milestone3) { create(:milestone, project: project, title: 'Milestone-3', due_date: 10.days.from_now) }
let_it_be(:group) { create(:group, :nested) }
let_it_be(:project) { create(:project, :public, namespace: group) }
let_it_be(:user) { create(:user) }
let_it_be(:label) { create(:label, project: project, title: 'bug') }
let_it_be(:issue) { create(:labeled_issue, project: project, labels: [label]) }
let_it_be(:mock_date) { Date.today.at_beginning_of_month + 2.days }
let_it_be(:issue_with_due_date) { create(:issue, project: project, due_date: mock_date) }
let_it_be(:xss_label) { create(:label, project: project, title: '&lt;script&gt;alert("xss");&lt;&#x2F;script&gt;') }
before do
stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab")
@ -204,7 +201,31 @@ RSpec.describe 'Issue Sidebar' do
end
end
context 'as a allowed user' do
context 'due date widget', :js do
let(:due_date_value) { find('[data-testid="due-date"] [data-testid="sidebar-date-value"]') }
context 'when no due date exists' do
before do
visit_issue(project, issue)
end
it "displays 'None'" do
expect(due_date_value.text).to have_content 'None'
end
end
context 'when due date exists' do
before do
visit_issue(project, issue_with_due_date)
end
it "displays the due date" do
expect(due_date_value.text).to have_content mock_date.strftime('%b %-d, %Y')
end
end
end
context 'as an allowed user' do
before do
project.add_developer(user)
visit_issue(project, issue)
@ -238,6 +259,12 @@ RSpec.describe 'Issue Sidebar' do
end
context 'editing issue milestone', :js do
let_it_be(:milestone_expired) { create(:milestone, project: project, due_date: 5.days.ago) }
let_it_be(:milestone_no_duedate) { create(:milestone, project: project, title: 'Foo - No due date') }
let_it_be(:milestone1) { create(:milestone, project: project, title: 'Milestone-1', due_date: 20.days.from_now) }
let_it_be(:milestone2) { create(:milestone, project: project, title: 'Milestone-2', due_date: 15.days.from_now) }
let_it_be(:milestone3) { create(:milestone, project: project, title: 'Milestone-3', due_date: 10.days.from_now) }
before do
page.within('.block.milestone > .title') do
click_on 'Edit'
@ -426,6 +453,8 @@ RSpec.describe 'Issue Sidebar' do
def visit_issue(project, issue)
visit project_issue_path(project, issue)
wait_for_requests
end
def open_issue_sidebar

View File

@ -406,6 +406,12 @@ RSpec.describe "Issues > User edits issue", :js do
end
context 'update due date' do
before do
# Due date widget uses GraphQL and needs to wait for requests to come back
# The date picker won't be rendered before requests complete
wait_for_requests
end
it 'adds due date to issue' do
date = Date.today.at_beginning_of_month + 2.days

View File

@ -34,7 +34,7 @@ RSpec.describe 'Project navbar' do
it 'redirects to value stream when Analytics item is clicked' do
page.within('.sidebar-top-level-items') do
find('[data-qa-selector=analytics_anchor]').click
find('.shortcuts-analytics').click
end
wait_for_requests

View File

@ -14,6 +14,7 @@
"due_date": { "type": ["string", "null"] },
"relative_position": { "type": ["integer", "null"] },
"time_estimate": { "type": "integer" },
"type": { "type": "string", "enum": ["ISSUE", "INCIDENT", "TEST_CASE", "REQUIREMENT"] },
"issue_sidebar_endpoint": { "type": "string" },
"toggle_subscription_endpoint": { "type": "string" },
"assignable_labels_endpoint": { "type": "string" },

View File

@ -80,6 +80,12 @@ describe('Sidebar date Widget', () => {
expect(findPopoverIcon().exists()).toBe(false);
});
it('does not render GlDatePicker', () => {
createComponent();
expect(findDatePicker().exists()).toBe(false);
});
describe('when issuable has no due date', () => {
beforeEach(async () => {
createComponent({
@ -114,8 +120,15 @@ describe('Sidebar date Widget', () => {
});
it('uses a correct prop to set the initial date for GlDatePicker', () => {
expect(findDatePicker().props('value')).toBe(null);
expect(findDatePicker().props('defaultDate')).toEqual(wrapper.vm.parsedDate);
expect(findDatePicker().props()).toMatchObject({
value: null,
autocomplete: 'off',
defaultDate: expect.any(Object),
});
});
it('renders GlDatePicker', async () => {
expect(findDatePicker().exists()).toBe(true);
});
});

View File

@ -21,17 +21,37 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Skip do
before do
allow(pipeline).to receive(:git_commit_message)
.and_return('commit message [ci skip]')
step.perform!
end
it 'breaks the chain' do
step.perform!
expect(step.break?).to be true
end
it 'skips the pipeline' do
step.perform!
expect(pipeline.reload).to be_skipped
end
it 'calls ensure_project_iid explicitly' do
expect(pipeline).to receive(:ensure_project_iid!)
step.perform!
end
context 'when the ci_pipeline_ensure_iid_on_save feature flag is off' do
before do
stub_feature_flags(ci_pipeline_ensure_iid_on_skip: false)
end
it 'does not call ensure_project_iid explicitly' do
expect(pipeline).not_to receive(:ensure_project_iid!)
step.perform!
end
end
end
context 'when pipeline has not been skipped' do

View File

@ -0,0 +1,120 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::AnalyticsMenu do
let_it_be(:project) { create(:project, :repository) }
let(:user) { project.owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project, current_ref: project.repository.root_ref) }
subject { described_class.new(context) }
describe '#render?' do
context 'whe user cannot read analytics' do
let(:user) { nil }
it 'returns false' do
expect(subject.render?).to be false
end
end
context 'whe user can read analytics' do
it 'returns true' do
expect(subject.render?).to be true
end
context 'when menu does not have any menu items' do
it 'returns false' do
allow(subject).to receive(:has_items?).and_return(false)
expect(subject.render?).to be false
end
end
context 'when menu has menu items' do
it 'returns true' do
expect(subject.render?).to be true
end
end
end
end
describe '#link' do
it 'returns link to the value stream page' do
expect(subject.link).to include('/-/value_stream_analytics')
end
context 'when Value Stream is not visible' do
it 'returns link to the the first visible menu item' do
allow(subject).to receive(:cycle_analytics_menu_item).and_return(nil)
expect(subject.link).to eq subject.items.first.link
end
end
end
describe 'Menu items' do
subject { described_class.new(context).items.index { |e| e.item_id == item_id } }
describe 'CI/CD' do
let(:item_id) { :ci_cd_analytics }
specify { is_expected.not_to be_nil }
describe 'when the project repository is empty' do
before do
allow(project).to receive(:empty_repo?).and_return(true)
end
specify { is_expected.to be_nil }
end
describe 'when builds access level is DISABLED' do
before do
project.project_feature.update!(builds_access_level: Featurable::DISABLED)
end
specify { is_expected.to be_nil }
end
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
end
describe 'Repository' do
let(:item_id) { :repository_analytics }
specify { is_expected.not_to be_nil }
describe 'when the project repository is empty' do
before do
allow(project).to receive(:empty_repo?).and_return(true)
end
specify { is_expected.to be_nil }
end
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
end
describe 'Value Stream' do
let(:item_id) { :cycle_analytics }
specify { is_expected.not_to be_nil }
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_nil }
end
end
end
end

View File

@ -21,6 +21,23 @@ RSpec.describe Ci::CreateWebIdeTerminalService do
expect(subject[:pipeline].stages.count).to eq(1)
expect(subject[:pipeline].builds.count).to eq(1)
end
it 'calls ensure_project_iid explicitly' do
expect_next_instance_of(Ci::Pipeline) do |instance|
expect(instance).to receive(:ensure_project_iid!).twice
end
subject
end
context 'when the ci_pipeline_ensure_iid_on_save feature flag is off' do
it 'does not call ensure_project_iid explicitly' do
stub_feature_flags(ci_pipeline_ensure_iid_on_save: false)
expect_next_instance_of(Ci::Pipeline) do |instance|
expect(instance).to receive(:ensure_project_iid!).once
end
subject
end
end
end
before do

View File

@ -220,7 +220,7 @@ RSpec.describe Tooling::Danger::ProjectHelper do
describe '.local_warning_message' do
it 'returns an informational message with rules that can run' do
expect(described_class.local_warning_message).to eq('==> Only the following Danger rules can be run locally: changelog, changes_size, commit_messages, database, datateam, documentation, duplicate_yarn_dependencies, eslint, karma, pajamas, pipeline, prettier, product_intelligence, utility_css')
expect(described_class.local_warning_message).to eq('==> Only the following Danger rules can be run locally: changelog, commit_messages, database, datateam, documentation, duplicate_yarn_dependencies, eslint, karma, pajamas, pipeline, prettier, product_intelligence, utility_css')
end
end

View File

@ -650,6 +650,68 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
end
describe 'Analytics' do
it 'top level navigation link is visible points to the value stream page' do
render
expect(rendered).to have_link('Analytics', href: project_cycle_analytics_path(project))
end
describe 'CI/CD' do
it 'has a link to the CI/CD analytics page' do
render
expect(rendered).to have_link('CI/CD', href: charts_project_pipelines_path(project))
end
context 'when user does not have access' do
let(:user) { nil }
it 'does not have a link to the CI/CD analytics page' do
render
expect(rendered).not_to have_link('CI/CD', href: charts_project_pipelines_path(project))
end
end
end
describe 'Repository' do
it 'has a link to the repository analytics page' do
render
expect(rendered).to have_link('Repository', href: charts_project_graph_path(project, 'master'))
end
context 'when user does not have access' do
let(:user) { nil }
it 'does not have a link to the repository analytics page' do
render
expect(rendered).not_to have_link('Repository', href: charts_project_graph_path(project, 'master'))
end
end
end
describe 'Value Stream' do
it 'has a link to the value stream page' do
render
expect(rendered).to have_link('Value Stream', href: project_cycle_analytics_path(project))
end
context 'when user does not have access' do
let(:user) { nil }
it 'does not have a link to the value stream page' do
render
expect(rendered).not_to have_link('Value Stream', href: project_cycle_analytics_path(project))
end
end
end
end
describe 'wiki entry tab' do
let(:can_read_wiki) { true }
@ -736,32 +798,6 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
end
describe 'value stream analytics entry' do
let(:read_cycle_analytics) { true }
before do
allow(view).to receive(:can?).with(user, :read_cycle_analytics, project).and_return(read_cycle_analytics)
end
describe 'when value stream analytics is enabled' do
it 'shows the value stream analytics entry' do
render
expect(rendered).to have_link('Value Stream', href: project_cycle_analytics_path(project))
end
end
describe 'when value stream analytics is disabled' do
let(:read_cycle_analytics) { false }
it 'does not show the value stream analytics entry' do
render
expect(rendered).not_to have_link('Value Stream', href: project_cycle_analytics_path(project))
end
end
end
describe 'operations settings tab' do
describe 'archive projects' do
before do

View File

@ -5,7 +5,6 @@ module Tooling
module ProjectHelper
LOCAL_RULES ||= %w[
changelog
changes_size
commit_messages
database
datateam
@ -176,26 +175,12 @@ module Tooling
ee? ? 'gitlab' : 'gitlab-foss'
end
def missing_database_labels(current_mr_labels)
labels = if has_database_scoped_labels?(current_mr_labels)
['database']
else
['database', 'database::review pending']
end
labels - current_mr_labels
end
private
def ee?
# Support former project name for `dev` and support local Danger run
%w[gitlab gitlab-ee].include?(ENV['CI_PROJECT_NAME']) || Dir.exist?(File.expand_path('../../../ee', __dir__))
end
def has_database_scoped_labels?(current_mr_labels)
current_mr_labels.any? { |label| label.start_with?('database::') }
end
end
end
end

View File

@ -283,6 +283,9 @@ func configureRoutes(u *upstream) {
// Gem Artifact Repository
u.route("POST", apiProjectPattern+`packages/rubygems/`, upload.BodyUploader(api, signingProxy, preparers.packages)),
// Terraform Module Package Repository
u.route("PUT", apiProjectPattern+`packages/terraform/modules/`, upload.BodyUploader(api, signingProxy, preparers.packages)),
// We are porting API to disk acceleration
// we need to declare each routes until we have fixed all the routes on the rails codebase.
// Overall status can be seen at https://gitlab.com/groups/gitlab-org/-/epics/1802#current-status

View File

@ -512,6 +512,7 @@ func TestPackageFilesUpload(t *testing.T) {
{"PUT", "/api/v4/projects/group%2Fproject/packages/generic/mypackage/0.0.1/myfile.tar.gz"},
{"PUT", "/api/v4/projects/group%2Fproject/packages/debian/libsample0_1.2.3~alpha2-1_amd64.deb"},
{"POST", "/api/v4/projects/group%2Fproject/packages/rubygems/api/v1/gems/sample.gem"},
{"PUT", "/api/v4/projects/group%2Fproject/packages/terraform/modules/mymodule/mysystem/0.0.1/file"},
}
for _, r := range routes {