Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3ce7340b2a
commit
dd1388bcdb
|
@ -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"
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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 need to sign in or sign up before continuing."
|
||||
You might get the following message prompting you to sign in to GitLab.com
|
||||
when you're already signed in:
|
||||
|
||||
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.
|
||||
```plaintext
|
||||
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.
|
||||
|
||||
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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -39,7 +39,6 @@ describe('Pipeline editor home wrapper', () => {
|
|||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
describe('renders', () => {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
]),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue