Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-10-20 18:12:43 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-10-20 18:12:43 +0300
commitdd1388bcdb2383df8aecc8dd22f8ae6bdd8473cd (patch)
treecb625083cedff10d1d42971e40ec59db1e6c4ca6
parent3ce7340b2ae954b6b449bfba5720fa356b803c51 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue31
-rw-r--r--app/assets/javascripts/pipeline_editor/constants.js7
-rw-r--r--app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue2
-rw-r--r--app/assets/javascripts/projects/storage_counter/utils.js4
-rw-r--r--app/models/namespace.rb4
-rw-r--r--app/services/ci/job_artifacts/create_service.rb4
-rw-r--r--app/views/projects/pipelines/show.html.haml4
-rw-r--r--config/feature_flags/development/ci_synchronous_artifact_parsing.yml8
-rw-r--r--doc/integration/jira/connect-app.md93
-rw-r--r--lib/gitlab/ci/templates/Terraform.latest.gitlab-ci.yml7
-rw-r--r--lib/gitlab/ci/templates/Terraform/Base.latest.gitlab-ci.yml9
-rw-r--r--lib/gitlab/database/connection.rb27
-rw-r--r--lib/gitlab/database/load_balancing/load_balancer.rb29
-rw-r--r--locale/gitlab.pot6
-rwxr-xr-xscripts/review_apps/review-apps.sh7
-rw-r--r--spec/frontend/fixtures/projects.rb26
-rw-r--r--spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js58
-rw-r--r--spec/frontend/pipeline_editor/pipeline_editor_app_spec.js68
-rw-r--r--spec/frontend/pipeline_editor/pipeline_editor_home_spec.js1
-rw-r--r--spec/frontend/projects/storage_counter/mock_data.js27
-rw-r--r--spec/frontend/projects/storage_counter/utils_spec.js17
-rw-r--r--spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb2
-rw-r--r--spec/lib/gitlab/database/connection_spec.rb11
-rw-r--r--spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb11
-rw-r--r--spec/models/namespace_spec.rb18
-rw-r--r--spec/services/ci/job_artifacts/create_service_spec.rb12
26 files changed, 299 insertions, 194 deletions
diff --git a/app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue b/app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue
index f7c9f10ea46..07af2b848c2 100644
--- a/app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue
+++ b/app/assets/javascripts/pipeline_editor/components/pipeline_editor_tabs.vue
@@ -3,6 +3,7 @@ import { GlAlert, GlLoadingIcon, GlTabs } from '@gitlab/ui';
import { s__ } from '~/locale';
import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import { getParameterValues, setUrlParams, updateHistory } from '~/lib/utils/url_utility';
import {
CREATE_TAB,
EDITOR_APP_STATUS_EMPTY,
@@ -12,6 +13,8 @@ import {
EDITOR_APP_STATUS_VALID,
LINT_TAB,
MERGED_TAB,
+ TAB_QUERY_PARAM,
+ TABS_INDEX,
VISUALIZE_TAB,
} from '../constants';
import getAppStatus from '../graphql/queries/client/app_status.graphql';
@@ -42,6 +45,9 @@ export default {
errorTexts: {
loadMergedYaml: s__('Pipelines|Could not load merged YAML content'),
},
+ query: {
+ TAB_QUERY_PARAM,
+ },
tabConstants: {
CREATE_TAB,
LINT_TAB,
@@ -98,15 +104,38 @@ export default {
return this.appStatus === EDITOR_APP_STATUS_LOADING;
},
},
+ created() {
+ const [tabQueryParam] = getParameterValues(TAB_QUERY_PARAM);
+
+ if (tabQueryParam && TABS_INDEX[tabQueryParam]) {
+ this.setDefaultTab(tabQueryParam);
+ }
+ },
methods: {
setCurrentTab(tabName) {
this.$emit('set-current-tab', tabName);
},
+ setDefaultTab(tabName) {
+ // We associate tab name with the index so that we can use tab name
+ // in other part of the app and load the corresponding tab closer to the
+ // actual component using a hash that binds the name to the indexes.
+ // This also means that if we ever changed tab order, we would justs need to
+ // update `TABS_INDEX` hash instead of all the instances in the app
+ // where we used the individual indexes
+ const newUrl = setUrlParams({ [TAB_QUERY_PARAM]: TABS_INDEX[tabName] });
+
+ this.setCurrentTab(tabName);
+ updateHistory({ url: newUrl, title: document.title, replace: true });
+ },
},
};
</script>
<template>
- <gl-tabs class="file-editor gl-mb-3">
+ <gl-tabs
+ class="file-editor gl-mb-3"
+ :query-param-name="$options.query.TAB_QUERY_PARAM"
+ sync-active-tab-with-query-params
+ >
<editor-tab
class="gl-mb-3"
:title="$options.i18n.tabEdit"
diff --git a/app/assets/javascripts/pipeline_editor/constants.js b/app/assets/javascripts/pipeline_editor/constants.js
index bb03fa126a5..89bf9acaf3d 100644
--- a/app/assets/javascripts/pipeline_editor/constants.js
+++ b/app/assets/javascripts/pipeline_editor/constants.js
@@ -22,7 +22,14 @@ export const LINT_TAB = 'LINT_TAB';
export const MERGED_TAB = 'MERGED_TAB';
export const VISUALIZE_TAB = 'VISUALIZE_TAB';
+export const TABS_INDEX = {
+ [CREATE_TAB]: '0',
+ [VISUALIZE_TAB]: '1',
+ [LINT_TAB]: '2',
+ [MERGED_TAB]: '3',
+};
export const TABS_WITH_COMMIT_FORM = [CREATE_TAB, LINT_TAB, VISUALIZE_TAB];
+export const TAB_QUERY_PARAM = 'tab';
export const COMMIT_ACTION_CREATE = 'CREATE';
export const COMMIT_ACTION_UPDATE = 'UPDATE';
diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue b/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue
index ba567023946..ae98b72a3ca 100644
--- a/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue
+++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_home.vue
@@ -4,7 +4,7 @@ import PipelineEditorDrawer from './components/drawer/pipeline_editor_drawer.vue
import PipelineEditorFileNav from './components/file_nav/pipeline_editor_file_nav.vue';
import PipelineEditorHeader from './components/header/pipeline_editor_header.vue';
import PipelineEditorTabs from './components/pipeline_editor_tabs.vue';
-import { TABS_WITH_COMMIT_FORM, CREATE_TAB } from './constants';
+import { CREATE_TAB, TABS_WITH_COMMIT_FORM } from './constants';
export default {
components: {
diff --git a/app/assets/javascripts/projects/storage_counter/utils.js b/app/assets/javascripts/projects/storage_counter/utils.js
index cb26603fff5..9fca9d88f46 100644
--- a/app/assets/javascripts/projects/storage_counter/utils.js
+++ b/app/assets/javascripts/projects/storage_counter/utils.js
@@ -14,10 +14,6 @@ export const parseGetProjectStorageResults = (data, helpLinks) => {
}
const { storageSize, ...storageStatistics } = projectStatistics;
const storageTypes = PROJECT_STORAGE_TYPES.reduce((types, currentType) => {
- if (!storageStatistics[currentType.id]) {
- return types;
- }
-
const helpPathKey = currentType.id.replace(`Size`, `HelpPagePath`);
const helpPath = helpLinks[helpPathKey];
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index bf005addf0b..79af57a6ff1 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -52,7 +52,7 @@ class Namespace < ApplicationRecord
belongs_to :owner, class_name: "User"
belongs_to :parent, class_name: "Namespace"
- has_many :children, class_name: "Namespace", foreign_key: :parent_id
+ has_many :children, -> { where(type: Group.sti_name) }, class_name: "Namespace", foreign_key: :parent_id
has_many :custom_emoji, inverse_of: :namespace
has_one :chat_team, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_one :root_storage_statistics, class_name: 'Namespace::RootStorageStatistics'
@@ -566,7 +566,7 @@ class Namespace < ApplicationRecord
end
if user_namespace?
- errors.add(:parent_id, _('cannot not be used for user namespace'))
+ errors.add(:parent_id, _('cannot be used for user namespace'))
elsif group_namespace?
errors.add(:parent_id, _('user namespace cannot be the parent of another namespace')) if parent.user_namespace?
end
diff --git a/app/services/ci/job_artifacts/create_service.rb b/app/services/ci/job_artifacts/create_service.rb
index 9fc7c3b4d40..50902d86070 100644
--- a/app/services/ci/job_artifacts/create_service.rb
+++ b/app/services/ci/job_artifacts/create_service.rb
@@ -109,10 +109,6 @@ module Ci
end
def parse_artifact(artifact)
- unless Feature.enabled?(:ci_synchronous_artifact_parsing, project, default_enabled: true)
- return success
- end
-
case artifact.file_type
when 'dotenv' then parse_dotenv_artifact(artifact)
else success
diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml
index 8fd8d3cf540..c911fc8a203 100644
--- a/app/views/projects/pipelines/show.html.haml
+++ b/app/views/projects/pipelines/show.html.haml
@@ -22,8 +22,8 @@
%ul
- @pipeline.yaml_errors.split(",").each do |error|
%li= error
- - lint_link_url = project_ci_lint_path(@project)
- - lint_link_start = '<a href="%{url}">'.html_safe % { url: lint_link_url }
+ - lint_link_url = project_ci_pipeline_editor_path(@project, tab: "LINT_TAB")
+ - lint_link_start = '<a href="%{url}" class="gl-text-blue-500!">'.html_safe % { url: lint_link_url }
= s_('You can also test your %{gitlab_ci_yml} in %{lint_link_start}CI Lint%{lint_link_end}').html_safe % { gitlab_ci_yml: '.gitlab-ci.yml', lint_link_start: lint_link_start, lint_link_end: '</a>'.html_safe }
= render "projects/pipelines/with_tabs", pipeline: @pipeline, stages: @stages, pipeline_has_errors: pipeline_has_errors
diff --git a/config/feature_flags/development/ci_synchronous_artifact_parsing.yml b/config/feature_flags/development/ci_synchronous_artifact_parsing.yml
deleted file mode 100644
index ab3a35e409b..00000000000
--- a/config/feature_flags/development/ci_synchronous_artifact_parsing.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_synchronous_artifact_parsing
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26247
-rollout_issue_url:
-milestone: '12.9'
-type: development
-group: group::release
-default_enabled: true
diff --git a/doc/integration/jira/connect-app.md b/doc/integration/jira/connect-app.md
index d274710b3cd..bdffde4df60 100644
--- a/doc/integration/jira/connect-app.md
+++ b/doc/integration/jira/connect-app.md
@@ -6,34 +6,39 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# GitLab.com for Jira Cloud app **(FREE)**
+You can integrate GitLab and Jira Cloud using the
+[GitLab.com for Jira Cloud](https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud)
+app in the Atlassian Marketplace.
+
NOTE:
-Only Jira users with administrator level access are able to install or configure
+Only Jira users with the administrator role can install or configure
the GitLab.com for Jira Cloud app.
-## GitLab.com for Jira Cloud app **(FREE SAAS)**
+## Install the GitLab.com for Jira Cloud app **(FREE SAAS)**
-You can integrate GitLab.com and Jira Cloud using the
-[GitLab.com for Jira Cloud](https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud)
-app in the Atlassian Marketplace. The user configuring GitLab.com for Jira Cloud app must have
-[Maintainer](../../user/permissions.md) permissions in the GitLab.com namespace.
+If you use GitLab.com and Jira Cloud, you can install the GitLab.com for Jira Cloud app.
+If you do not use both of these environments, use the [Jira DVCS Connector](dvcs.md) or
+[install GitLab.com for Jira Cloud app for self-managed instances](#install-the-gitlabcom-for-jira-cloud-app-for-self-managed-instances).
+We recommend the GitLab.com for Jira Cloud app, because data is
+synchronized in real time. The DVCS connector updates data only once per hour.
-This integration method supports [smart commits](dvcs.md#smart-commits).
+The user configuring the GitLab.com for Jira Cloud app must have
+at least the [Maintainer](../../user/permissions.md) role the GitLab.com namespace.
-This method is recommended when using GitLab.com and Jira Cloud because data is
-synchronized in real-time. The DVCS connector updates data only once per hour.
-If you are not using both of these environments, use the [Jira DVCS Connector](dvcs.md) method or
-[steps to install GitLab.com for Jira Cloud app for self-managed instances](#install-the-gitlabcom-for-jira-cloud-app-for-self-managed-instances).
+This integration method supports [Smart Commits](dvcs.md#smart-commits).
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For a walkthrough of the integration with GitLab.com for Jira Cloud app, watch
[Configure GitLab.com Jira Could Integration using Marketplace App](https://youtu.be/SwR-g1s1zTo) on YouTube.
-1. Go to **Jira Settings > Apps > Find new apps**, then search for GitLab.
-1. Click **GitLab.com for Jira Cloud**, then click **Get it now**, or go to the
+To install the GitLab.com for Jira Cloud app:
+
+1. In Jira, go to **Jira Settings > Apps > Find new apps**, then search for GitLab.
+1. Select **GitLab.com for Jira Cloud**, then select **Get it now**, or go to the
[App in the marketplace directly](https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud).
![Install GitLab.com app on Jira Cloud](img/jira_dev_panel_setup_com_1.png)
-1. After installing, click **Get started** to go to the configurations page.
+1. After installing, to go to the configurations page, select **Get started**.
This page is always available under **Jira Settings > Apps > Manage apps**.
![Start GitLab.com app configuration on Jira Cloud](img/jira_dev_panel_setup_com_2.png)
@@ -41,7 +46,7 @@ For a walkthrough of the integration with GitLab.com for Jira Cloud app, watch
[Maintainer](../../user/permissions.md) permissions to add namespaces.
![Sign in to GitLab.com in GitLab.com for Jira Cloud app](img/jira_dev_panel_setup_com_3_v13_9.png)
-1. Select **Add namespace** to open the list of available namespaces.
+1. To open the list of available namespaces, select **Add namespace**.
1. Identify the namespace you want to link, and select **Link**. Only Jira site
administrators are permitted to add or remove namespaces for an installation.
@@ -89,30 +94,30 @@ from outside the Marketplace, which allows you to install the application:
1. Sign in to your Jira instance as a user with an Administrator role.
1. Place your Jira instance into
[development mode](https://developer.atlassian.com/cloud/jira/platform/getting-started-with-connect/#step-2--enable-development-mode).
-1. Sign in to your GitLab application as a user with an [Administrator](../../user/permissions.md) role.
+1. Sign in to your GitLab application as an [administrator](../../user/permissions.md).
1. Install the GitLab application from your self-managed GitLab instance, as
described in the [Atlassian developer guides](https://developer.atlassian.com/cloud/jira/platform/getting-started-with-connect/#step-3--install-and-test-your-app):
- 1. In your Jira instance, go to **Apps > Manage Apps** and click **Upload app**:
+ 1. In your Jira instance, go to **Apps > Manage Apps** and select **Upload app**:
- ![Image showing button labeled "upload app"](img/jira-upload-app_v13_11.png)
+ ![Button labeled "upload app"](img/jira-upload-app_v13_11.png)
- 1. For **App descriptor URL**, provide full URL to your manifest file, modifying this
- URL based on your instance configuration: `https://your.domain/your-path/-/jira_connect/app_descriptor.json`
- 1. Click **Upload**, and Jira fetches the content of your `app_descriptor` file and installs
- it for you.
+ 1. For **App descriptor URL**, provide the full URL to your manifest file, based
+ on your instance configuration. For example: `https://your.domain/your-path/-/jira_connect/app_descriptor.json`.
+ 1. Select **Upload**. Jira fetches the content of your `app_descriptor` file and installs
+ it.
1. If the upload is successful, Jira displays a modal panel: **Installed and ready to go!**
- Click **Get started** to configure the integration.
+ To configure the integration, select **Get started**.
- ![Image showing success modal](img/jira-upload-app-success_v13_11.png)
+ ![Success modal](img/jira-upload-app-success_v13_11.png)
1. Disable [development mode](https://developer.atlassian.com/cloud/jira/platform/getting-started-with-connect/#step-2--enable-development-mode) on your Jira instance.
The **GitLab.com for Jira Cloud** app now displays under **Manage apps**. You can also
-click **Get started** to open the configuration page rendered from your GitLab instance.
+select **Get started** to open the configuration page rendered from your GitLab instance.
NOTE:
-If a GitLab update makes changes to the application descriptor, you must uninstall, then reinstall, the
-application.
+If a GitLab update makes changes to the application descriptor, you must uninstall,
+then reinstall the application.
### Create a Marketplace listing
@@ -120,31 +125,33 @@ If you prefer to not use development mode on your Jira instance, you can create
your own Marketplace listing for your instance. This enables your application
to be installed from the Atlassian Marketplace.
-For full instructions, review the Atlassian [guide to creating a marketplace listing](https://developer.atlassian.com/platform/marketplace/installing-cloud-apps/#creating-the-marketplace-listing). To create a
-Marketplace listing, you must:
+For full instructions, review the Atlassian [guide to creating a marketplace listing](https://developer.atlassian.com/platform/marketplace/installing-cloud-apps/#creating-the-marketplace-listing).
+To create a Marketplace listing:
1. Register as a Marketplace vendor.
-1. List your application, using the application descriptor URL.
+1. List your application using the application descriptor URL.
- Your manifest file is located at: `https://your.domain/your-path/-/jira_connect/app_descriptor.json`
- - GitLab recommends you list your application as `private`, because public
+ - We recommend you list your application as `private`, because public
applications can be viewed and installed by any user.
1. Generate test license tokens for your application.
-Review the
-[official Atlassian documentation](https://developer.atlassian.com/platform/marketplace/installing-cloud-apps/#creating-the-marketplace-listing)
-for details.
-
NOTE:
-Using this method, [updates are automated](#update-the-gitlabcom-for-jira-cloud-app)
-the same way as when using our GitLab.com Marketplace listing.
+This method uses [automated updates](#update-the-gitlabcom-for-jira-cloud-app)
+the same way as our GitLab.com Marketplace listing.
## Troubleshoot GitLab.com for Jira Cloud app
-The GitLab.com for Jira Cloud app uses an iframe to add namespaces on the
-settings page. Some browsers block cross-site cookies, which can lead to a
-message saying that the user needs to log in on GitLab.com even though the user
-is already logged in.
+### Browser displays sign-in message when already signed in
+
+You might get the following message prompting you to sign in to GitLab.com
+when you're already signed in:
+
+```plaintext
+You need to sign in or sign up before continuing.
+```
-> "You need to sign in or sign up before continuing."
+GitLab.com for Jira Cloud app uses an iframe to add namespaces on the
+settings page. Some browsers block cross-site cookies, which can lead to this issue.
-In this case, use [Firefox](https://www.mozilla.org/en-US/firefox/), [Google Chrome](https://www.google.com/chrome/), or enable cross-site cookies in your browser.
+To resolve this issue, use [Firefox](https://www.mozilla.org/en-US/firefox/),
+[Google Chrome](https://www.google.com/chrome/), or enable cross-site cookies in your browser.
diff --git a/lib/gitlab/ci/templates/Terraform.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Terraform.latest.gitlab-ci.yml
index 081a3a6cc78..e554742735c 100644
--- a/lib/gitlab/ci/templates/Terraform.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Terraform.latest.gitlab-ci.yml
@@ -7,20 +7,17 @@ include:
- template: Terraform/Base.latest.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Terraform/Base.latest.gitlab-ci.yml
stages:
- - init
- validate
- build
- deploy
- - cleanup
-
-init:
- extends: .terraform:init
fmt:
extends: .terraform:fmt
+ needs: []
validate:
extends: .terraform:validate
+ needs: []
build:
extends: .terraform:build
diff --git a/lib/gitlab/ci/templates/Terraform/Base.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Terraform/Base.latest.gitlab-ci.yml
index 3a70e6bc4b8..47cb043f2ba 100644
--- a/lib/gitlab/ci/templates/Terraform/Base.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Terraform/Base.latest.gitlab-ci.yml
@@ -21,18 +21,11 @@ cache:
paths:
- ${TF_ROOT}/.terraform/
-.terraform:init: &terraform_init
- stage: init
- script:
- - cd ${TF_ROOT}
- - gitlab-terraform init
-
.terraform:fmt: &terraform_fmt
stage: validate
- needs: []
script:
- cd ${TF_ROOT}
- - gitlab-terraform fmt -check -recursive
+ - gitlab-terraform fmt
allow_failure: true
.terraform:validate: &terraform_validate
diff --git a/lib/gitlab/database/connection.rb b/lib/gitlab/database/connection.rb
index cda6220ee6c..061ac6b975a 100644
--- a/lib/gitlab/database/connection.rb
+++ b/lib/gitlab/database/connection.rb
@@ -200,33 +200,6 @@ module Gitlab
row['result'] if row
end
- # @param [ActiveRecord::Connection] ar_connection
- # @return [String]
- def get_write_location(ar_connection)
- use_new_load_balancer_query = Gitlab::Utils
- .to_boolean(ENV['USE_NEW_LOAD_BALANCER_QUERY'], default: true)
-
- sql =
- if use_new_load_balancer_query
- <<~NEWSQL
- SELECT CASE
- WHEN pg_is_in_recovery() = true AND EXISTS (SELECT 1 FROM pg_stat_get_wal_senders())
- THEN pg_last_wal_replay_lsn()::text
- WHEN pg_is_in_recovery() = false
- THEN pg_current_wal_insert_lsn()::text
- ELSE NULL
- END AS location;
- NEWSQL
- else
- <<~SQL
- SELECT pg_current_wal_insert_lsn()::text AS location
- SQL
- end
-
- row = ar_connection.select_all(sql).first
- row['location'] if row
- end
-
# inside_transaction? will return true if the caller is running within a
# transaction. Handles special cases when running inside a test
# environment, where tests may be wrapped in transactions
diff --git a/lib/gitlab/database/load_balancing/load_balancer.rb b/lib/gitlab/database/load_balancing/load_balancer.rb
index cc9ca325337..aee9acf3e9b 100644
--- a/lib/gitlab/database/load_balancing/load_balancer.rb
+++ b/lib/gitlab/database/load_balancing/load_balancer.rb
@@ -133,7 +133,7 @@ module Gitlab
# Returns the transaction write location of the primary.
def primary_write_location
location = read_write do |connection|
- ::Gitlab::Database.main.get_write_location(connection)
+ get_write_location(connection)
end
return location if location
@@ -268,6 +268,33 @@ module Gitlab
base = SafeRequestStore[:gitlab_load_balancer] ||= {}
base[self] ||= {}
end
+
+ # @param [ActiveRecord::Connection] ar_connection
+ # @return [String]
+ def get_write_location(ar_connection)
+ use_new_load_balancer_query = Gitlab::Utils
+ .to_boolean(ENV['USE_NEW_LOAD_BALANCER_QUERY'], default: true)
+
+ sql =
+ if use_new_load_balancer_query
+ <<~NEWSQL
+ SELECT CASE
+ WHEN pg_is_in_recovery() = true AND EXISTS (SELECT 1 FROM pg_stat_get_wal_senders())
+ THEN pg_last_wal_replay_lsn()::text
+ WHEN pg_is_in_recovery() = false
+ THEN pg_current_wal_insert_lsn()::text
+ ELSE NULL
+ END AS location;
+ NEWSQL
+ else
+ <<~SQL
+ SELECT pg_current_wal_insert_lsn()::text AS location
+ SQL
+ end
+
+ row = ar_connection.select_all(sql).first
+ row['location'] if row
+ end
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 7981c07fd77..053ba7b951c 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -39886,6 +39886,9 @@ msgstr ""
msgid "cannot be modified"
msgstr ""
+msgid "cannot be used for user namespace"
+msgstr ""
+
msgid "cannot block others"
msgstr ""
@@ -39901,9 +39904,6 @@ msgstr ""
msgid "cannot merge"
msgstr ""
-msgid "cannot not be used for user namespace"
-msgstr ""
-
msgid "ciReport|%{degradedNum} degraded"
msgstr ""
diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh
index 8ec26e7ba89..edb55a83555 100755
--- a/scripts/review_apps/review-apps.sh
+++ b/scripts/review_apps/review-apps.sh
@@ -10,7 +10,12 @@ function deploy_exists() {
helm status --namespace "${namespace}" "${release}" >/dev/null 2>&1
deploy_exists=$?
- echoinfo "Deployment status for ${release} is ${deploy_exists}"
+ if [ $deploy_exists -eq 0 ]; then
+ echoinfo "Previous deployment for ${release} found."
+ else
+ echoerr "Previous deployment for ${release} NOT found."
+ fi
+
return $deploy_exists
}
diff --git a/spec/frontend/fixtures/projects.rb b/spec/frontend/fixtures/projects.rb
index 3c8964d398a..23c18c97df2 100644
--- a/spec/frontend/fixtures/projects.rb
+++ b/spec/frontend/fixtures/projects.rb
@@ -65,5 +65,31 @@ RSpec.describe 'Projects (JavaScript fixtures)', type: :controller do
expect_graphql_errors_to_be_empty
end
end
+
+ context 'project storage count query' do
+ before do
+ project.statistics.update!(
+ repository_size: 3900000,
+ lfs_objects_size: 4800000,
+ build_artifacts_size: 400000,
+ pipeline_artifacts_size: 400000,
+ wiki_size: 300000,
+ packages_size: 3800000,
+ uploads_size: 900000
+ )
+ end
+
+ base_input_path = 'projects/storage_counter/queries/'
+ base_output_path = 'graphql/projects/storage_counter/'
+ query_name = 'project_storage.query.graphql'
+
+ it "#{base_output_path}#{query_name}.json" do
+ query = get_graphql_query_as_string("#{base_input_path}#{query_name}")
+
+ post_graphql(query, current_user: user, variables: { fullPath: project.full_path })
+
+ expect_graphql_errors_to_be_empty
+ end
+ end
end
end
diff --git a/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js b/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js
index 5cf8d47bc23..87301f6b7cb 100644
--- a/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js
+++ b/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js
@@ -1,16 +1,21 @@
-import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
+import { GlAlert, GlLoadingIcon, GlTabs } from '@gitlab/ui';
import { shallowMount, mount } from '@vue/test-utils';
import { nextTick } from 'vue';
+import setWindowLocation from 'helpers/set_window_location_helper';
import CiConfigMergedPreview from '~/pipeline_editor/components/editor/ci_config_merged_preview.vue';
import CiLint from '~/pipeline_editor/components/lint/ci_lint.vue';
import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue';
import EditorTab from '~/pipeline_editor/components/ui/editor_tab.vue';
import {
+ CREATE_TAB,
EDITOR_APP_STATUS_EMPTY,
EDITOR_APP_STATUS_ERROR,
EDITOR_APP_STATUS_LOADING,
EDITOR_APP_STATUS_INVALID,
EDITOR_APP_STATUS_VALID,
+ MERGED_TAB,
+ TAB_QUERY_PARAM,
+ TABS_INDEX,
} from '~/pipeline_editor/constants';
import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
import { mockLintResponse, mockCiYml } from '../mock_data';
@@ -53,6 +58,7 @@ describe('Pipeline editor tabs component', () => {
const findAlert = () => wrapper.findComponent(GlAlert);
const findCiLint = () => wrapper.findComponent(CiLint);
+ const findGlTabs = () => wrapper.findComponent(GlTabs);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findPipelineGraph = () => wrapper.findComponent(PipelineGraph);
const findTextEditor = () => wrapper.findComponent(MockTextEditor);
@@ -181,4 +187,54 @@ describe('Pipeline editor tabs component', () => {
},
);
});
+
+ describe('default tab based on url query param', () => {
+ const gitlabUrl = 'https://gitlab.test/ci/editor/';
+ const matchObject = {
+ hostname: 'gitlab.test',
+ pathname: '/ci/editor/',
+ search: '',
+ };
+
+ it(`is ${CREATE_TAB} if the query param ${TAB_QUERY_PARAM} is not present`, () => {
+ setWindowLocation(gitlabUrl);
+ createComponent();
+
+ expect(window.location).toMatchObject(matchObject);
+ });
+
+ it(`is ${CREATE_TAB} tab if the query param ${TAB_QUERY_PARAM} is invalid`, () => {
+ const queryValue = 'FOO';
+ setWindowLocation(`${gitlabUrl}?${TAB_QUERY_PARAM}=${queryValue}`);
+ createComponent();
+
+ // If the query param remains unchanged, then we have ignored it.
+ expect(window.location).toMatchObject({
+ ...matchObject,
+ search: `?${TAB_QUERY_PARAM}=${queryValue}`,
+ });
+ });
+
+ it('is the tab specified in query param and transform it into an index value', async () => {
+ setWindowLocation(`${gitlabUrl}?${TAB_QUERY_PARAM}=${MERGED_TAB}`);
+ createComponent();
+
+ // If the query param has changed to an index, it means we have synced the
+ // query with.
+ expect(window.location).toMatchObject({
+ ...matchObject,
+ search: `?${TAB_QUERY_PARAM}=${TABS_INDEX[MERGED_TAB]}`,
+ });
+ });
+ });
+
+ describe('glTabs', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('passes the `sync-active-tab-with-query-params` prop', () => {
+ expect(findGlTabs().props('syncActiveTabWithQueryParams')).toBe(true);
+ });
+ });
});
diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
index b6713319e69..26a31b2fa84 100644
--- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
+++ b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
@@ -1,11 +1,9 @@
-import { GlAlert, GlButton, GlLoadingIcon, GlTabs } from '@gitlab/ui';
+import { GlAlert, GlButton, GlLoadingIcon } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import setWindowLocation from 'helpers/set_window_location_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import CommitForm from '~/pipeline_editor/components/commit/commit_form.vue';
-import TextEditor from '~/pipeline_editor/components/editor/text_editor.vue';
import PipelineEditorTabs from '~/pipeline_editor/components/pipeline_editor_tabs.vue';
import PipelineEditorEmptyState from '~/pipeline_editor/components/ui/pipeline_editor_empty_state.vue';
@@ -35,10 +33,6 @@ import {
const localVue = createLocalVue();
localVue.use(VueApollo);
-const MockSourceEditor = {
- template: '<div/>',
-};
-
const mockProvide = {
ciConfigPath: mockCiConfigPath,
defaultBranch: mockDefaultBranch,
@@ -55,19 +49,15 @@ describe('Pipeline editor app component', () => {
let mockLatestCommitShaQuery;
let mockPipelineQuery;
- const createComponent = ({ blobLoading = false, options = {}, provide = {} } = {}) => {
+ const createComponent = ({
+ blobLoading = false,
+ options = {},
+ provide = {},
+ stubs = {},
+ } = {}) => {
wrapper = shallowMount(PipelineEditorApp, {
provide: { ...mockProvide, ...provide },
- stubs: {
- GlTabs,
- GlButton,
- CommitForm,
- PipelineEditorHome,
- PipelineEditorTabs,
- PipelineEditorMessages,
- SourceEditor: MockSourceEditor,
- PipelineEditorEmptyState,
- },
+ stubs,
data() {
return {
commitSha: '',
@@ -89,7 +79,7 @@ describe('Pipeline editor app component', () => {
});
};
- const createComponentWithApollo = async ({ props = {}, provide = {} } = {}) => {
+ const createComponentWithApollo = async ({ props = {}, provide = {}, stubs = {} } = {}) => {
const handlers = [
[getBlobContent, mockBlobContentData],
[getCiConfigData, mockCiConfigData],
@@ -111,7 +101,7 @@ describe('Pipeline editor app component', () => {
apolloProvider: mockApollo,
};
- createComponent({ props, provide, options });
+ createComponent({ props, provide, stubs, options });
return waitForPromises();
};
@@ -119,7 +109,6 @@ describe('Pipeline editor app component', () => {
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findAlert = () => wrapper.findComponent(GlAlert);
const findEditorHome = () => wrapper.findComponent(PipelineEditorHome);
- const findTextEditor = () => wrapper.findComponent(TextEditor);
const findEmptyState = () => wrapper.findComponent(PipelineEditorEmptyState);
const findEmptyStateButton = () =>
wrapper.findComponent(PipelineEditorEmptyState).findComponent(GlButton);
@@ -141,7 +130,7 @@ describe('Pipeline editor app component', () => {
createComponent({ blobLoading: true });
expect(findLoadingIcon().exists()).toBe(true);
- expect(findTextEditor().exists()).toBe(false);
+ expect(findEditorHome().exists()).toBe(false);
});
});
@@ -185,7 +174,11 @@ describe('Pipeline editor app component', () => {
describe('when no CI config file exists', () => {
beforeEach(async () => {
mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponseNoCiFile);
- await createComponentWithApollo();
+ await createComponentWithApollo({
+ stubs: {
+ PipelineEditorEmptyState,
+ },
+ });
jest
.spyOn(wrapper.vm.$apollo.queries.commitSha, 'startPolling')
@@ -207,7 +200,11 @@ describe('Pipeline editor app component', () => {
const loadUnknownFailureText = 'The CI configuration was not loaded, please try again.';
mockBlobContentData.mockRejectedValueOnce(new Error('My error!'));
- await createComponentWithApollo();
+ await createComponentWithApollo({
+ stubs: {
+ PipelineEditorMessages,
+ },
+ });
expect(findEmptyState().exists()).toBe(false);
@@ -222,15 +219,20 @@ describe('Pipeline editor app component', () => {
mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponseNoCiFile);
mockLatestCommitShaQuery.mockResolvedValue(mockEmptyCommitShaResults);
- await createComponentWithApollo();
+ await createComponentWithApollo({
+ stubs: {
+ PipelineEditorHome,
+ PipelineEditorEmptyState,
+ },
+ });
expect(findEmptyState().exists()).toBe(true);
- expect(findTextEditor().exists()).toBe(false);
+ expect(findEditorHome().exists()).toBe(false);
await findEmptyStateButton().vm.$emit('click');
expect(findEmptyState().exists()).toBe(false);
- expect(findTextEditor().exists()).toBe(true);
+ expect(findEditorHome().exists()).toBe(true);
});
});
@@ -241,7 +243,7 @@ describe('Pipeline editor app component', () => {
describe('and the commit mutation succeeds', () => {
beforeEach(async () => {
window.scrollTo = jest.fn();
- await createComponentWithApollo();
+ await createComponentWithApollo({ stubs: { PipelineEditorMessages } });
findEditorHome().vm.$emit('commit', { type: COMMIT_SUCCESS });
});
@@ -295,7 +297,7 @@ describe('Pipeline editor app component', () => {
beforeEach(async () => {
window.scrollTo = jest.fn();
- await createComponentWithApollo();
+ await createComponentWithApollo({ stubs: { PipelineEditorMessages } });
findEditorHome().vm.$emit('showError', {
type: COMMIT_FAILURE,
@@ -319,7 +321,7 @@ describe('Pipeline editor app component', () => {
beforeEach(async () => {
window.scrollTo = jest.fn();
- await createComponentWithApollo();
+ await createComponentWithApollo({ stubs: { PipelineEditorMessages } });
findEditorHome().vm.$emit('showError', {
type: COMMIT_FAILURE,
@@ -386,7 +388,9 @@ describe('Pipeline editor app component', () => {
});
it('renders the given template', async () => {
- await createComponentWithApollo();
+ await createComponentWithApollo({
+ stubs: { PipelineEditorHome, PipelineEditorTabs },
+ });
expect(mockGetTemplate).toHaveBeenCalledWith({
projectPath: mockProjectFullPath,
@@ -394,7 +398,7 @@ describe('Pipeline editor app component', () => {
});
expect(findEmptyState().exists()).toBe(false);
- expect(findTextEditor().exists()).toBe(true);
+ expect(findEditorHome().exists()).toBe(true);
});
});
});
diff --git a/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js
index 335049892ec..8d5ff90bf1d 100644
--- a/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js
+++ b/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js
@@ -39,7 +39,6 @@ describe('Pipeline editor home wrapper', () => {
afterEach(() => {
wrapper.destroy();
- wrapper = null;
});
describe('renders', () => {
diff --git a/spec/frontend/projects/storage_counter/mock_data.js b/spec/frontend/projects/storage_counter/mock_data.js
index b9fa68b3ec7..d6630b355c5 100644
--- a/spec/frontend/projects/storage_counter/mock_data.js
+++ b/spec/frontend/projects/storage_counter/mock_data.js
@@ -1,23 +1,6 @@
-export const mockGetProjectStorageCountGraphQLResponse = {
- data: {
- project: {
- id: 'gid://gitlab/Project/20',
- statistics: {
- buildArtifactsSize: 400000.0,
- pipelineArtifactsSize: 25000.0,
- lfsObjectsSize: 4800000.0,
- packagesSize: 3800000.0,
- repositorySize: 3900000.0,
- snippetsSize: 1200000.0,
- storageSize: 15300000.0,
- uploadsSize: 900000.0,
- wikiSize: 300000.0,
- __typename: 'ProjectStatistics',
- },
- __typename: 'Project',
- },
- },
-};
+import mockGetProjectStorageCountGraphQLResponse from 'test_fixtures/graphql/projects/storage_counter/project_storage.query.graphql.json';
+
+export { mockGetProjectStorageCountGraphQLResponse };
export const mockEmptyResponse = { data: { project: null } };
@@ -37,7 +20,7 @@ export const defaultProvideValues = {
export const projectData = {
storage: {
- totalUsage: '14.6 MiB',
+ totalUsage: '13.8 MiB',
storageTypes: [
{
storageType: {
@@ -84,7 +67,7 @@ export const projectData = {
description: 'Shared bits of code and text.',
helpPath: '/snippets',
},
- value: 1200000,
+ value: 0,
},
{
storageType: {
diff --git a/spec/frontend/projects/storage_counter/utils_spec.js b/spec/frontend/projects/storage_counter/utils_spec.js
index 57c755266a0..fb91975a3cf 100644
--- a/spec/frontend/projects/storage_counter/utils_spec.js
+++ b/spec/frontend/projects/storage_counter/utils_spec.js
@@ -14,4 +14,21 @@ describe('parseGetProjectStorageResults', () => {
),
).toMatchObject(projectData);
});
+
+ it('includes storage type with size of 0 in returned value', () => {
+ const mockedResponse = mockGetProjectStorageCountGraphQLResponse.data;
+ // ensuring a specific storage type item has size of 0
+ mockedResponse.project.statistics.repositorySize = 0;
+
+ const response = parseGetProjectStorageResults(mockedResponse, defaultProvideValues.helpLinks);
+
+ expect(response.storage.storageTypes).toEqual(
+ expect.arrayContaining([
+ {
+ storageType: expect.any(Object),
+ value: 0,
+ },
+ ]),
+ );
+ });
});
diff --git a/spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb
index 3d1306e82a5..fd5d5d6af7f 100644
--- a/spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb
+++ b/spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe 'Terraform.latest.gitlab-ci.yml' do
context 'on master branch' do
it 'creates init, validate and build jobs', :aggregate_failures do
expect(pipeline.errors).to be_empty
- expect(build_names).to include('init', 'validate', 'build', 'deploy')
+ expect(build_names).to include('validate', 'build', 'deploy')
end
end
diff --git a/spec/lib/gitlab/database/connection_spec.rb b/spec/lib/gitlab/database/connection_spec.rb
index ee1df141cd6..02a31aa1084 100644
--- a/spec/lib/gitlab/database/connection_spec.rb
+++ b/spec/lib/gitlab/database/connection_spec.rb
@@ -428,15 +428,4 @@ RSpec.describe Gitlab::Database::Connection do
expect(connection.system_id).to be_an_instance_of(Integer)
end
end
-
- describe '#get_write_location' do
- it 'returns a string' do
- expect(connection.get_write_location(connection.scope.connection))
- .to be_a(String)
- end
-
- it 'returns nil if there are no results' do
- expect(connection.get_write_location(double(select_all: []))).to be_nil
- end
- end
end
diff --git a/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb b/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb
index f3ce5563e38..78d75d69e15 100644
--- a/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb
+++ b/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb
@@ -441,4 +441,15 @@ RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do
lb.disconnect!(timeout: 30)
end
end
+
+ describe '#get_write_location' do
+ it 'returns a string' do
+ expect(lb.send(:get_write_location, lb.pool.connection))
+ .to be_a(String)
+ end
+
+ it 'returns nil if there are no results' do
+ expect(lb.send(:get_write_location, double(select_all: []))).to be_nil
+ end
+ end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index c201d89947e..351d170a0a1 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -28,6 +28,16 @@ RSpec.describe Namespace do
it { is_expected.to have_one :onboarding_progress }
it { is_expected.to have_one :admin_note }
it { is_expected.to have_many :pending_builds }
+
+ describe '#children' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:subgroup) { create(:group, parent: group) }
+ let_it_be(:project_namespace) { create(:project_namespace, parent: group) }
+
+ it 'excludes project namespaces' do
+ expect(group.children).to match_array([subgroup])
+ end
+ end
end
describe 'validations' do
@@ -50,10 +60,10 @@ RSpec.describe Namespace do
ref(:project_sti_name) | ref(:user_sti_name) | 'project namespace cannot be the parent of another namespace'
ref(:project_sti_name) | ref(:group_sti_name) | 'project namespace cannot be the parent of another namespace'
ref(:project_sti_name) | ref(:project_sti_name) | 'project namespace cannot be the parent of another namespace'
- ref(:group_sti_name) | ref(:user_sti_name) | 'cannot not be used for user namespace'
+ ref(:group_sti_name) | ref(:user_sti_name) | 'cannot be used for user namespace'
ref(:group_sti_name) | ref(:group_sti_name) | nil
ref(:group_sti_name) | ref(:project_sti_name) | nil
- ref(:user_sti_name) | ref(:user_sti_name) | 'cannot not be used for user namespace'
+ ref(:user_sti_name) | ref(:user_sti_name) | 'cannot be used for user namespace'
ref(:user_sti_name) | ref(:group_sti_name) | 'user namespace cannot be the parent of another namespace'
ref(:user_sti_name) | ref(:project_sti_name) | nil
end
@@ -273,8 +283,8 @@ RSpec.describe Namespace do
describe '.by_parent' do
it 'includes correct namespaces' do
- expect(described_class.by_parent(namespace1.id)).to eq([namespace1sub])
- expect(described_class.by_parent(namespace2.id)).to eq([namespace2sub])
+ expect(described_class.by_parent(namespace1.id)).to match_array([namespace1sub])
+ expect(described_class.by_parent(namespace2.id)).to match_array([namespace2sub])
expect(described_class.by_parent(nil)).to match_array([namespace, namespace1, namespace2])
end
end
diff --git a/spec/services/ci/job_artifacts/create_service_spec.rb b/spec/services/ci/job_artifacts/create_service_spec.rb
index e6d9f208096..ba06e5f8cbe 100644
--- a/spec/services/ci/job_artifacts/create_service_spec.rb
+++ b/spec/services/ci/job_artifacts/create_service_spec.rb
@@ -175,18 +175,6 @@ RSpec.describe Ci::JobArtifacts::CreateService do
hash_including('key' => 'KEY1', 'value' => 'VAR1', 'source' => 'dotenv'),
hash_including('key' => 'KEY2', 'value' => 'VAR2', 'source' => 'dotenv'))
end
-
- context 'when ci_synchronous_artifact_parsing feature flag is disabled' do
- before do
- stub_feature_flags(ci_synchronous_artifact_parsing: false)
- end
-
- it 'does not call parse service' do
- expect(Ci::ParseDotenvArtifactService).not_to receive(:new)
-
- expect(subject[:status]).to eq(:success)
- end
- end
end
context 'when artifact_type is metrics' do