Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
7717a594e8
commit
ce07dcdcf5
|
|
@ -32,6 +32,20 @@
|
|||
expire_in: 7 days
|
||||
when: always
|
||||
|
||||
.parallel-qa-base:
|
||||
parallel: 5
|
||||
script:
|
||||
- export KNAPSACK_REPORT_PATH=knapsack/master_report.json
|
||||
- export KNAPSACK_TEST_FILE_PATTERN=qa/specs/features/**/*_spec.rb
|
||||
- |
|
||||
bin/test "${QA_SCENARIO}" "${CI_ENVIRONMENT_URL}" \
|
||||
-- \
|
||||
--color --format documentation \
|
||||
--format RspecJunitFormatter --out tmp/rspec.xml
|
||||
artifacts:
|
||||
reports:
|
||||
junit: qa/tmp/rspec.xml
|
||||
|
||||
.allure-report-base:
|
||||
image:
|
||||
name: ${GITLAB_DEPENDENCY_PROXY}andrcuns/allure-report-publisher:0.4.1
|
||||
|
|
@ -65,24 +79,23 @@ review-qa-smoke:
|
|||
script:
|
||||
- bin/test Test::Instance::Smoke "${CI_ENVIRONMENT_URL}"
|
||||
|
||||
review-qa-reliable:
|
||||
extends:
|
||||
- .review-qa-base
|
||||
- .review:rules:review-qa-reliable
|
||||
- .parallel-qa-base
|
||||
variables:
|
||||
QA_RUN_TYPE: review-qa-reliable
|
||||
QA_SCENARIO: Test::Instance::Reliable
|
||||
|
||||
review-qa-all:
|
||||
extends:
|
||||
- .review-qa-base
|
||||
- .review:rules:review-qa-all
|
||||
- .parallel-qa-base
|
||||
variables:
|
||||
QA_RUN_TYPE: review-qa-all
|
||||
parallel: 5
|
||||
script:
|
||||
- export KNAPSACK_REPORT_PATH=knapsack/master_report.json
|
||||
- export KNAPSACK_TEST_FILE_PATTERN=qa/specs/features/**/*_spec.rb
|
||||
- |
|
||||
bin/test Test::Instance::All "${CI_ENVIRONMENT_URL}" \
|
||||
-- \
|
||||
--color --format documentation \
|
||||
--format RspecJunitFormatter --out tmp/rspec.xml
|
||||
artifacts:
|
||||
reports:
|
||||
junit: qa/tmp/rspec.xml
|
||||
QA_SCENARIO: Test::Instance::All
|
||||
|
||||
review-performance:
|
||||
extends:
|
||||
|
|
|
|||
|
|
@ -1666,6 +1666,11 @@
|
|||
- <<: *if-dot-com-ee-schedule-child-pipeline
|
||||
when: on_failure
|
||||
|
||||
.review:rules:review-qa-reliable:
|
||||
rules:
|
||||
- when: on_success
|
||||
allow_failure: true
|
||||
|
||||
.review:rules:review-qa-all:
|
||||
rules:
|
||||
- <<: *if-not-ee
|
||||
|
|
|
|||
|
|
@ -2629,6 +2629,5 @@ Style/OpenStructUse:
|
|||
- 'spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb'
|
||||
- 'spec/support_specs/helpers/stub_feature_flags_spec.rb'
|
||||
- 'spec/tooling/rspec_flaky/flaky_example_spec.rb'
|
||||
- 'tooling/rspec_flaky/flaky_example.rb'
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
1c25541fb9d55a330b83c5b01ed996eb6021459c
|
||||
a4079265eb1b35c794ca45a056984cb13a7c2c82
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {
|
|||
import * as Sentry from '@sentry/browser';
|
||||
import { isEqual, isEmpty, omit } from 'lodash';
|
||||
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
||||
import { PROMO_URL } from 'jh_else_ce/lib/utils/url_utility';
|
||||
import {
|
||||
integrationTypes,
|
||||
integrationSteps,
|
||||
|
|
@ -129,6 +130,7 @@ export default {
|
|||
name: true,
|
||||
apiUrl: true,
|
||||
},
|
||||
pricingLink: `${PROMO_URL}/pricing`,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -436,7 +438,7 @@ export default {
|
|||
disabled="true"
|
||||
class="gl-display-inline-block gl-my-4"
|
||||
:message="$options.i18n.integrationFormSteps.selectType.enterprise"
|
||||
link="https://about.gitlab.com/pricing"
|
||||
:link="pricingLink"
|
||||
data-testid="multi-integrations-not-supported"
|
||||
/>
|
||||
</gl-form-group>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ const PATH_SEPARATOR_LEADING_REGEX = new RegExp(`^${PATH_SEPARATOR}+`);
|
|||
const PATH_SEPARATOR_ENDING_REGEX = new RegExp(`${PATH_SEPARATOR}+$`);
|
||||
const SHA_REGEX = /[\da-f]{40}/gi;
|
||||
|
||||
// About GitLab default host (overwrite in jh)
|
||||
export const PROMO_HOST = 'about.gitlab.com';
|
||||
|
||||
// About Gitlab default url (overwrite in jh)
|
||||
export const PROMO_URL = `https://${PROMO_HOST}`;
|
||||
|
||||
// Reset the cursor in a Regex so that multiple uses before a recompile don't fail
|
||||
function resetRegExp(regex) {
|
||||
regex.lastIndex = 0; /* eslint-disable-line no-param-reassign */
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlLoadingIcon, GlTable } from '@gitlab/ui';
|
||||
import { GlLoadingIcon, GlTableLite } from '@gitlab/ui';
|
||||
import createFlash from '~/flash';
|
||||
import { TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/graphql_shared/constants';
|
||||
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
||||
|
|
@ -12,7 +12,7 @@ const TIME_DATE_FORMAT = 'mmmm d, yyyy, HH:MM ("UTC:" o)';
|
|||
export default {
|
||||
components: {
|
||||
GlLoadingIcon,
|
||||
GlTable,
|
||||
GlTableLite,
|
||||
},
|
||||
inject: ['issuableType'],
|
||||
props: {
|
||||
|
|
@ -89,7 +89,7 @@ export default {
|
|||
<template>
|
||||
<div>
|
||||
<div v-if="isLoading"><gl-loading-icon size="md" /></div>
|
||||
<gl-table v-else :items="report" :fields="$options.fields" foot-clone>
|
||||
<gl-table-lite v-else :items="report" :fields="$options.fields" foot-clone>
|
||||
<template #cell(spentAt)="{ item: { spentAt } }">
|
||||
<div>{{ formatDate(spentAt) }}</div>
|
||||
</template>
|
||||
|
|
@ -111,6 +111,6 @@ export default {
|
|||
<div>{{ getSummary(summary, note) }}</div>
|
||||
</template>
|
||||
<template #foot(note)> </template>
|
||||
</gl-table>
|
||||
</gl-table-lite>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ module Projects
|
|||
module Security
|
||||
module ConfigurationHelper
|
||||
def security_upgrade_path
|
||||
'https://about.gitlab.com/pricing/'
|
||||
"https://#{ApplicationHelper.promo_host}/pricing/"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,11 +7,15 @@ module BlobViewer
|
|||
self.file_types = %i(package_json)
|
||||
|
||||
def manager_name
|
||||
'npm'
|
||||
yarn? ? 'yarn' : 'npm'
|
||||
end
|
||||
|
||||
def yarn?
|
||||
json_data['engines'].present? && json_data['engines']['yarn'].present?
|
||||
end
|
||||
|
||||
def manager_url
|
||||
'https://www.npmjs.com/'
|
||||
yarn? ? 'https://yarnpkg.com/' : 'https://www.npmjs.com/'
|
||||
end
|
||||
|
||||
def package_name
|
||||
|
|
@ -38,7 +42,11 @@ module BlobViewer
|
|||
end
|
||||
|
||||
def npm_url
|
||||
"https://www.npmjs.com/package/#{package_name}"
|
||||
if yarn?
|
||||
"https://yarnpkg.com/package/#{package_name}"
|
||||
else
|
||||
"https://www.npmjs.com/package/#{package_name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -24,8 +24,9 @@ class ProjectAuthorization < ApplicationRecord
|
|||
end
|
||||
|
||||
connection.execute <<-EOF.strip_heredoc
|
||||
INSERT INTO project_authorizations (user_id, project_id, access_level)
|
||||
VALUES #{tuples.map { |tuple| "(#{tuple.join(', ')})" }.join(', ')}
|
||||
INSERT INTO project_authorizations (user_id, project_id, access_level)
|
||||
VALUES #{tuples.map { |tuple| "(#{tuple.join(', ')})" }.join(', ')}
|
||||
ON CONFLICT DO NOTHING
|
||||
EOF
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1020,8 +1020,10 @@ class User < ApplicationRecord
|
|||
end
|
||||
# rubocop: enable CodeReuse/ServiceClass
|
||||
|
||||
def remove_project_authorizations(project_ids)
|
||||
project_authorizations.where(project_id: project_ids).delete_all
|
||||
def remove_project_authorizations(project_ids, per_batch = 1000)
|
||||
project_ids.each_slice(per_batch) do |project_ids_batch|
|
||||
project_authorizations.where(project_id: project_ids_batch).delete_all
|
||||
end
|
||||
end
|
||||
|
||||
def authorized_projects(min_access_level = nil)
|
||||
|
|
|
|||
|
|
@ -67,10 +67,8 @@ module Users
|
|||
def update_authorizations(remove = [], add = [])
|
||||
log_refresh_details(remove, add)
|
||||
|
||||
User.transaction do
|
||||
user.remove_project_authorizations(remove) unless remove.empty?
|
||||
ProjectAuthorization.insert_authorizations(add) unless add.empty?
|
||||
end
|
||||
user.remove_project_authorizations(remove) unless remove.empty?
|
||||
ProjectAuthorization.insert_authorizations(add) unless add.empty?
|
||||
|
||||
# Since we batch insert authorization rows, Rails' associations may get
|
||||
# out of sync. As such we force a reload of the User object.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
.top-area.adjust
|
||||
.gl-display-block.gl-text-right.gl-my-4.gl-w-full
|
||||
- if clusterable.can_add_cluster?
|
||||
= link_to s_('ClusterIntegration|Connect cluster with certificate'), clusterable.new_path, class: 'btn gl-button btn-confirm js-add-cluster gl-py-2', qa_selector: :integrate_kubernetes_cluster_button
|
||||
= link_to s_('ClusterIntegration|Connect cluster with certificate'), clusterable.new_path, class: 'btn gl-button btn-confirm js-add-cluster gl-py-2', data: { qa_selector: 'integrate_kubernetes_cluster_button' }
|
||||
- else
|
||||
%span.btn.gl-button.btn-confirm.js-add-cluster.disabled.gl-py-2
|
||||
= s_("ClusterIntegration|Connect cluster with certificate")
|
||||
|
|
|
|||
|
|
@ -1,18 +1,13 @@
|
|||
- feature_project_list_filter_bar = Feature.enabled?(:project_list_filter_bar)
|
||||
- is_your_projects_path = current_page?(dashboard_projects_path) || current_page?(root_path)
|
||||
- is_explore_projects_path = current_page?(explore_root_path) || current_page?(trending_explore_projects_path) || current_page?(starred_explore_projects_path) || current_page?(explore_projects_path)
|
||||
|
||||
%ul.nav-links.scrolling-tabs.mobile-separator.nav.nav-tabs{ class: ('gl-border-0!' if feature_project_list_filter_bar) }
|
||||
= nav_link(page: [dashboard_projects_path, root_path]) do
|
||||
= link_to dashboard_projects_path, class: 'shortcuts-activity', data: {placement: 'right'} do
|
||||
= _("Your projects")
|
||||
%span.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm= limited_counter_with_delimiter(@total_user_projects_count)
|
||||
= nav_link(page: starred_dashboard_projects_path) do
|
||||
= link_to starred_dashboard_projects_path, data: {placement: 'right'} do
|
||||
= _("Starred projects")
|
||||
%span.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm= limited_counter_with_delimiter(@total_starred_projects_count)
|
||||
= nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path]) do
|
||||
= link_to explore_root_path, data: {placement: 'right'} do
|
||||
= _("Explore projects")
|
||||
= nav_link(page: topics_explore_projects_path) do
|
||||
= link_to topics_explore_projects_path, data: {placement: 'right'} do
|
||||
= _("Explore topics")
|
||||
= gl_tabs_nav({ class: 'scrolling-tabs nav-links gl-display-flex gl-flex-grow-1 gl-flex-nowrap gl-border-0' }) do
|
||||
= gl_tab_link_to dashboard_projects_path, { item_active: is_your_projects_path, class: 'shortcuts-activity', data: { placement: 'right' } } do
|
||||
= _("Your projects")
|
||||
= gl_tab_counter_badge(limited_counter_with_delimiter(@total_user_projects_count))
|
||||
= gl_tab_link_to starred_dashboard_projects_path, { data: { placement: 'right' } } do
|
||||
= _("Starred projects")
|
||||
= gl_tab_counter_badge(limited_counter_with_delimiter(@total_starred_projects_count))
|
||||
= gl_tab_link_to _("Explore projects"), explore_root_path, { item_active: is_explore_projects_path, data: { placement: 'right' } }
|
||||
= gl_tab_link_to _("Explore topics"), topics_explore_projects_path, { data: { placement: 'right' } }
|
||||
= render_if_exists "dashboard/removed_projects_tab", removed_projects_count: @removed_projects_count
|
||||
|
|
|
|||
|
|
@ -46,5 +46,5 @@
|
|||
%button.btn-blank.btn-link.js-trigger-shortcut{ type: 'button' }
|
||||
= _('Use shortcuts')
|
||||
- unless Gitlab::CurrentSettings.help_page_hide_commercial_content?
|
||||
%li= link_to _('Get a support subscription'), 'https://about.gitlab.com/pricing/'
|
||||
%li= link_to _('Compare GitLab editions'), 'https://about.gitlab.com/features/#compare'
|
||||
%li= link_to _('Get a support subscription'), "https://#{ApplicationHelper.promo_host}/pricing/"
|
||||
%li= link_to _('Compare GitLab editions'), "https://#{ApplicationHelper.promo_host}/features/#compare"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
name: invite_team_email
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72470
|
||||
rollout_issue_url:
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345553
|
||||
milestone: '14.5'
|
||||
type: experiment
|
||||
group: group::activation
|
||||
|
|
|
|||
|
|
@ -1100,17 +1100,15 @@ To test the logic of Apollo cache updates, we might want to mock an Apollo Clien
|
|||
|
||||
To separate tests with mocked client from 'usual' unit tests, create an additional factory and pass the created `mockApollo` as an option to the `createComponent`-factory. This way we only create Apollo Client instance when it's necessary.
|
||||
|
||||
We need to inject `VueApollo` to the Vue local instance and, likewise, it is recommended to call `localVue.use()` in `createMockApolloProvider()` to only load it when it is necessary.
|
||||
We need to inject `VueApollo` into the Vue instance by calling `Vue.use(VueApollo)`. This will install `VueApollo` globally for all the tests in the file. It is recommended to call `Vue.use(VueApollo)` just after the imports.
|
||||
|
||||
```javascript
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { createLocalVue } from '@vue/test-utils';
|
||||
import Vue from 'vue';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
Vue.use(VueApollo);
|
||||
|
||||
function createMockApolloProvider() {
|
||||
localVue.use(VueApollo);
|
||||
|
||||
return createMockApollo(requestHandlers);
|
||||
}
|
||||
|
||||
|
|
@ -1118,7 +1116,6 @@ function createComponent(options = {}) {
|
|||
const { mockApollo } = options;
|
||||
...
|
||||
return shallowMount(..., {
|
||||
localVue,
|
||||
apolloProvider: mockApollo,
|
||||
...
|
||||
});
|
||||
|
|
@ -1188,7 +1185,6 @@ describe('Some component', () => {
|
|||
const { mockApollo } = options;
|
||||
|
||||
return shallowMount(Index, {
|
||||
localVue,
|
||||
apolloProvider: mockApollo,
|
||||
});
|
||||
}
|
||||
|
|
@ -1275,7 +1271,6 @@ function createComponent(options = {}) {
|
|||
const { mockApollo } = options;
|
||||
|
||||
return shallowMount(Index, {
|
||||
localVue,
|
||||
apolloProvider: mockApollo,
|
||||
});
|
||||
}
|
||||
|
|
@ -1408,7 +1403,6 @@ const createComponentWithApollo = ({ props = {} } = {}) => {
|
|||
mockApollo = createMockApollo([], mockResolvers); // resolvers are the second parameter
|
||||
|
||||
wrapper = shallowMount(MyComponent, {
|
||||
localVue,
|
||||
propsData: {},
|
||||
apolloProvider: mockApollo,
|
||||
// ...
|
||||
|
|
@ -1476,7 +1470,6 @@ function createComponent(options = {}) {
|
|||
const { mockApollo } = options;
|
||||
|
||||
return shallowMount(Index, {
|
||||
localVue,
|
||||
apolloProvider: mockApollo,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -199,8 +199,9 @@ security issues:
|
|||
### Vulnerability-Check rule
|
||||
|
||||
To prevent a merge request introducing a security vulnerability in a project, enable the
|
||||
Vulnerability-Check rule. While this rule is enabled, an additional merge request approval is
|
||||
required when the latest security report in a merge request:
|
||||
Vulnerability-Check rule. While this rule is enabled, additional merge request approval by
|
||||
[eligible approvers](../project/merge_requests/approvals/rules.md#eligible-approvers)
|
||||
is required when the latest security report in a merge request:
|
||||
|
||||
- Contains vulnerabilities with states (for example, `previously detected`, `dismissed`) matching the rule's vulnerability states. Only `newly detected` will be considered if the target branch differs from the project default branch.
|
||||
- Contains vulnerabilities with severity levels (for example, `high`, `critical`, or `unknown`)
|
||||
|
|
@ -215,13 +216,12 @@ An approval is optional when the security report:
|
|||
the rule's severity levels.
|
||||
- Contains a vulnerability count equal to or less than what the rule allows.
|
||||
|
||||
Project members assigned [at least the Maintainer role](../permissions.md#project-members-permissions) can enable or edit
|
||||
the Vulnerability-Check rule.
|
||||
|
||||
#### Enable the Vulnerability-Check rule
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Maintainer or Owner [role](../permissions.md#project-members-permissions).
|
||||
|
||||
To enable the `Vulnerability-Check` rule:
|
||||
To enable or edit the Vulnerability-Check rule:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Settings > General**.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,13 @@ const baseConfig = require('./jest.config.base');
|
|||
|
||||
checkEnvironment();
|
||||
|
||||
console.log(`
|
||||
PSA: Running into unexpected and/or strange frontend integration test errors?
|
||||
Please help improve our error logging by following the instructions on this issue:
|
||||
|
||||
https://gitlab.com/gitlab-org/gitlab/-/issues/345513
|
||||
`);
|
||||
|
||||
module.exports = {
|
||||
...baseConfig('spec/frontend_integration', {
|
||||
moduleNameMapper: {
|
||||
|
|
|
|||
|
|
@ -24,8 +24,7 @@ spec:
|
|||
args:
|
||||
- --token-file=/config/token
|
||||
- --kas-address
|
||||
- "<%= kas_wss_address %>" # Use this for GitLab chart deployments
|
||||
# - "<%= kas_grpc_address %>" # Use this for GDK
|
||||
- "<%= kas_wss_address %>"
|
||||
volumeMounts:
|
||||
- name: token-volume
|
||||
mountPath: /config
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ module QA
|
|||
end
|
||||
|
||||
def add_existing_cluster
|
||||
click_element(:add_existing_cluster_tab)
|
||||
page.find('.gl-tab-nav-item', text: 'Connect existing cluster').click
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ module QA
|
|||
module Infrastructure
|
||||
module Kubernetes
|
||||
class Index < Page::Base
|
||||
view 'app/assets/javascripts/clusters_list/components/clusters_empty_state.vue' do
|
||||
element :add_kubernetes_cluster_link
|
||||
view 'app/views/clusters/clusters/_cluster_list.html.haml' do
|
||||
element :integrate_kubernetes_cluster_button
|
||||
end
|
||||
|
||||
def add_kubernetes_cluster
|
||||
click_element :add_kubernetes_cluster_link
|
||||
def connect_cluster_with_certificate
|
||||
find('.js-add-cluster').click
|
||||
end
|
||||
|
||||
def has_cluster?(cluster)
|
||||
|
|
|
|||
|
|
@ -7,26 +7,9 @@ module QA
|
|||
module Kubernetes
|
||||
class Show < Page::Base
|
||||
view 'app/assets/javascripts/clusters/forms/components/integration_form.vue' do
|
||||
element :integration_status_toggle, required: true
|
||||
element :base_domain_field, required: true
|
||||
element :save_changes_button, required: true
|
||||
end
|
||||
|
||||
view 'app/views/clusters/clusters/_details_tab.html.haml' do
|
||||
element :details, required: true
|
||||
end
|
||||
|
||||
view 'app/views/clusters/clusters/_health.html.haml' do
|
||||
element :cluster_health_section
|
||||
end
|
||||
|
||||
view 'app/views/clusters/clusters/_health_tab.html.haml' do
|
||||
element :health, required: true
|
||||
end
|
||||
|
||||
def open_details
|
||||
has_element?(:details, wait: 30)
|
||||
click_element :details
|
||||
element :integration_status_toggle
|
||||
element :base_domain_field
|
||||
element :save_changes_button
|
||||
end
|
||||
|
||||
def set_domain(domain)
|
||||
|
|
@ -36,29 +19,6 @@ module QA
|
|||
def save_domain
|
||||
click_element :save_changes_button, Page::Project::Infrastructure::Kubernetes::Show
|
||||
end
|
||||
|
||||
def wait_for_cluster_health
|
||||
wait_until(max_duration: 120, sleep_interval: 3, reload: true) do
|
||||
has_cluster_health_graphs?
|
||||
end
|
||||
end
|
||||
|
||||
def open_health
|
||||
has_element?(:health, wait: 30)
|
||||
click_element :health
|
||||
end
|
||||
|
||||
def has_cluster_health_graphs?
|
||||
within_cluster_health_section do
|
||||
has_text?('CPU Usage')
|
||||
end
|
||||
end
|
||||
|
||||
def within_cluster_health_section
|
||||
within_element :cluster_health_section do
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,13 +19,12 @@ module QA
|
|||
def fabricate!
|
||||
puts 'TODO: FABRICATE VIA UI'
|
||||
end
|
||||
# TODO
|
||||
#
|
||||
# The UI for this model is not yet implemented. So far it can only be
|
||||
# created through the GraphQL API
|
||||
# def fabricate
|
||||
#
|
||||
# end
|
||||
|
||||
def resource_web_url(resource)
|
||||
super
|
||||
rescue ResourceURLMissingError
|
||||
# this particular resource does not expose a web_url property
|
||||
end
|
||||
|
||||
def api_get_path
|
||||
"gid://gitlab/Clusters::Agent/#{id}"
|
||||
|
|
|
|||
|
|
@ -13,13 +13,12 @@ module QA
|
|||
def fabricate!
|
||||
puts 'TODO: FABRICATE VIA UI'
|
||||
end
|
||||
# TODO
|
||||
#
|
||||
# The UI for this model is not yet implemented. So far it can only be
|
||||
# created through the GraphQL API
|
||||
# def fabricate
|
||||
#
|
||||
# end
|
||||
|
||||
def resource_web_url(resource)
|
||||
super
|
||||
rescue ResourceURLMissingError
|
||||
# this particular resource does not expose a web_url property
|
||||
end
|
||||
|
||||
def api_get_path
|
||||
"gid://gitlab/Clusters::AgentToken/#{id}"
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ module QA
|
|||
Resource::Project.fabricate!
|
||||
end
|
||||
|
||||
def ingress_ip
|
||||
@ingress_ip ||= @cluster.fetch_external_ip_for_ingress
|
||||
attribute :ingress_ip do
|
||||
@cluster.fetch_external_ip_for_ingress
|
||||
end
|
||||
|
||||
def fabricate!
|
||||
|
|
@ -24,7 +24,7 @@ module QA
|
|||
&:go_to_infrastructure_kubernetes)
|
||||
|
||||
Page::Project::Infrastructure::Kubernetes::Index.perform(
|
||||
&:add_kubernetes_cluster)
|
||||
&:connect_cluster_with_certificate)
|
||||
|
||||
Page::Project::Infrastructure::Kubernetes::Add.perform(
|
||||
&:add_existing_cluster)
|
||||
|
|
@ -39,14 +39,10 @@ module QA
|
|||
end
|
||||
|
||||
Page::Project::Infrastructure::Kubernetes::Show.perform do |show|
|
||||
# We must wait a few seconds for permissions to be set up correctly for new cluster
|
||||
sleep 25
|
||||
|
||||
if @install_ingress
|
||||
populate(:ingress_ip)
|
||||
ingress_ip
|
||||
|
||||
show.open_details
|
||||
show.set_domain("#{ingress_ip}.nip.io")
|
||||
show.set_domain("#{@ingress_ip}.nip.io")
|
||||
show.save_domain
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -403,7 +403,7 @@ module QA
|
|||
end
|
||||
|
||||
def gitlab_agentk_version
|
||||
ENV.fetch('GITLAB_AGENTK_VERSION', 'v13.7.0')
|
||||
ENV.fetch('GITLAB_AGENTK_VERSION', 'v14.4.0')
|
||||
end
|
||||
|
||||
def transient_trials
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Scenario
|
||||
module Test
|
||||
module Instance
|
||||
class Reliable < Template
|
||||
include Bootable
|
||||
include SharedAttributes
|
||||
|
||||
tags :reliable
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -24,16 +24,6 @@ module QA
|
|||
)
|
||||
end
|
||||
|
||||
def set_credentials(admin_user)
|
||||
master_auth = JSON.parse(`gcloud container clusters describe #{cluster_name} --region #{@region} --format 'json(masterAuth.username, masterAuth.password)'`)
|
||||
|
||||
shell <<~CMD.tr("\n", ' ')
|
||||
kubectl config set-credentials #{admin_user}
|
||||
--username #{master_auth['masterAuth']['username']}
|
||||
--password #{master_auth['masterAuth']['password']}
|
||||
CMD
|
||||
end
|
||||
|
||||
def setup
|
||||
login_if_not_already_logged_in
|
||||
create_cluster
|
||||
|
|
@ -43,6 +33,12 @@ module QA
|
|||
delete_cluster
|
||||
end
|
||||
|
||||
def install_ingress
|
||||
QA::Runtime::Logger.info "Attempting to install Ingress on cluster #{cluster_name}"
|
||||
shell 'kubectl create -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-0.31.0/deploy/static/provider/cloud/deploy.yaml'
|
||||
wait_for_ingress
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def login_if_not_already_logged_in
|
||||
|
|
@ -59,7 +55,7 @@ module QA
|
|||
end
|
||||
|
||||
def attempt_login_with_env_vars
|
||||
puts "No gcloud account. Attempting to login from env vars GCLOUD_ACCOUNT_EMAIL and GCLOUD_ACCOUNT_KEY."
|
||||
QA::Runtime::Logger.debug("Logging in with GCLOUD_ACCOUNT_EMAIL and GCLOUD_ACCOUNT_KEY.")
|
||||
gcloud_account_key = Tempfile.new('gcloud-account-key')
|
||||
gcloud_account_key.write(Runtime::Env.gcloud_account_key)
|
||||
gcloud_account_key.close
|
||||
|
|
@ -80,7 +76,6 @@ module QA
|
|||
gcloud container clusters
|
||||
create #{cluster_name}
|
||||
#{auth_options}
|
||||
--enable-basic-auth
|
||||
--region #{@region}
|
||||
--disk-size 10GB
|
||||
--num-nodes #{Runtime::Env.gcloud_num_nodes}
|
||||
|
|
@ -109,6 +104,18 @@ module QA
|
|||
def get_region
|
||||
Runtime::Env.gcloud_region || @available_regions.delete(@available_regions.sample)
|
||||
end
|
||||
|
||||
def wait_for_ingress
|
||||
QA::Runtime::Logger.info 'Waiting for Ingress controller pod to be initialized'
|
||||
|
||||
Support::Retrier.retry_until(max_attempts: 60, sleep_interval: 1) do
|
||||
service_available?('kubectl get pods --all-namespaces -l app.kubernetes.io/component=controller | grep -o "ingress-nginx-controller.*1/1"')
|
||||
end
|
||||
end
|
||||
|
||||
def service_available?(command)
|
||||
system("#{command} > /dev/null 2>&1")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ module QA
|
|||
cluster_name
|
||||
end
|
||||
|
||||
def install_ingress
|
||||
@provider.install_ingress
|
||||
end
|
||||
|
||||
def create_secret(secret, secret_name)
|
||||
shell("kubectl create secret generic #{secret_name} --from-literal=token='#{secret}'")
|
||||
end
|
||||
|
|
@ -70,7 +74,13 @@ module QA
|
|||
end
|
||||
|
||||
def fetch_external_ip_for_ingress
|
||||
`kubectl get svc --all-namespaces --no-headers=true -l app.kubernetes.io/name=ingress-nginx -o custom-columns=:'status.loadBalancer.ingress[0].ip' | grep -v 'none'`
|
||||
install_ingress
|
||||
|
||||
# need to wait since the ingress-nginx service has an initial delay set of 10 seconds
|
||||
sleep 10
|
||||
ingress_ip = `kubectl get svc --all-namespaces --no-headers=true -l app.kubernetes.io/name=ingress-nginx -o custom-columns=:'status.loadBalancer.ingress[0].ip' | grep -v 'none'`
|
||||
QA::Runtime::Logger.debug "Has ingress address set to: #{ingress_ip}"
|
||||
ingress_ip
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -82,7 +92,6 @@ module QA
|
|||
def fetch_credentials
|
||||
return global_credentials unless rbac
|
||||
|
||||
@provider.set_credentials(admin_user)
|
||||
create_service_account(admin_user)
|
||||
account_credentials
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Configure' do
|
||||
RSpec.describe 'Configure', only: { subdomain: :staging } do
|
||||
let(:project) do
|
||||
Resource::Project.fabricate_via_api! do |project|
|
||||
project.name = Runtime::Env.auto_devops_project_name || 'autodevops-project'
|
||||
project.name = 'autodevops-project'
|
||||
project.auto_devops_enabled = true
|
||||
end
|
||||
end
|
||||
|
|
@ -13,35 +13,24 @@ module QA
|
|||
disable_optional_jobs(project)
|
||||
end
|
||||
|
||||
describe 'Auto DevOps support', :orchestrated, :kubernetes, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/251090', type: :stale } do
|
||||
describe 'Auto DevOps support' do
|
||||
context 'when rbac is enabled' do
|
||||
let(:cluster) { Service::KubernetesCluster.new.create! }
|
||||
|
||||
after do
|
||||
cluster&.remove!
|
||||
project.remove_via_api!
|
||||
end
|
||||
|
||||
it 'runs auto devops', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1422' do
|
||||
Flow::Login.sign_in
|
||||
|
||||
# Set an application secret CI variable (prefixed with K8S_SECRET_)
|
||||
Resource::CiVariable.fabricate! do |resource|
|
||||
resource.project = project
|
||||
resource.key = 'K8S_SECRET_OPTIONAL_MESSAGE'
|
||||
resource.value = 'you_can_see_this_variable'
|
||||
resource.masked = false
|
||||
end
|
||||
|
||||
# Connect K8s cluster
|
||||
Resource::KubernetesCluster::ProjectCluster.fabricate! do |k8s_cluster|
|
||||
k8s_cluster.project = project
|
||||
k8s_cluster.cluster = cluster
|
||||
k8s_cluster.install_ingress = true
|
||||
k8s_cluster.install_prometheus = true
|
||||
k8s_cluster.install_runner = true
|
||||
end
|
||||
|
||||
# Create Auto DevOps compatible repo
|
||||
Resource::Repository::ProjectPush.fabricate! do |push|
|
||||
push.project = project
|
||||
push.directory = Pathname
|
||||
|
|
@ -78,46 +67,6 @@ module QA
|
|||
|
||||
job.click_element(:pipeline_path)
|
||||
end
|
||||
|
||||
Page::Project::Menu.perform(&:go_to_deployments_environments)
|
||||
Page::Project::Deployments::Environments::Index.perform do |index|
|
||||
index.click_environment_link('production')
|
||||
end
|
||||
Page::Project::Deployments::Environments::Show.perform do |show|
|
||||
show.view_deployment do
|
||||
expect(page).to have_content('Hello World!')
|
||||
expect(page).to have_content('you_can_see_this_variable')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Auto DevOps', :smoke do
|
||||
before do
|
||||
Flow::Login.sign_in
|
||||
|
||||
project.visit!
|
||||
|
||||
Page::Project::Menu.perform(&:go_to_ci_cd_settings)
|
||||
Page::Project::Settings::CiCd.perform(&:expand_auto_devops)
|
||||
Page::Project::Settings::AutoDevops.perform(&:enable_autodevops)
|
||||
|
||||
# Create AutoDevOps repo
|
||||
Resource::Repository::ProjectPush.fabricate! do |push|
|
||||
push.project = project
|
||||
push.directory = Pathname
|
||||
.new(__dir__)
|
||||
.join('../../../../../fixtures/auto_devops_rack')
|
||||
push.commit_message = 'Create AutoDevOps compatible Project'
|
||||
end
|
||||
end
|
||||
|
||||
it 'runs an AutoDevOps pipeline', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/quality/test_cases/1564' do
|
||||
Flow::Pipeline.visit_latest_pipeline
|
||||
|
||||
Page::Project::Pipeline::Show.perform do |pipeline|
|
||||
expect(pipeline).to have_tag('Auto DevOps')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -128,7 +77,8 @@ module QA
|
|||
%w[
|
||||
CODE_QUALITY_DISABLED LICENSE_MANAGEMENT_DISABLED
|
||||
SAST_DISABLED DAST_DISABLED DEPENDENCY_SCANNING_DISABLED
|
||||
CONTAINER_SCANNING_DISABLED
|
||||
CONTAINER_SCANNING_DISABLED BROWSER_PERFORMANCE_DISABLED
|
||||
SECRET_DETECTION_DISABLED
|
||||
].each do |key|
|
||||
Resource::CiVariable.fabricate_via_api! do |resource|
|
||||
resource.project = project
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe QA::Scenario::Test::Instance::Reliable do
|
||||
it_behaves_like 'a QA scenario class' do
|
||||
let(:tags) { [:reliable] }
|
||||
end
|
||||
end
|
||||
|
|
@ -80,7 +80,7 @@ RSpec.describe 'Dashboard Projects' do
|
|||
visit dashboard_projects_path
|
||||
|
||||
expect(page).to have_content(project.name)
|
||||
expect(find('.nav-links li:nth-child(1) .badge-pill')).to have_content(1)
|
||||
expect(find('.gl-tabs-nav li:nth-child(1) .badge-pill')).to have_content(1)
|
||||
end
|
||||
|
||||
it 'shows personal projects on personal projects tab', :js do
|
||||
|
|
@ -128,8 +128,8 @@ RSpec.describe 'Dashboard Projects' do
|
|||
|
||||
expect(page).not_to have_content(project.name)
|
||||
expect(page).to have_content(project2.name)
|
||||
expect(find('.nav-links li:nth-child(1) .badge-pill')).to have_content(1)
|
||||
expect(find('.nav-links li:nth-child(2) .badge-pill')).to have_content(1)
|
||||
expect(find('.gl-tabs-nav li:nth-child(1) .badge-pill')).to have_content(1)
|
||||
expect(find('.gl-tabs-nav li:nth-child(2) .badge-pill')).to have_content(1)
|
||||
end
|
||||
|
||||
it 'does not show tabs to filter by all projects or personal' do
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
import { GlForm, GlFormSelect, GlFormInput, GlToggle, GlFormTextarea, GlTab } from '@gitlab/ui';
|
||||
import {
|
||||
GlForm,
|
||||
GlFormSelect,
|
||||
GlFormInput,
|
||||
GlToggle,
|
||||
GlFormTextarea,
|
||||
GlTab,
|
||||
GlLink,
|
||||
} from '@gitlab/ui';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { nextTick } from 'vue';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
|
|
@ -101,6 +109,12 @@ describe('AlertsSettingsForm', () => {
|
|||
expect(findFormFields().at(0).attributes('id')).not.toBe('name-integration');
|
||||
});
|
||||
|
||||
it('verify pricing link url', () => {
|
||||
createComponent({ props: { canAddIntegration: false } });
|
||||
const link = findMultiSupportText().findComponent(GlLink);
|
||||
expect(link.attributes('href')).toMatch(/https:\/\/about.gitlab.(com|cn)\/pricing/);
|
||||
});
|
||||
|
||||
describe('form tabs', () => {
|
||||
it('renders 3 tabs', () => {
|
||||
expect(findTabs()).toHaveLength(3);
|
||||
|
|
|
|||
|
|
@ -1060,4 +1060,12 @@ describe('URL utility', () => {
|
|||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('defaultPromoUrl', () => {
|
||||
it('Gitlab about page url', () => {
|
||||
const url = 'https://about.gitlab.com';
|
||||
|
||||
expect(urlUtils.PROMO_URL).toBe(url);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,6 +8,6 @@ RSpec.describe Projects::Security::ConfigurationHelper do
|
|||
describe 'security_upgrade_path' do
|
||||
subject { security_upgrade_path }
|
||||
|
||||
it { is_expected.to eq('https://about.gitlab.com/pricing/') }
|
||||
it { is_expected.to eq("https://#{ApplicationHelper.promo_host}/pricing/") }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -27,11 +27,55 @@ RSpec.describe BlobViewer::PackageJson do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#package_url' do
|
||||
it 'returns the package URL' do
|
||||
expect(subject).to receive(:prepare!)
|
||||
context 'yarn' do
|
||||
let(:data) do
|
||||
<<-SPEC.strip_heredoc
|
||||
{
|
||||
"name": "module-name",
|
||||
"version": "10.3.1",
|
||||
"engines": {
|
||||
"yarn": "^2.4.0"
|
||||
}
|
||||
}
|
||||
SPEC
|
||||
end
|
||||
|
||||
expect(subject.package_url).to eq("https://www.npmjs.com/package/#{subject.package_name}")
|
||||
let(:blob) { fake_blob(path: 'package.json', data: data) }
|
||||
|
||||
subject { described_class.new(blob) }
|
||||
|
||||
describe '#package_url' do
|
||||
it 'returns the package URL', :aggregate_failures do
|
||||
expect(subject).to receive(:prepare!)
|
||||
|
||||
expect(subject.package_url).to eq("https://yarnpkg.com/package/#{subject.package_name}")
|
||||
end
|
||||
end
|
||||
|
||||
describe '#manager_url' do
|
||||
it 'returns the manager URL', :aggregate_failures do
|
||||
expect(subject).to receive(:prepare!)
|
||||
|
||||
expect(subject.manager_url).to eq("https://yarnpkg.com/")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'npm' do
|
||||
describe '#package_url' do
|
||||
it 'returns the package URL', :aggregate_failures do
|
||||
expect(subject).to receive(:prepare!)
|
||||
|
||||
expect(subject.package_url).to eq("https://www.npmjs.com/package/#{subject.package_name}")
|
||||
end
|
||||
end
|
||||
|
||||
describe '#manager_url' do
|
||||
it 'returns the manager URL', :aggregate_failures do
|
||||
expect(subject).to receive(:prepare!)
|
||||
|
||||
expect(subject.manager_url).to eq("https://www.npmjs.com/")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ProjectAuthorization do
|
||||
let(:user) { create(:user) }
|
||||
let(:project1) { create(:project) }
|
||||
let(:project2) { create(:project) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project1) { create(:project) }
|
||||
let_it_be(:project2) { create(:project) }
|
||||
let_it_be(:project3) { create(:project) }
|
||||
|
||||
describe '.insert_authorizations' do
|
||||
it 'inserts the authorizations' do
|
||||
|
|
@ -23,5 +24,19 @@ RSpec.describe ProjectAuthorization do
|
|||
|
||||
expect(user.project_authorizations.count).to eq(2)
|
||||
end
|
||||
|
||||
it 'skips duplicates and inserts the remaining rows without error' do
|
||||
create(:project_authorization, user: user, project: project1, access_level: Gitlab::Access::MAINTAINER)
|
||||
|
||||
rows = [
|
||||
[user.id, project1.id, Gitlab::Access::MAINTAINER],
|
||||
[user.id, project2.id, Gitlab::Access::MAINTAINER],
|
||||
[user.id, project3.id, Gitlab::Access::MAINTAINER]
|
||||
]
|
||||
|
||||
described_class.insert_authorizations(rows)
|
||||
|
||||
expect(user.project_authorizations.pluck(:user_id, :project_id, :access_level)).to match_array(rows)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4147,6 +4147,23 @@ RSpec.describe User do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#remove_project_authorizations' do
|
||||
let_it_be(:project1) { create(:project) }
|
||||
let_it_be(:project2) { create(:project) }
|
||||
let_it_be(:project3) { create(:project) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
it 'removes the project authorizations of the user, in specified projects' do
|
||||
create(:project_authorization, user: user, project: project1)
|
||||
create(:project_authorization, user: user, project: project2)
|
||||
create(:project_authorization, user: user, project: project3)
|
||||
|
||||
user.remove_project_authorizations([project1.id, project2.id])
|
||||
|
||||
expect(user.project_authorizations.pluck(:project_id)).to match_array([project3.id])
|
||||
end
|
||||
end
|
||||
|
||||
describe '#access_level=' do
|
||||
let(:user) { build(:user) }
|
||||
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ RSpec.describe StubFeatureFlags do
|
|||
context 'type handling' do
|
||||
context 'raises error' do
|
||||
where(:feature_actors) do
|
||||
['string', 1, 1.0, OpenStruct.new]
|
||||
['string', 1, 1.0, Object.new]
|
||||
end
|
||||
|
||||
with_them do
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ module Tooling
|
|||
%r{\A((ee|jh)/)?app/finders/} => [:database, :backend],
|
||||
%r{\Arubocop/cop/migration(/|\.rb)} => :database,
|
||||
|
||||
%r{\A(\.ruby-version\z|\.nvmrc\z|\.tool-versions\z)} => :tooling,
|
||||
%r{\A(\.gitlab-ci\.yml\z|\.gitlab\/ci)} => :tooling,
|
||||
%r{\A\.codeclimate\.yml\z} => :tooling,
|
||||
%r{\Alefthook.yml\z} => :tooling,
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
golang 1.16.9
|
||||
Loading…
Reference in New Issue