summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/artifacts_settings/keep_latest_artifact_checkbox.vue18
-rw-r--r--app/assets/javascripts/clone_panel.js1
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue2
-rw-r--r--app/helpers/application_helper.rb2
-rw-r--r--app/models/repository.rb14
-rw-r--r--app/services/issues/export_csv_service.rb10
-rw-r--r--app/views/projects/settings/ci_cd/show.html.haml21
-rwxr-xr-xbin/actioncable2
-rwxr-xr-xbin/web_puma2
-rw-r--r--changelogs/unreleased/322198-fix-metrics-tab.yml5
-rw-r--r--changelogs/unreleased/fix-email-participants-migration-version-number.yml6
-rw-r--r--changelogs/unreleased/fix-keep-artifacts-project-setting.yml5
-rw-r--r--changelogs/unreleased/lm-fix-authorization-lint.yml5
-rw-r--r--changelogs/unreleased/psi-board-scroll.yml5
-rw-r--r--changelogs/unreleased/puma_sigint.yml5
-rw-r--r--changelogs/unreleased/reset_template_cache_key.yml5
-rw-r--r--changelogs/unreleased/revert-fb45e290.yml5
-rw-r--r--changelogs/unreleased/sh-fix-export-csv-service-nplusone.yml5
-rw-r--r--data/whats_new/202102180001_13_09.yml121
-rw-r--r--db/migrate/20201128210000_add_service_desk_reply_to_is_not_null_index_on_issues_fix.rb18
-rw-r--r--db/migrate/20201221225303_add_service_desk_reply_to_is_not_null_index_on_issues.rb15
-rw-r--r--db/schema_migrations/202011282100001
-rw-r--r--lib/api/lint.rb2
-rw-r--r--lib/gitlab/current_settings.rb4
-rw-r--r--lib/gitlab/import_export/repo_restorer.rb2
-rw-r--r--lib/gitlab/template/base_template.rb2
-rw-r--r--lib/gitlab/template/issue_template.rb2
-rw-r--r--lib/gitlab/template/merge_request_template.rb2
-rw-r--r--lib/object_storage/config.rb36
-rw-r--r--lib/object_storage/direct_upload.rb4
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/frontend/artifacts_settings/components/__snapshots__/keep_latest_artifact_checkbox_spec.js.snap32
-rw-r--r--spec/frontend/artifacts_settings/components/keep_latest_artifact_checkbox_spec.js19
-rw-r--r--spec/frontend/vue_shared/alert_details/alert_details_spec.js7
-rw-r--r--spec/lib/gitlab/current_settings_spec.rb20
-rw-r--r--spec/lib/gitlab/import_export/repo_restorer_spec.rb2
-rw-r--r--spec/lib/object_storage/config_spec.rb63
-rw-r--r--spec/models/repository_spec.rb4
-rw-r--r--spec/requests/api/lint_spec.rb19
-rw-r--r--spec/services/issues/export_csv_service_spec.rb45
40 files changed, 312 insertions, 229 deletions
diff --git a/app/assets/javascripts/artifacts_settings/keep_latest_artifact_checkbox.vue b/app/assets/javascripts/artifacts_settings/keep_latest_artifact_checkbox.vue
index 92f1cc8117a..d797469dd53 100644
--- a/app/assets/javascripts/artifacts_settings/keep_latest_artifact_checkbox.vue
+++ b/app/assets/javascripts/artifacts_settings/keep_latest_artifact_checkbox.vue
@@ -2,7 +2,6 @@
import { GlAlert, GlFormCheckbox, GlLink } from '@gitlab/ui';
import { __ } from '~/locale';
import UpdateKeepLatestArtifactProjectSetting from './graphql/mutations/update_keep_latest_artifact_project_setting.mutation.graphql';
-import GetKeepLatestArtifactApplicationSetting from './graphql/queries/get_keep_latest_artifact_application_setting.query.graphql';
import GetKeepLatestArtifactProjectSetting from './graphql/queries/get_keep_latest_artifact_project_setting.query.graphql';
export default {
@@ -14,7 +13,6 @@ export default {
enabledHelpText: __(
'The latest artifacts created by jobs in the most recent successful pipeline will be stored.',
),
- disabledHelpText: __('This feature is disabled at the instance level.'),
helpLinkText: __('More information'),
checkboxText: __('Keep artifacts from most recent successful jobs'),
},
@@ -46,19 +44,12 @@ export default {
this.reportError(this.$options.errors.fetchError);
},
},
- projectSettingDisabled: {
- query: GetKeepLatestArtifactApplicationSetting,
- update(data) {
- return !data.ciApplicationSettings?.keepLatestArtifact;
- },
- },
},
data() {
return {
keepLatestArtifact: null,
errorMessage: '',
isAlertDismissed: false,
- projectSettingDisabled: true,
};
},
computed: {
@@ -66,9 +57,7 @@ export default {
return this.errorMessage && !this.isAlertDismissed;
},
helpText() {
- return this.projectSettingDisabled
- ? this.$options.i18n.disabledHelpText
- : this.$options.i18n.enabledHelpText;
+ return this.$options.i18n.enabledHelpText;
},
},
methods: {
@@ -106,10 +95,7 @@ export default {
@dismiss="isAlertDismissed = true"
>{{ errorMessage }}</gl-alert
>
- <gl-form-checkbox
- v-model="keepLatestArtifact"
- :disabled="projectSettingDisabled"
- @change="updateSetting"
+ <gl-form-checkbox v-model="keepLatestArtifact" @change="updateSetting"
><strong class="gl-mr-3">{{ $options.i18n.checkboxText }}</strong>
<gl-link :href="helpPagePath">{{ $options.i18n.helpLinkText }}</gl-link>
<template v-if="!$apollo.loading" #help>{{ helpText }}</template>
diff --git a/app/assets/javascripts/clone_panel.js b/app/assets/javascripts/clone_panel.js
index c9fae8f17a4..ec831a77bde 100644
--- a/app/assets/javascripts/clone_panel.js
+++ b/app/assets/javascripts/clone_panel.js
@@ -15,7 +15,6 @@ export default function initClonePanel() {
}
$('a', $cloneOptions).on('click', (e) => {
- e.preventDefault();
const $this = $(e.currentTarget);
const url = $this.attr('href');
if (url && (url.startsWith('vscode://') || url.startsWith('xcode://'))) {
diff --git a/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue b/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue
index bcea7ca654e..0af5d028a2a 100644
--- a/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue
+++ b/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue
@@ -368,7 +368,7 @@ export default {
<alert-details-table :alert="alert" :loading="loading" />
</gl-tab>
<gl-tab
- v-if="isThreatMonitoringPage"
+ v-if="!isThreatMonitoringPage"
:data-testid="$options.tabsConfig[1].id"
:title="$options.tabsConfig[1].title"
>
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 8268ab1ad56..9af45daaca4 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -280,7 +280,7 @@ module ApplicationHelper
def page_class
class_names = []
- class_names << 'issue-boards-page gl-overflow-hidden' if current_controller?(:boards)
+ class_names << 'issue-boards-page gl-overflow-auto' if current_controller?(:boards)
class_names << 'environment-logs-page' if current_controller?(:logs)
class_names << 'with-performance-bar' if performance_bar_enabled?
class_names << system_message_class
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 06a13194e1a..84ca8f0c12a 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -43,7 +43,7 @@ class Repository
changelog license_blob license_key gitignore
gitlab_ci_yml branch_names tag_names branch_count
tag_count avatar exists? root_ref merged_branch_names
- has_visible_content? issue_template_names_by_category merge_request_template_names_by_category
+ has_visible_content? issue_template_names_hash merge_request_template_names_hash
user_defined_metrics_dashboard_paths xcode_project? has_ambiguous_refs?).freeze
# Methods that use cache_method but only memoize the value
@@ -60,8 +60,8 @@ class Repository
gitignore: :gitignore,
gitlab_ci: :gitlab_ci_yml,
avatar: :avatar,
- issue_template: :issue_template_names_by_category,
- merge_request_template: :merge_request_template_names_by_category,
+ issue_template: :issue_template_names_hash,
+ merge_request_template: :merge_request_template_names_hash,
metrics_dashboard: :user_defined_metrics_dashboard_paths,
xcode_config: :xcode_project?
}.freeze
@@ -573,15 +573,15 @@ class Repository
cache_method :avatar
# store issue_template_names as hash
- def issue_template_names_by_category
+ def issue_template_names_hash
Gitlab::Template::IssueTemplate.repository_template_names(project)
end
- cache_method :issue_template_names_by_category, fallback: {}
+ cache_method :issue_template_names_hash, fallback: {}
- def merge_request_template_names_by_category
+ def merge_request_template_names_hash
Gitlab::Template::MergeRequestTemplate.repository_template_names(project)
end
- cache_method :merge_request_template_names_by_category, fallback: {}
+ cache_method :merge_request_template_names_hash, fallback: {}
def user_defined_metrics_dashboard_paths
Gitlab::Metrics::Dashboard::RepoDashboardFinder.list_dashboards(project)
diff --git a/app/services/issues/export_csv_service.rb b/app/services/issues/export_csv_service.rb
index 2181c46c90d..dd43c77adfa 100644
--- a/app/services/issues/export_csv_service.rb
+++ b/app/services/issues/export_csv_service.rb
@@ -5,6 +5,12 @@ module Issues
include Gitlab::Routing.url_helpers
include GitlabRoutingHelper
+ def initialize(issuables_relation, project)
+ super
+
+ @labels = @issuables.labels_hash.transform_values { |labels| labels.sort.join(',').presence }
+ end
+
def email(user)
Notify.issues_csv_email(user, project, csv_data, csv_builder.status).deliver_now
end
@@ -12,7 +18,7 @@ module Issues
private
def associations_to_preload
- %i(author assignees timelogs milestone)
+ %i(author assignees timelogs milestone project)
end
def header_to_value_hash
@@ -41,7 +47,7 @@ module Issues
end
def issue_labels(issue)
- issuables.labels_hash[issue.id].sort.join(',').presence
+ @labels[issue.id]
end
# rubocop: disable CodeReuse/ActiveRecord
diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml
index ee49377595b..51abb8ed791 100644
--- a/app/views/projects/settings/ci_cd/show.html.haml
+++ b/app/views/projects/settings/ci_cd/show.html.haml
@@ -45,16 +45,17 @@
.settings-content
= render 'projects/runners/index'
-%section.settings.no-animate#js-artifacts-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _("Artifacts")
- %button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _("A job artifact is an archive of files and directories saved by a job when it finishes.")
- .settings-content
- #js-artifacts-settings-app{ data: { full_path: @project.full_path, help_page_path: help_page_path('ci/pipelines/job_artifacts', anchor: 'keep-artifacts-from-most-recent-successful-jobs') } }
+- if Gitlab::CurrentSettings.current_application_settings.keep_latest_artifact?
+ %section.settings.no-animate#js-artifacts-settings{ class: ('expanded' if expanded) }
+ .settings-header
+ %h4
+ = _("Artifacts")
+ %button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded ? _('Collapse') : _('Expand')
+ %p
+ = _("A job artifact is an archive of files and directories saved by a job when it finishes.")
+ .settings-content
+ #js-artifacts-settings-app{ data: { full_path: @project.full_path, help_page_path: help_page_path('ci/pipelines/job_artifacts', anchor: 'keep-artifacts-from-most-recent-successful-jobs') } }
%section.qa-variables-settings.settings.no-animate#js-cicd-variables-settings{ class: ('expanded' if expanded), data: { qa_selector: 'variables_settings_content' } }
.settings-header
diff --git a/bin/actioncable b/bin/actioncable
index 0aacb19e070..14600ec1177 100755
--- a/bin/actioncable
+++ b/bin/actioncable
@@ -36,7 +36,7 @@ start_foreground()
stop()
{
get_puma_pid
- kill -QUIT "$(get_puma_pid)"
+ kill -INT "$(get_puma_pid)"
}
reload()
diff --git a/bin/web_puma b/bin/web_puma
index 6a9b729f6dd..c1ab4718f0d 100755
--- a/bin/web_puma
+++ b/bin/web_puma
@@ -36,7 +36,7 @@ start_foreground()
stop()
{
get_puma_pid
- kill -QUIT "$(get_puma_pid)"
+ kill -INT "$(get_puma_pid)"
}
reload()
diff --git a/changelogs/unreleased/322198-fix-metrics-tab.yml b/changelogs/unreleased/322198-fix-metrics-tab.yml
new file mode 100644
index 00000000000..d7600177be7
--- /dev/null
+++ b/changelogs/unreleased/322198-fix-metrics-tab.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Metric tab not showing up on operations page
+merge_request: 54736
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-email-participants-migration-version-number.yml b/changelogs/unreleased/fix-email-participants-migration-version-number.yml
new file mode 100644
index 00000000000..c0333716789
--- /dev/null
+++ b/changelogs/unreleased/fix-email-participants-migration-version-number.yml
@@ -0,0 +1,6 @@
+---
+title: Fix creating the idx_on_issues_where_service_desk_reply_to_is_not_null index
+ before the post migration
+merge_request: 54346
+author:
+type: other
diff --git a/changelogs/unreleased/fix-keep-artifacts-project-setting.yml b/changelogs/unreleased/fix-keep-artifacts-project-setting.yml
new file mode 100644
index 00000000000..dfff4cf73da
--- /dev/null
+++ b/changelogs/unreleased/fix-keep-artifacts-project-setting.yml
@@ -0,0 +1,5 @@
+---
+title: Fix keep latest artifacts checkbox being always disabled
+merge_request: 54669
+author:
+type: fixed
diff --git a/changelogs/unreleased/lm-fix-authorization-lint.yml b/changelogs/unreleased/lm-fix-authorization-lint.yml
new file mode 100644
index 00000000000..cca59b7b43e
--- /dev/null
+++ b/changelogs/unreleased/lm-fix-authorization-lint.yml
@@ -0,0 +1,5 @@
+---
+title: Updates authorization for linting endpoint
+merge_request: 54492
+author:
+type: changed
diff --git a/changelogs/unreleased/psi-board-scroll.yml b/changelogs/unreleased/psi-board-scroll.yml
new file mode 100644
index 00000000000..3d68933b6f8
--- /dev/null
+++ b/changelogs/unreleased/psi-board-scroll.yml
@@ -0,0 +1,5 @@
+---
+title: Restore missing horizontal scrollbar on issue boards
+merge_request: 54634
+author:
+type: fixed
diff --git a/changelogs/unreleased/puma_sigint.yml b/changelogs/unreleased/puma_sigint.yml
new file mode 100644
index 00000000000..9bc6fd856f1
--- /dev/null
+++ b/changelogs/unreleased/puma_sigint.yml
@@ -0,0 +1,5 @@
+---
+title: Send SIGINT instead of SIGQUIT to puma
+merge_request: 54446
+author: Jörg Behrmann @behrmann
+type: fixed
diff --git a/changelogs/unreleased/reset_template_cache_key.yml b/changelogs/unreleased/reset_template_cache_key.yml
new file mode 100644
index 00000000000..7ee55542bae
--- /dev/null
+++ b/changelogs/unreleased/reset_template_cache_key.yml
@@ -0,0 +1,5 @@
+---
+title: Reset description template names cache key to reload an updated templates structure
+merge_request: 54614
+author:
+type: fixed
diff --git a/changelogs/unreleased/revert-fb45e290.yml b/changelogs/unreleased/revert-fb45e290.yml
new file mode 100644
index 00000000000..79554e2a12d
--- /dev/null
+++ b/changelogs/unreleased/revert-fb45e290.yml
@@ -0,0 +1,5 @@
+---
+title: Fix S3 object storage failing when endpoint is not specified
+merge_request: 54868
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-export-csv-service-nplusone.yml b/changelogs/unreleased/sh-fix-export-csv-service-nplusone.yml
new file mode 100644
index 00000000000..f1da7ec9682
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-export-csv-service-nplusone.yml
@@ -0,0 +1,5 @@
+---
+title: Fix N+1 SQL regression in exporting issues to CSV
+merge_request: 54287
+author:
+type: performance
diff --git a/data/whats_new/202102180001_13_09.yml b/data/whats_new/202102180001_13_09.yml
new file mode 100644
index 00000000000..314ffc8dba3
--- /dev/null
+++ b/data/whats_new/202102180001_13_09.yml
@@ -0,0 +1,121 @@
+- title: "Select CI/CD configuration from any job and reuse it"
+ body: |
+ Previously, if you wanted to reuse the same configuration in multiple jobs, you had two options: add YAML anchors, which don't work across different configuration files, or use `extends` to reuse an entire section.
+
+ In this release, we've added a new YAML function called `!reference`, which lets you target the exact configuration you want to reuse as part of your CI/CD pipeline, even if it's in another file.
+ stage: Verify
+ self-managed: true
+ gitlab-com: true
+ packages: [Free, Premium, Ultimate]
+ url: https://docs.gitlab.com/ee/ci/yaml/README.html#reference-tags
+ image_url: https://about.gitlab.com/images/13_9/reference.png
+ published_at: 2021-02-22
+ release: 13.9
+- title: "GPU and smart scheduling support for GitLab Runner"
+ body: |
+ Specialized compute workloads like those used in machine learning can significantly benefit from access to GPUs. Developers can configure GitLab Runner to leverage GPUs in the Docker executor by forwarding the `--gpu` flag. You can also use this with recent support in [GitLab’s fork of Docker Machine](https://docs.gitlab.com/runner/executors/docker_machine.html#using-the-forked-version-of-docker-machine), which allows you to [accelerate workloads with attached GPUs](https://cloud.google.com/compute/docs/gpus/create-vm-with-gpus). Doing so can help control costs associated with potentially expensive machine configurations.
+ stage: Verify
+ self-managed: true
+ gitlab-com: true
+ packages: [Free, Premium, Ultimate]
+ url: https://docs.gitlab.com/runner/executors/docker_machine.html#using-gpus-on-google-compute-engine
+ image_url: https://about.gitlab.com/images/ci/gitlab-ci-cd-logo_2x.png
+ published_at: 2021-02-22
+ release: 13.9
+- title: "Vault JWT (JSON Web Token) supports GitLab environments"
+ body: |
+ To simplify integrations with HashiCorp Vault, we've shipped Vault JWT token support. From the launch, you could restrict access based on data in the JWT. This release gives you a new dimension for restricting access to credentials: the environment a job targets.
+
+ This release extends the existing Vault JWT token to support environment-based restrictions too. As the `environment` name could be supplied by the user running the pipeline, we recommend you use the new environment-based restrictions with the already-existing `ref_type` values for maximum security.
+ stage: Configure
+ self-managed: true
+ gitlab-com: true
+ packages: [Free, Premium, Ultimate]
+ url: https://docs.gitlab.com/ee/ci/examples/authenticating-with-hashicorp-vault/index.html
+ image_url: https://about.gitlab.com/images/icons/get-a-license.svg
+ published_at: 2021-02-22
+ release: 13.9
+- title: "SAST Support for .NET 5"
+ body: |
+ Microsoft's [release of .NET 5.0](https://docs.microsoft.com/en-us/dotnet/core/dotnet-five) is the next major release of .NET Core which supports more types of apps and more platforms than .NET Core or .NET Framework. We have updated our .NET SAST analyzer, [Security Code Scan](https://security-code-scan.github.io/) to support this new version which is also now supported with [our SAST language detection](https://docs.gitlab.com/ee/user/application_security/sast/#supported-languages-and-frameworks) allowing GitLab SAST to automatically detect .NET 5 projects. This change was part of a community contribution by [@shaun.burns](https://gitlab.com/shaun.burns).
+ stage: Secure
+ self-managed: true
+ gitlab-com: true
+ packages: [Free, Premium, Ultimate]
+ url: https://docs.gitlab.com/ee/user/application_security/sast/analyzers.html#analyzers-data
+ image_url: https://about.gitlab.com/images/solutions/icon_sheild-check.svg
+ published_at: 2021-02-22
+ release: 13.9
+- title: "Create Jira issues from Vulnerabilities"
+ body: |
+ Many customers use Jira as their single source of truth for tracking issues and engineering work. When using GitLab for SCM and CI/CD, they can take advantage of our existing integration to pass information from MRs and commits into existing Jira issues. However, until now there has been no way to automatically pass information about vulnerabilities detected by security scanners into Jira. This leaves users who rely on Jira to track work no choice but to manually copy vulnerability information into new Jira issues.
+
+ To address this, we're introducing a new ability to create Jira issues directly from a vulnerability's details. You will see this as a new option in your existing Jira integration settings. Simply enable the new option and select the desired Jira issue type. Available issue types are pulled automatically based on the currently configured Jira target project. Once enabled, all places in your project where you can create GitLab issues from a vulnerability will instead directly create a Jira issue of the configured type.
+ stage: Secure
+ self-managed: true
+ gitlab-com: true
+ packages: [Ultimate]
+ url: https://docs.gitlab.com/ee/user/application_security/vulnerabilities/#create-a-gitlab-issue-for-a-vulnerability
+ image_url: https://about.gitlab.com/images/13_9/jira-vulnerability-integration.png
+ published_at: 2021-02-22
+ release: 13.9
+- title: "Security Alert Dashboard for Container Network Policy Alerts"
+ body: |
+ We're pleased to announce the first release of a dashboard for security alerts! Users can now configure Container Network Policies to send alerts to the security alert dashboard. This is especially useful when traffic must be closely monitored but cannot be blocked entirely without negatively impacting the business. You can configure the alerts at **Security & Compliance > Threat Management > Policies**, and view them at **Security & Compliance > Threat Management > Alerts**.
+ stage: Secure
+ self-managed: true
+ gitlab-com: true
+ packages: [Ultimate]
+ url: https://docs.gitlab.com/ee/user/application_security/threat_monitoring/#configuring-network-policy-alerts
+ image_url: https://about.gitlab.com/images/13_9/security_alert_dashboard.png
+ published_at: 2021-02-22
+ release: 13.9
+- title: "Maintenance Mode"
+ body: |
+ Systems administrators occasionally perform maintenance operations on their GitLab instance to keep it performing optimally. Administrators want to offer the highest level of access to their users while these operations are taking place. For example, you may want to perform a [failover test to a secondary site](https://docs.gitlab.com/ee/administration/geo/disaster_recovery/planned_failover.html) as part of the company's business continuity plan. Prior to the failover, you want to pause changes for a short period to ensure the secondary is fully synchronized. Until GitLab 13.8, you could [restrict users from logging in](https://docs.gitlab.com/omnibus/maintenance/#restrict-users-from-logging-into-gitlab), but this would block the entire UI and would render GitLab inaccessible to users.
+
+ GitLab 13.9 introduces [maintenance mode](https://docs.gitlab.com/ee/administration/maintenance_mode/index.html), where write operations are disabled at the application level. This means that GitLab is effectively in a read-only state for all non-administrative users (administrators are still able to edit application settings and [background jobs continue](https://docs.gitlab.com/ee/administration/maintenance_mode/index.html#background-jobs)). Regular users are able to log in to GitLab, view the interface and perform other read-only operations, such as `git clone` or `git pull`. Using maintenance mode, systems administrators can perform maintenance operations, such as failing over to a secondary site, with minimal disruption to regular users.
+
+ Note that GitLab already [supports zero-downtime updates](https://docs.gitlab.com/omnibus/update/#zero-downtime-updates) and enabling maintenance mode is not required to keep your instance up-to-date.
+ stage: Enablement
+ self-managed: true
+ gitlab-com: false
+ packages: [Premium, Ultimate]
+ url: https://docs.gitlab.com/ee/administration/maintenance_mode/index.html
+ image_url: https://about.gitlab.com/images/13_9/maintenance_mode.png
+ published_at: 2021-02-22
+ release: 13.9
+- title: "New merge request metric: mean time to merge"
+ body: |
+ A new metric, mean time to merge (MTTM), is available in project-level merge request analytics. MTTM is the average time from when a merge request (MR) is created, until the time it is merged. By selecting different dates in the date range selector, you can see how your time to merge MRs changes over time and understand whether you are becoming more efficient at reviewing and merging code.
+ stage: Manage
+ self-managed: true
+ gitlab-com: true
+ packages: [Premium, Ultimate]
+ url: https://docs.gitlab.com/ee/user/analytics/merge_request_analytics.html#mean-time-to-merge
+ image_url: https://about.gitlab.com/images/13_9/mttm.png
+ published_at: 2021-02-22
+ release: 13.9
+- title: "Filter roadmaps by confidentiality"
+ body: |
+ When viewing a roadmap, there used to be no way to hide confidential epics from the main view. This could be frustrating if you wanted to share your roadmap with a public audience. We've updated the search bar filter to include confidentiality so you now have another layer in which you can refine your roadmap.
+ stage: Plan
+ self-managed: true
+ gitlab-com: true
+ packages: [Premium, Ultimate]
+ url: https://docs.gitlab.com/ee/user/group/roadmap/#sort-and-filter-the-roadmap
+ image_url: https://about.gitlab.com/images/13_9/roadmap-filter-by-confidentiality.png
+ published_at: 2021-02-22
+ release: 13.9
+- title: "Allow Deploy Keys to push to protected branches"
+ body: |
+ When viewing a roadmap, there used to be no way to hide confidential epics from the main view. This could be frustrating if you wanted to share your roadmap with a public audience. We've updated the search bar filter to include confidentiality so you now have another layer in which you can refine your roadmap.
+ stage: Plan
+ self-managed: true
+ gitlab-com: true
+ packages: [Free, Premium, Ultimate]
+ url: https://docs.gitlab.com/ee/user/project/protected_branches.html#allow-deploy-keys-to-push-to-a-protected-branch
+ image_url: https://about.gitlab.com/images/13_9/deploy_keys.png
+ published_at: 2021-02-22
+ release: 13.9
+
diff --git a/db/migrate/20201128210000_add_service_desk_reply_to_is_not_null_index_on_issues_fix.rb b/db/migrate/20201128210000_add_service_desk_reply_to_is_not_null_index_on_issues_fix.rb
new file mode 100644
index 00000000000..52d24de60d5
--- /dev/null
+++ b/db/migrate/20201128210000_add_service_desk_reply_to_is_not_null_index_on_issues_fix.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddServiceDeskReplyToIsNotNullIndexOnIssuesFix < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'idx_on_issues_where_service_desk_reply_to_is_not_null'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index(:issues, [:id], name: INDEX_NAME, where: 'service_desk_reply_to IS NOT NULL')
+ end
+
+ def down
+ remove_concurrent_index_by_name(:issues, INDEX_NAME)
+ end
+end
diff --git a/db/migrate/20201221225303_add_service_desk_reply_to_is_not_null_index_on_issues.rb b/db/migrate/20201221225303_add_service_desk_reply_to_is_not_null_index_on_issues.rb
index fb5429af458..7643fc107aa 100644
--- a/db/migrate/20201221225303_add_service_desk_reply_to_is_not_null_index_on_issues.rb
+++ b/db/migrate/20201221225303_add_service_desk_reply_to_is_not_null_index_on_issues.rb
@@ -1,18 +1,11 @@
# frozen_string_literal: true
class AddServiceDeskReplyToIsNotNullIndexOnIssues < ActiveRecord::Migration[6.0]
- include Gitlab::Database::MigrationHelpers
-
DOWNTIME = false
- INDEX_NAME = 'idx_on_issues_where_service_desk_reply_to_is_not_null'
-
- disable_ddl_transaction!
-
- def up
- add_concurrent_index(:issues, [:id], name: INDEX_NAME, where: 'service_desk_reply_to IS NOT NULL')
- end
- def down
- remove_concurrent_index_by_name(:issues, INDEX_NAME)
+ def change
+ # no-op, the migration's version number was lowered to be executed earlier than db/post_migrate/20201128210234_schedule_populate_issue_email_participants.rb
+ #
+ # The new migration is located here: db/migrate/20201128210000_add_service_desk_reply_to_is_not_null_index_on_issues_fix.rb
end
end
diff --git a/db/schema_migrations/20201128210000 b/db/schema_migrations/20201128210000
new file mode 100644
index 00000000000..9ce8d37ff37
--- /dev/null
+++ b/db/schema_migrations/20201128210000
@@ -0,0 +1 @@
+2f7415e3e3e66f326f2f65c38406c2103d5075493c86a836497c3541655f4e86 \ No newline at end of file
diff --git a/lib/api/lint.rb b/lib/api/lint.rb
index 2d30754a36d..e0806674c6a 100644
--- a/lib/api/lint.rb
+++ b/lib/api/lint.rb
@@ -11,7 +11,7 @@ module API
optional :include_merged_yaml, type: Boolean, desc: 'Whether or not to include merged CI config yaml in the response'
end
post '/lint' do
- unauthorized! unless Gitlab::CurrentSettings.signup_enabled? && current_user
+ unauthorized! if Gitlab::CurrentSettings.signup_disabled? && current_user.nil?
result = Gitlab::Ci::YamlProcessor.new(params[:content], user: current_user).execute
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 0bf41f9dc0d..55f381fcb64 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -3,6 +3,10 @@
module Gitlab
module CurrentSettings
class << self
+ def signup_disabled?
+ !signup_enabled?
+ end
+
def current_application_settings
Gitlab::SafeRequestStore.fetch(:current_application_settings) { ensure_application_settings! }
end
diff --git a/lib/gitlab/import_export/repo_restorer.rb b/lib/gitlab/import_export/repo_restorer.rb
index 8af7b68d78e..998da3e4afb 100644
--- a/lib/gitlab/import_export/repo_restorer.rb
+++ b/lib/gitlab/import_export/repo_restorer.rb
@@ -42,7 +42,7 @@ module Gitlab
def ensure_repository_does_not_exist!
if repository.exists?
shared.logger.info(
- message: %Q{Deleting existing "#{repository.path}" to re-import it.}
+ message: %Q{Deleting existing "#{repository.disk_path}" to re-import it.}
)
Repositories::DestroyService.new(repository).execute
diff --git a/lib/gitlab/template/base_template.rb b/lib/gitlab/template/base_template.rb
index 0f933a61598..3be77aff07e 100644
--- a/lib/gitlab/template/base_template.rb
+++ b/lib/gitlab/template/base_template.rb
@@ -108,7 +108,7 @@ module Gitlab
# Gitaly the actual template names within a given project's repository for all file templates
# other than `issue` and `merge request` description templates, which would instead
# overwrite the `template_names` method to return a redis cached version, by reading cached values
- # from `repository.issue_template_names_by_category` and `repository.merge_request_template_names_by_category`
+ # from `repository.issue_template_names_hash` and `repository.merge_request_template_names_hash`
# methods.
def repository_template_names(project)
template_names_by_category(self.all(project))
diff --git a/lib/gitlab/template/issue_template.rb b/lib/gitlab/template/issue_template.rb
index 3049f43b322..30fff9bdae9 100644
--- a/lib/gitlab/template/issue_template.rb
+++ b/lib/gitlab/template/issue_template.rb
@@ -23,7 +23,7 @@ module Gitlab
# own caching mechanism to avoid the back and forth call jumps between finder and model.
#
# follow-up issue: https://gitlab.com/gitlab-org/gitlab/-/issues/300279
- project.repository.issue_template_names_by_category
+ project.repository.issue_template_names_hash
end
end
end
diff --git a/lib/gitlab/template/merge_request_template.rb b/lib/gitlab/template/merge_request_template.rb
index 9442f3b13fb..7491c8f26c6 100644
--- a/lib/gitlab/template/merge_request_template.rb
+++ b/lib/gitlab/template/merge_request_template.rb
@@ -23,7 +23,7 @@ module Gitlab
# own caching mechanism to avoid the back and forth call jumps between finder and model.
#
# follow-up issue: https://gitlab.com/gitlab-org/gitlab/-/issues/300279
- project.repository.merge_request_template_names_by_category
+ project.repository.merge_request_template_names_hash
end
end
end
diff --git a/lib/object_storage/config.rb b/lib/object_storage/config.rb
index 0e6408b4917..f933d4e4866 100644
--- a/lib/object_storage/config.rb
+++ b/lib/object_storage/config.rb
@@ -2,8 +2,6 @@
module ObjectStorage
class Config
- include Gitlab::Utils::StrongMemoize
-
AWS_PROVIDER = 'AWS'
AZURE_PROVIDER = 'AzureRM'
GOOGLE_PROVIDER = 'Google'
@@ -68,36 +66,6 @@ module ObjectStorage
def provider
credentials[:provider].to_s
end
-
- # This method converts fog-aws parameters to an endpoint for the
- # Workhorse S3 client.
- def s3_endpoint
- strong_memoize(:s3_endpoint) do
- # We could omit this line and let the following code handle this, but
- # this will ensure that working configurations that use `endpoint`
- # will continue to work.
- next credentials[:endpoint] if credentials[:endpoint].present?
-
- generate_s3_endpoint_from_credentials
- end
- end
-
- def generate_s3_endpoint_from_credentials
- # fog-aws has special handling of the host, region, scheme, etc:
- # https://github.com/fog/fog-aws/blob/c7a11ba377a76d147861d0e921eb1e245bc11b6c/lib/fog/aws/storage.rb#L440-L449
- # Rather than reimplement this, we derive it from a sample GET URL.
- url = fog_connection.get_object_url(bucket, "tmp", nil)
- uri = ::Addressable::URI.parse(url)
-
- return unless uri&.scheme && uri&.host
-
- endpoint = "#{uri.scheme}://#{uri.host}"
- endpoint += ":#{uri.port}" if uri.port
- endpoint
- rescue ::URI::InvalidComponentError, ::Addressable::URI::InvalidURIError => e
- Gitlab::ErrorTracking.track_exception(e)
- nil
- end
# End AWS-specific options
# Begin Azure-specific options
@@ -123,10 +91,6 @@ module ObjectStorage
end
end
- def fog_connection
- @connection ||= ::Fog::Storage.new(credentials)
- end
-
private
# This returns a Hash of HTTP encryption headers to send along to S3.
diff --git a/lib/object_storage/direct_upload.rb b/lib/object_storage/direct_upload.rb
index 9fb4b571e06..7f1c30e574d 100644
--- a/lib/object_storage/direct_upload.rb
+++ b/lib/object_storage/direct_upload.rb
@@ -80,7 +80,7 @@ module ObjectStorage
S3Config: {
Bucket: bucket_name,
Region: credentials[:region],
- Endpoint: config.s3_endpoint,
+ Endpoint: credentials[:endpoint],
PathStyle: config.use_path_style?,
UseIamProfile: config.use_iam_profile?,
ServerSideEncryption: config.server_side_encryption,
@@ -229,7 +229,7 @@ module ObjectStorage
end
def connection
- config.fog_connection
+ @connection ||= ::Fog::Storage.new(credentials)
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 1bcd85ca9e2..839c249eb29 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -30012,9 +30012,6 @@ msgstr ""
msgid "This epic does not exist or you don't have sufficient permission."
msgstr ""
-msgid "This feature is disabled at the instance level."
-msgstr ""
-
msgid "This feature requires local storage to be enabled"
msgstr ""
diff --git a/spec/frontend/artifacts_settings/components/__snapshots__/keep_latest_artifact_checkbox_spec.js.snap b/spec/frontend/artifacts_settings/components/__snapshots__/keep_latest_artifact_checkbox_spec.js.snap
index bfe7e40fb32..bf33aa731ef 100644
--- a/spec/frontend/artifacts_settings/components/__snapshots__/keep_latest_artifact_checkbox_spec.js.snap
+++ b/spec/frontend/artifacts_settings/components/__snapshots__/keep_latest_artifact_checkbox_spec.js.snap
@@ -1,37 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`Keep latest artifact checkbox when application keep latest artifact setting is disabled checkbox is disabled when application setting is disabled 1`] = `
-<div>
- <!---->
-
- <b-form-checkbox-stub
- checked="true"
- class="gl-form-checkbox"
- disabled="true"
- plain="true"
- value="true"
- >
- <strong
- class="gl-mr-3"
- >
- Keep artifacts from most recent successful jobs
- </strong>
-
- <gl-link-stub
- href="/help/ci/pipelines/job_artifacts"
- >
- More information
- </gl-link-stub>
-
- <p
- class="help-text"
- >
- This feature is disabled at the instance level.
- </p>
- </b-form-checkbox-stub>
-</div>
-`;
-
exports[`Keep latest artifact checkbox when application keep latest artifact setting is enabled sets correct setting value in checkbox with query result 1`] = `
<div>
<!---->
diff --git a/spec/frontend/artifacts_settings/components/keep_latest_artifact_checkbox_spec.js b/spec/frontend/artifacts_settings/components/keep_latest_artifact_checkbox_spec.js
index fe2886d6c95..b0d1b70c198 100644
--- a/spec/frontend/artifacts_settings/components/keep_latest_artifact_checkbox_spec.js
+++ b/spec/frontend/artifacts_settings/components/keep_latest_artifact_checkbox_spec.js
@@ -120,23 +120,4 @@ describe('Keep latest artifact checkbox', () => {
expect(findCheckbox().attributes('disabled')).toBeUndefined();
});
});
-
- describe('when application keep latest artifact setting is disabled', () => {
- it('checkbox is disabled when application setting is disabled', async () => {
- createComponent({
- keepLatestArtifactApplicationQueryHandler: jest.fn().mockResolvedValue({
- data: {
- ciApplicationSettings: {
- keepLatestArtifact: false,
- },
- },
- }),
- });
-
- await wrapper.vm.$nextTick();
-
- expect(wrapper.element).toMatchSnapshot();
- expect(findCheckbox().attributes('disabled')).toBe('true');
- });
- });
});
diff --git a/spec/frontend/vue_shared/alert_details/alert_details_spec.js b/spec/frontend/vue_shared/alert_details/alert_details_spec.js
index dd9a7be6268..ce410a8b3e7 100644
--- a/spec/frontend/vue_shared/alert_details/alert_details_spec.js
+++ b/spec/frontend/vue_shared/alert_details/alert_details_spec.js
@@ -128,6 +128,10 @@ describe('AlertDetails', () => {
expect(wrapper.findByTestId('startTimeItem').exists()).toBe(true);
expect(wrapper.findByTestId('startTimeItem').props('time')).toBe(mockAlert.startedAt);
});
+
+ it('renders the metrics tab', () => {
+ expect(findMetricsTab().exists()).toBe(true);
+ });
});
describe('individual alert fields', () => {
@@ -179,7 +183,8 @@ describe('AlertDetails', () => {
describe('Threat Monitoring details', () => {
it('should not render the metrics tab', () => {
mountComponent({
- data: { alert: mockAlert, provide: { isThreatMonitoringPage: true } },
+ data: { alert: mockAlert },
+ provide: { isThreatMonitoringPage: true },
});
expect(findMetricsTab().exists()).toBe(false);
});
diff --git a/spec/lib/gitlab/current_settings_spec.rb b/spec/lib/gitlab/current_settings_spec.rb
index 01aceec12c5..f5cb1987c5c 100644
--- a/spec/lib/gitlab/current_settings_spec.rb
+++ b/spec/lib/gitlab/current_settings_spec.rb
@@ -24,6 +24,26 @@ RSpec.describe Gitlab::CurrentSettings do
end
end
+ describe '.signup_disabled?' do
+ subject { described_class.signup_disabled? }
+
+ context 'when signup is enabled' do
+ before do
+ create(:application_setting, signup_enabled: true)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when signup is disabled' do
+ before do
+ create(:application_setting, signup_enabled: false)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
describe '#current_application_settings', :use_clean_rails_memory_store_caching do
it 'allows keys to be called directly' do
db_settings = create(:application_setting,
diff --git a/spec/lib/gitlab/import_export/repo_restorer_spec.rb b/spec/lib/gitlab/import_export/repo_restorer_spec.rb
index fe43a23e242..718a23f80a1 100644
--- a/spec/lib/gitlab/import_export/repo_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/repo_restorer_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe Gitlab::ImportExport::RepoRestorer do
context 'when the repository already exists' do
it 'deletes the existing repository before importing' do
allow(project.repository).to receive(:exists?).and_return(true)
- allow(project.repository).to receive(:path).and_return('repository_path')
+ allow(project.repository).to receive(:disk_path).and_return('repository_path')
expect_next_instance_of(Repositories::DestroyService) do |instance|
expect(instance).to receive(:execute).and_call_original
diff --git a/spec/lib/object_storage/config_spec.rb b/spec/lib/object_storage/config_spec.rb
index 1361d80fe75..0ead2a1d269 100644
--- a/spec/lib/object_storage/config_spec.rb
+++ b/spec/lib/object_storage/config_spec.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
require 'rspec-parameterized'
+require 'fog/core'
RSpec.describe ObjectStorage::Config do
using RSpec::Parameterized::TableSyntax
@@ -33,9 +34,7 @@ RSpec.describe ObjectStorage::Config do
}
end
- subject do
- described_class.new(raw_config.as_json)
- end
+ subject { described_class.new(raw_config.as_json) }
describe '#load_provider' do
before do
@@ -46,10 +45,6 @@ RSpec.describe ObjectStorage::Config do
it 'registers AWS as a provider' do
expect(Fog.providers.keys).to include(:aws)
end
-
- describe '#fog_connection' do
- it { expect(subject.fog_connection).to be_a_kind_of(Fog::AWS::Storage::Real) }
- end
end
context 'with Google' do
@@ -64,10 +59,6 @@ RSpec.describe ObjectStorage::Config do
it 'registers Google as a provider' do
expect(Fog.providers.keys).to include(:google)
end
-
- describe '#fog_connection' do
- it { expect(subject.fog_connection).to be_a_kind_of(Fog::Storage::GoogleXML::Real) }
- end
end
context 'with Azure' do
@@ -82,10 +73,6 @@ RSpec.describe ObjectStorage::Config do
it 'registers AzureRM as a provider' do
expect(Fog.providers.keys).to include(:azurerm)
end
-
- describe '#fog_connection' do
- it { expect(subject.fog_connection).to be_a_kind_of(Fog::Storage::AzureRM::Real) }
- end
end
end
@@ -183,50 +170,6 @@ RSpec.describe ObjectStorage::Config do
it { expect(subject.provider).to eq('AWS') }
it { expect(subject.aws?).to be true }
it { expect(subject.google?).to be false }
-
- it 'returns the default S3 endpoint' do
- subject.load_provider
-
- expect(subject.s3_endpoint).to eq("https://test-bucket.s3.amazonaws.com")
- end
-
- describe 'with a custom endpoint' do
- let(:endpoint) { 'https://my.example.com' }
-
- before do
- credentials[:endpoint] = endpoint
- end
-
- it 'returns the custom endpoint' do
- subject.load_provider
-
- expect(subject.s3_endpoint).to eq(endpoint)
- end
- end
-
- context 'with custom S3 host and port' do
- where(:host, :port, :scheme, :expected) do
- 's3.example.com' | 8080 | nil | 'https://test-bucket.s3.example.com:8080'
- 's3.example.com' | 443 | nil | 'https://test-bucket.s3.example.com'
- 's3.example.com' | 443 | "https" | 'https://test-bucket.s3.example.com'
- 's3.example.com' | nil | nil | 'https://test-bucket.s3.example.com'
- 's3.example.com' | 80 | "http" | 'http://test-bucket.s3.example.com'
- 's3.example.com' | "bogus" | nil | nil
- end
-
- with_them do
- before do
- credentials[:host] = host
- credentials[:port] = port
- credentials[:scheme] = scheme
- subject.load_provider
- end
-
- it 'returns expected host' do
- expect(subject.s3_endpoint).to eq(expected)
- end
- end
- end
end
context 'with Google credentials' do
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 3a4de7ba279..84347ec2a51 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1949,8 +1949,8 @@ RSpec.describe Repository do
:root_ref,
:merged_branch_names,
:has_visible_content?,
- :issue_template_names_by_category,
- :merge_request_template_names_by_category,
+ :issue_template_names_hash,
+ :merge_request_template_names_hash,
:user_defined_metrics_dashboard_paths,
:xcode_project?,
:has_ambiguous_refs?
diff --git a/spec/requests/api/lint_spec.rb b/spec/requests/api/lint_spec.rb
index 2316e702c3e..b5bf697e9e3 100644
--- a/spec/requests/api/lint_spec.rb
+++ b/spec/requests/api/lint_spec.rb
@@ -5,7 +5,9 @@ require 'spec_helper'
RSpec.describe API::Lint do
describe 'POST /ci/lint' do
context 'when signup settings are disabled' do
- Gitlab::CurrentSettings.signup_enabled = false
+ before do
+ Gitlab::CurrentSettings.signup_enabled = false
+ end
context 'when unauthenticated' do
it 'returns authentication error' do
@@ -16,22 +18,25 @@ RSpec.describe API::Lint do
end
context 'when authenticated' do
- it 'returns unauthorized error' do
- post api('/ci/lint'), params: { content: 'content' }
+ let_it_be(:api_user) { create(:user) }
+ it 'returns authorized' do
+ post api('/ci/lint', api_user), params: { content: 'content' }
- expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(response).to have_gitlab_http_status(:ok)
end
end
end
context 'when signup settings are enabled' do
- Gitlab::CurrentSettings.signup_enabled = true
+ before do
+ Gitlab::CurrentSettings.signup_enabled = true
+ end
context 'when unauthenticated' do
- it 'returns authentication error' do
+ it 'returns authorized success' do
post api('/ci/lint'), params: { content: 'content' }
- expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(response).to have_gitlab_http_status(:ok)
end
end
diff --git a/spec/services/issues/export_csv_service_spec.rb b/spec/services/issues/export_csv_service_spec.rb
index fd1bcf82ccd..d199f825276 100644
--- a/spec/services/issues/export_csv_service_spec.rb
+++ b/spec/services/issues/export_csv_service_spec.rb
@@ -4,11 +4,11 @@ require 'spec_helper'
RSpec.describe Issues::ExportCsvService do
let_it_be(:user) { create(:user) }
- let(:group) { create(:group) }
- let(:project) { create(:project, :public, group: group) }
- let!(:issue) { create(:issue, project: project, author: user) }
- let!(:bad_issue) { create(:issue, project: project, author: user) }
- let(:subject) { described_class.new(Issue.all, project) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :public, group: group) }
+ let_it_be(:issue) { create(:issue, project: project, author: user) }
+ let_it_be(:bad_issue) { create(:issue, project: project, author: user) }
+ subject { described_class.new(Issue.all, project) }
it 'renders csv to string' do
expect(subject.csv_data).to be_a String
@@ -33,11 +33,11 @@ RSpec.describe Issues::ExportCsvService do
end
context 'includes' do
- let(:milestone) { create(:milestone, title: 'v1.0', project: project) }
- let(:idea_label) { create(:label, project: project, title: 'Idea') }
- let(:feature_label) { create(:label, project: project, title: 'Feature') }
+ let_it_be(:milestone) { create(:milestone, title: 'v1.0', project: project) }
+ let_it_be(:idea_label) { create(:label, project: project, title: 'Idea') }
+ let_it_be(:feature_label) { create(:label, project: project, title: 'Feature') }
- before do
+ before_all do
# Creating a timelog touches the updated_at timestamp of issue,
# so create these first.
issue.timelogs.create!(time_spent: 360, user: user)
@@ -60,6 +60,10 @@ RSpec.describe Issues::ExportCsvService do
expect(csv.headers).to include('Title', 'Description')
end
+ it 'returns two issues' do
+ expect(csv.count).to eq(2)
+ end
+
specify 'iid' do
expect(csv[0]['Issue ID']).to eq issue.iid.to_s
end
@@ -150,7 +154,7 @@ RSpec.describe Issues::ExportCsvService do
end
context 'with issues filtered by labels and project' do
- let(:subject) do
+ subject do
described_class.new(
IssuesFinder.new(user,
project_id: project.id,
@@ -162,6 +166,27 @@ RSpec.describe Issues::ExportCsvService do
expect(csv[0]['Issue ID']).to eq issue.iid.to_s
end
end
+
+ context 'with label links' do
+ let(:labeled_issues) { create_list(:labeled_issue, 2, project: project, author: user, labels: [feature_label, idea_label]) }
+
+ it 'does not run a query for each label link' do
+ control_count = ActiveRecord::QueryRecorder.new { csv }.count
+
+ labeled_issues
+
+ expect { csv }.not_to exceed_query_limit(control_count)
+ expect(csv.count).to eq(4)
+ end
+
+ it 'returns the labels in sorted order' do
+ labeled_issues
+
+ labeled_rows = csv.select { |entry| labeled_issues.map(&:iid).include?(entry['Issue ID'].to_i) }
+ expect(labeled_rows.count).to eq(2)
+ expect(labeled_rows.map { |entry| entry['Labels'] }).to all( eq("Feature,Idea") )
+ end
+ end
end
context 'with minimal details' do