diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-29 21:09:52 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-29 21:09:52 +0300 |
commit | 5c2377d19572bdaaa6026fa6be131c3649074b36 (patch) | |
tree | 04acff23fd41360701c29c8694d24e587bc7ac5a | |
parent | 20fda899a62cc27a4d40a168640e7e926c69eb62 (diff) |
Add latest changes from gitlab-org/gitlab@master
129 files changed, 1925 insertions, 802 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index b4fd436cc58..f2df17b1ed8 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -184,7 +184,7 @@ Dangerfile @gl-quality/eng-prod /lib/gitlab/auth/ldap/ @dblessing @mkozono [Templates] -/lib/gitlab/ci/templates/ @nolith @dosuken123 +/lib/gitlab/ci/templates/ @nolith @shinya.maeda /lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @DylanGriffith @mayra-cabrera @tkuah /lib/gitlab/ci/templates/Security/ @plafoucriere @gonzoyumo @twoodham @sethgitlab diff --git a/.gitlab/issue_templates/Implementation.md b/.gitlab/issue_templates/Implementation.md new file mode 100644 index 00000000000..dc5eb18a25e --- /dev/null +++ b/.gitlab/issue_templates/Implementation.md @@ -0,0 +1,62 @@ +<!-- +Implementation issues are used break-up a large piece of work into small, discrete tasks that can +move independently through the build workflow steps. They're typically used to populate a Feature +Epic. Once created, an implementation issue is usually refined in order to populate and review the +implementation plan and weight. +Example workflow: https://about.gitlab.com/handbook/engineering/development/threat-management/planning/diagram.html#plan +--> + +## Why are we doing this work +<!-- +A brief explanation of the why, not the what or how. Assume the reader doesn't know the +background and won't have time to dig-up information from comment threads. +--> + + +## Relevant links +<!-- +Information that the developer might need to refer to when implementing the issue. + +- [Design Issue](https://gitlab.com/gitlab-org/gitlab/-/issues/<id>) + - [Design 1](https://gitlab.com/gitlab-org/gitlab/-/issues/<id>/designs/<image>.png) + - [Design 2](https://gitlab.com/gitlab-org/gitlab/-/issues/<id>/designs/<image>.png) +- [Similar implementation](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/<id>) +--> + + +## Non-functional requirements +<!-- +Add details for required items and delete others. +--> + +- [ ] Documentation: +- [ ] Feature flag: +- [ ] Performance: +- [ ] Testing: + + +## Implementation plan +<!-- +Steps and the parts of the code that will need to get updated. The plan can also +call-out responsibilities for other team members or teams. +--> + +- [ ] ~frontend Step 1 + - [ ] @person Step 1a +- [ ] ~frontend Step 2 + + +<!-- +Workflow and other relevant labels + +~"group::" ~"Category:" ~"GitLab Ultimate" +--> +/label ~"workflow::refinement" + +<!-- +Other settings you might want to include when creating the issue. + +/milestone %"Next 1-3 releases" +/assign @ +/epic & +--> diff --git a/.gitlab/merge_request_templates/Change Documentation Location.md b/.gitlab/merge_request_templates/Change Documentation Location.md index f18957fdaaa..155694c947d 100644 --- a/.gitlab/merge_request_templates/Change Documentation Location.md +++ b/.gitlab/merge_request_templates/Change Documentation Location.md @@ -15,7 +15,7 @@ Closes ## Moving docs to a new location? Read the guidelines: -https://docs.gitlab.com/ee/development/documentation/index.html#changing-document-location +https://docs.gitlab.com/ee/development/documentation/index.html#move-or-rename-a-page - [ ] Make sure the old link is not removed and has its contents replaced with a link to the new location. diff --git a/app/assets/javascripts/boards/components/board_configuration_options.vue b/app/assets/javascripts/boards/components/board_configuration_options.vue new file mode 100644 index 00000000000..ad3d653b905 --- /dev/null +++ b/app/assets/javascripts/boards/components/board_configuration_options.vue @@ -0,0 +1,65 @@ +<script> +import { GlFormCheckbox } from '@gitlab/ui'; + +export default { + components: { + GlFormCheckbox, + }, + props: { + currentBoard: { + type: Object, + required: true, + }, + board: { + type: Object, + required: true, + }, + isNewForm: { + type: Boolean, + required: false, + default: false, + }, + }, + data() { + const { hide_backlog_list: hideBacklogList, hide_closed_list: hideClosedList } = this.isNewForm + ? this.board + : this.currentBoard; + + return { + hideClosedList, + hideBacklogList, + }; + }, + methods: { + changeClosedList(checked) { + this.board.hideClosedList = !checked; + }, + changeBacklogList(checked) { + this.board.hideBacklogList = !checked; + }, + }, +}; +</script> + +<template> + <div class="append-bottom-20"> + <label class="form-section-title label-bold" for="board-new-name"> + {{ __('List options') }} + </label> + <p class="text-secondary gl-mb-3"> + {{ __('Configure which lists are shown for anyone who visits this board') }} + </p> + <gl-form-checkbox + :checked="!hideBacklogList" + data-testid="backlog-list-checkbox" + @change="changeBacklogList" + >{{ __('Show the Open list') }} + </gl-form-checkbox> + <gl-form-checkbox + :checked="!hideClosedList" + data-testid="closed-list-checkbox" + @change="changeClosedList" + >{{ __('Show the Closed list') }} + </gl-form-checkbox> + </div> +</template> diff --git a/app/assets/javascripts/boards/components/board_form.vue b/app/assets/javascripts/boards/components/board_form.vue index 385dd5fdc71..793c594cf16 100644 --- a/app/assets/javascripts/boards/components/board_form.vue +++ b/app/assets/javascripts/boards/components/board_form.vue @@ -5,6 +5,8 @@ import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue'; import { visitUrl } from '~/lib/utils/url_utility'; import boardsStore from '~/boards/stores/boards_store'; +import BoardConfigurationOptions from './board_configuration_options.vue'; + const boardDefaults = { id: false, name: '', @@ -13,12 +15,15 @@ const boardDefaults = { assignee: {}, assignee_id: undefined, weight: null, + hide_backlog_list: false, + hide_closed_list: false, }; export default { components: { BoardScope: () => import('ee_component/boards/components/board_scope.vue'), DeprecatedModal, + BoardConfigurationOptions, }, props: { canAdminBoard: { @@ -140,7 +145,17 @@ export default { } else { boardsStore .createBoard(this.board) - .then(resp => resp.data) + .then(resp => { + // This handles 2 use cases + // - In create call we only get one parameter, the new board + // - In update call, due to Promise.all, we get REST response in + // array index 0 + + if (Array.isArray(resp)) { + return resp[0].data; + } + return resp.data ? resp.data : resp; + }) .then(data => { visitUrl(data.board_path); }) @@ -182,7 +197,7 @@ export default { <form v-else class="js-board-config-modal" @submit.prevent> <div v-if="!readonly" class="append-bottom-20"> <label class="form-section-title label-bold" for="board-new-name">{{ - __('Board name') + __('Title') }}</label> <input id="board-new-name" @@ -196,6 +211,12 @@ export default { /> </div> + <board-configuration-options + :is-new-form="isNewForm" + :board="board" + :current-board="currentBoard" + /> + <board-scope v-if="scopedIssueBoardFeatureEnabled" :collapse-scope="isNewForm" diff --git a/app/assets/javascripts/boards/queries/board.mutation.graphql b/app/assets/javascripts/boards/queries/board.mutation.graphql new file mode 100644 index 00000000000..ef2b81a7939 --- /dev/null +++ b/app/assets/javascripts/boards/queries/board.mutation.graphql @@ -0,0 +1,11 @@ +mutation UpdateBoard($id: ID!, $hideClosedList: Boolean, $hideBacklogList: Boolean) { + updateBoard( + input: { id: $id, hideClosedList: $hideClosedList, hideBacklogList: $hideBacklogList } + ) { + board { + id + hideClosedList + hideBacklogList + } + } +} diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js index faf4f9ebfd3..cbbec70ada9 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js +++ b/app/assets/javascripts/boards/stores/boards_store.js @@ -2,7 +2,7 @@ /* global List */ /* global ListIssue */ import $ from 'jquery'; -import { sortBy } from 'lodash'; +import { sortBy, pick } from 'lodash'; import Vue from 'vue'; import Cookies from 'js-cookie'; import BoardsStoreEE from 'ee_else_ce/boards/stores/boards_store_ee'; @@ -12,6 +12,7 @@ import { parseBoolean, convertObjectPropsToCamelCase, } from '~/lib/utils/common_utils'; +import createDefaultClient from '~/lib/graphql'; import { __ } from '~/locale'; import axios from '~/lib/utils/axios_utils'; import { mergeUrlParams } from '~/lib/utils/url_utility'; @@ -23,7 +24,11 @@ import ListLabel from '../models/label'; import ListAssignee from '../models/assignee'; import ListMilestone from '../models/milestone'; +import createBoardMutation from '../queries/board.mutation.graphql'; + const PER_PAGE = 20; +export const gqlClient = createDefaultClient(); + const boardsStore = { disabled: false, timeTracking: { @@ -542,6 +547,10 @@ const boardsStore = { this.timeTracking.limitToHours = parseBoolean(limitToHours); }, + generateBoardGid(boardId) { + return `gid://gitlab/Board/${boardId}`; + }, + generateBoardsPath(id) { return `${this.state.endpoints.boardsEndpoint}${id ? `/${id}` : ''}.json`; }, @@ -800,9 +809,33 @@ const boardsStore = { } if (boardPayload.id) { - return axios.put(this.generateBoardsPath(boardPayload.id), { board: boardPayload }); + const input = { + ...pick(boardPayload, ['hideClosedList', 'hideBacklogList']), + id: this.generateBoardGid(boardPayload.id), + }; + + return Promise.all([ + axios.put(this.generateBoardsPath(boardPayload.id), { board: boardPayload }), + gqlClient.mutate({ + mutation: createBoardMutation, + variables: input, + }), + ]); } - return axios.post(this.generateBoardsPath(), { board: boardPayload }); + + return axios + .post(this.generateBoardsPath(), { board: boardPayload }) + .then(resp => resp.data) + .then(data => { + gqlClient.mutate({ + mutation: createBoardMutation, + variables: { + ...pick(boardPayload, ['hideClosedList', 'hideBacklogList']), + id: this.generateBoardGid(data.id), + }, + }); + return data; + }); }, deleteBoard({ id }) { diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue index fbf19847e9d..a2f4bea2f61 100644 --- a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue +++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue @@ -6,7 +6,6 @@ import { GlFormCheckbox, GlFormCombobox, GlFormGroup, - GlFormInput, GlFormSelect, GlFormTextarea, GlIcon, @@ -41,7 +40,6 @@ export default { GlFormCheckbox, GlFormCombobox, GlFormGroup, - GlFormInput, GlFormSelect, GlFormTextarea, GlIcon, @@ -122,11 +120,6 @@ export default { return ''; }, tokenValidationState() { - // If the feature flag is off, do not validate. Remove when flag is removed. - if (!this.glFeatures.ciKeyAutocomplete) { - return true; - } - const validator = this.$options.tokens?.[this.variable.key]?.validation; if (validator) { @@ -204,21 +197,12 @@ export default { > <form> <gl-form-combobox - v-if="glFeatures.ciKeyAutocomplete" v-model="key" :token-list="$options.tokenList" :label-text="__('Key')" data-qa-selector="ci_variable_key_field" /> - <gl-form-group v-else :label="__('Key')" label-for="ci-variable-key"> - <gl-form-input - id="ci-variable-key" - v-model="key" - data-qa-selector="ci_variable_key_field" - /> - </gl-form-group> - <gl-form-group :label="__('Value')" label-for="ci-variable-value" diff --git a/app/assets/javascripts/create_cluster/eks_cluster/components/create_eks_cluster.vue b/app/assets/javascripts/create_cluster/eks_cluster/components/create_eks_cluster.vue index 3f7c2204b9f..eb195ad2b30 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/components/create_eks_cluster.vue +++ b/app/assets/javascripts/create_cluster/eks_cluster/components/create_eks_cluster.vue @@ -13,6 +13,10 @@ export default { type: String, required: true, }, + namespacePerEnvironmentHelpPath: { + type: String, + required: true, + }, kubernetesIntegrationHelpPath: { type: String, required: true, @@ -40,6 +44,7 @@ export default { <eks-cluster-configuration-form v-if="hasCredentials" :gitlab-managed-cluster-help-path="gitlabManagedClusterHelpPath" + :namespace-per-environment-help-path="namespacePerEnvironmentHelpPath" :kubernetes-integration-help-path="kubernetesIntegrationHelpPath" :external-link-icon="externalLinkIcon" /> diff --git a/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue b/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue index a653e228e3f..0249b485e20 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue +++ b/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue @@ -37,6 +37,10 @@ export default { type: String, required: true, }, + namespacePerEnvironmentHelpPath: { + type: String, + required: true, + }, kubernetesIntegrationHelpPath: { type: String, required: true, @@ -60,6 +64,7 @@ export default { 'selectedInstanceType', 'nodeCount', 'gitlabManagedCluster', + 'namespacePerEnvironment', 'isCreatingCluster', ]), ...mapGetters(['subnetValid']), @@ -270,6 +275,20 @@ export default { false, ); }, + namespacePerEnvironmentHelpText() { + const escapedUrl = escape(this.namespacePerEnvironmentClusterHelpPath); + + return sprintf( + s__( + 'ClusterIntegration|Deploy each environment to its own namespace. Otherwise, environments within a project share a project-wide namespace. Note that anyone who can trigger a deployment of a namespace can read its secrets. If modified, existing environments will use their current namespaces until the cluster cache is cleared. %{startLink}More information%{endLink}', + ), + { + startLink: `<a href="${escapedUrl}" target="_blank" rel="noopener noreferrer">`, + endLink: '</a>', + }, + false, + ); + }, }, mounted() { this.fetchRegions(); @@ -290,6 +309,7 @@ export default { 'setInstanceType', 'setNodeCount', 'setGitlabManagedCluster', + 'setNamespacePerEnvironment', ]), ...mapRegionsActions({ fetchRegions: 'fetchItems' }), ...mapVpcActions({ fetchVpcs: 'fetchItems' }), @@ -520,6 +540,14 @@ export default { <p class="form-text text-muted" v-html="gitlabManagedHelpText"></p> </div> <div class="form-group"> + <gl-form-checkbox + :checked="namespacePerEnvironment" + @input="setNamespacePerEnvironment({ namespacePerEnvironment: $event })" + >{{ s__('ClusterIntegration|Namespace per environment') }}</gl-form-checkbox + > + <p class="form-text text-muted" v-html="namespacePerEnvironmentHelpText"></p> + </div> + <div class="form-group"> <loading-button class="js-create-cluster btn-success" :disabled="createClusterButtonDisabled" diff --git a/app/assets/javascripts/create_cluster/eks_cluster/index.js b/app/assets/javascripts/create_cluster/eks_cluster/index.js index fb993a7aa59..6d1034b4a72 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/index.js +++ b/app/assets/javascripts/create_cluster/eks_cluster/index.js @@ -9,6 +9,7 @@ Vue.use(Vuex); export default el => { const { gitlabManagedClusterHelpPath, + namespacePerEnvironmentHelpPath, kubernetesIntegrationHelpPath, accountAndExternalIdsHelpPath, createRoleArnHelpPath, @@ -42,6 +43,7 @@ export default el => { return createElement('create-eks-cluster', { props: { gitlabManagedClusterHelpPath, + namespacePerEnvironmentHelpPath, kubernetesIntegrationHelpPath, accountAndExternalIdsHelpPath, createRoleArnHelpPath, diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js b/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js index 5abff3c7831..48c85ff627f 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js +++ b/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js @@ -55,6 +55,7 @@ export const createCluster = ({ dispatch, state }) => { name: state.clusterName, environment_scope: state.environmentScope, managed: state.gitlabManagedCluster, + namespace_per_environment: state.namespacePerEnvironment, provider_aws_attributes: { kubernetes_version: state.kubernetesVersion, region: state.selectedRegion, @@ -114,6 +115,10 @@ export const setGitlabManagedCluster = ({ commit }, payload) => { commit(types.SET_GITLAB_MANAGED_CLUSTER, payload); }; +export const setNamespacePerEnvironment = ({ commit }, payload) => { + commit(types.SET_NAMESPACE_PER_ENVIRONMENT, payload); +}; + export const setInstanceType = ({ commit }, payload) => { commit(types.SET_INSTANCE_TYPE, payload); }; diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js b/app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js index 9dee6abae5f..4a48195a27b 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js +++ b/app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js @@ -10,6 +10,7 @@ export const SET_SECURITY_GROUP = 'SET_SECURITY_GROUP'; export const SET_INSTANCE_TYPE = 'SET_INSTANCE_TYPE'; export const SET_NODE_COUNT = 'SET_NODE_COUNT'; export const SET_GITLAB_MANAGED_CLUSTER = 'SET_GITLAB_MANAGED_CLUSTER'; +export const SET_NAMESPACE_PER_ENVIRONMENT = 'SET_NAMESPACE_PER_ENVIRONMENT'; export const REQUEST_CREATE_ROLE = 'REQUEST_CREATE_ROLE'; export const CREATE_ROLE_SUCCESS = 'CREATE_ROLE_SUCCESS'; export const CREATE_ROLE_ERROR = 'CREATE_ROLE_ERROR'; diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js b/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js index c331d27d255..f57236e0e31 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js +++ b/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js @@ -37,6 +37,9 @@ export default { [types.SET_GITLAB_MANAGED_CLUSTER](state, { gitlabManagedCluster }) { state.gitlabManagedCluster = gitlabManagedCluster; }, + [types.SET_NAMESPACE_PER_ENVIRONMENT](state, { namespacePerEnvironment }) { + state.namespacePerEnvironment = namespacePerEnvironment; + }, [types.REQUEST_CREATE_ROLE](state) { state.isCreatingRole = true; state.createRoleError = null; diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/state.js b/app/assets/javascripts/create_cluster/eks_cluster/store/state.js index ed51e95e434..c957eca1f7a 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/store/state.js +++ b/app/assets/javascripts/create_cluster/eks_cluster/store/state.js @@ -30,4 +30,5 @@ export default () => ({ createClusterError: false, gitlabManagedCluster: true, + namespacePerEnvironment: true, }); diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js index cc338237191..f998d5f1b7f 100644 --- a/app/assets/javascripts/gfm_auto_complete.js +++ b/app/assets/javascripts/gfm_auto_complete.js @@ -278,11 +278,13 @@ class GfmAutoComplete { return $.fn.atwho.default.callbacks.filter(query, data, searchKey); } - if (command === MEMBER_COMMAND.ASSIGN || command === MEMBER_COMMAND.REASSIGN) { + if (command === MEMBER_COMMAND.ASSIGN) { // Only include members which are not assigned to Issuable currently return data.filter( member => member.type === 'User' && !assignees.includes(member.search), ); + } else if (command === MEMBER_COMMAND.REASSIGN) { + return data.filter(member => member.type === 'User'); } else if (command === MEMBER_COMMAND.UNASSIGN) { // Only include members which are assigned to Issuable currently return data.filter(member => assignees.includes(member.search)); diff --git a/app/assets/javascripts/ide/components/activity_bar.vue b/app/assets/javascripts/ide/components/activity_bar.vue index 183816921c1..69e5cd839b4 100644 --- a/app/assets/javascripts/ide/components/activity_bar.vue +++ b/app/assets/javascripts/ide/components/activity_bar.vue @@ -32,7 +32,7 @@ export default { </script> <template> - <nav class="ide-activity-bar"> + <nav class="ide-activity-bar" data-testid="left-sidebar"> <ul class="list-unstyled"> <li> <button diff --git a/app/assets/javascripts/ide/components/editor_mode_dropdown.vue b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue index 732fa0786b0..dec8aa61838 100644 --- a/app/assets/javascripts/ide/components/editor_mode_dropdown.vue +++ b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue @@ -1,8 +1,12 @@ <script> +import { GlButton } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; import { viewerTypes } from '../constants'; export default { + components: { + GlButton, + }, props: { viewer: { type: String, @@ -31,7 +35,7 @@ export default { <template> <div class="dropdown"> - <button type="button" class="btn btn-link" data-toggle="dropdown">{{ __('Edit') }}</button> + <gl-button variant="link" data-toggle="dropdown">{{ __('Edit') }}</gl-button> <div class="dropdown-menu dropdown-menu-selectable dropdown-open-left"> <ul> <li> diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue index 659d2153187..dc4f7a77bec 100644 --- a/app/assets/javascripts/ide/components/ide_tree_list.vue +++ b/app/assets/javascripts/ide/components/ide_tree_list.vue @@ -44,7 +44,7 @@ export default { <nav-dropdown /> <slot name="header"></slot> </header> - <div class="ide-tree-body h-100"> + <div class="ide-tree-body h-100" data-testid="ide-tree-body"> <template v-if="currentTree.tree.length"> <file-tree v-for="file in currentTree.tree" diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue index 528475849de..5ad836f346a 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue @@ -152,6 +152,7 @@ export default { v-model.trim="entryName" type="text" class="form-control" + data-testid="file-name-field" data-qa-selector="file_name_field" :placeholder="placeholder" /> diff --git a/app/assets/javascripts/packages/list/components/package_title.vue b/app/assets/javascripts/packages/list/components/package_title.vue new file mode 100644 index 00000000000..e5cab310bc8 --- /dev/null +++ b/app/assets/javascripts/packages/list/components/package_title.vue @@ -0,0 +1,47 @@ +<script> +import { n__ } from '~/locale'; +import TitleArea from '~/vue_shared/components/registry/title_area.vue'; +import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue'; +import { LIST_INTRO_TEXT, LIST_TITLE_TEXT } from '../constants'; + +export default { + name: 'PackageTitle', + components: { + TitleArea, + MetadataItem, + }, + props: { + packagesCount: { + type: Number, + required: false, + default: null, + }, + packageHelpUrl: { + type: String, + required: true, + }, + }, + computed: { + showPackageCount() { + return Number.isInteger(this.packagesCount); + }, + packageAmountText() { + return n__(`%d Package`, `%d Packages`, this.packagesCount); + }, + infoMessages() { + return [{ text: LIST_INTRO_TEXT, link: this.packageHelpUrl }]; + }, + }, + i18n: { + LIST_TITLE_TEXT, + }, +}; +</script> + +<template> + <title-area :title="$options.i18n.LIST_TITLE_TEXT" :info-messages="infoMessages"> + <template #metadata_amount> + <metadata-item v-if="showPackageCount" icon="package" :text="packageAmountText" /> + </template> + </title-area> +</template> diff --git a/app/assets/javascripts/packages/list/components/packages_list_app.vue b/app/assets/javascripts/packages/list/components/packages_list_app.vue index 6304f723f6a..ad60ee6f379 100644 --- a/app/assets/javascripts/packages/list/components/packages_list_app.vue +++ b/app/assets/javascripts/packages/list/components/packages_list_app.vue @@ -3,13 +3,14 @@ import { mapActions, mapState } from 'vuex'; import { GlEmptyState, GlTab, GlTabs, GlLink, GlSprintf } from '@gitlab/ui'; import { s__, sprintf } from '~/locale'; import createFlash from '~/flash'; +import { historyReplaceState } from '~/lib/utils/common_utils'; +import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages/shared/constants'; import PackageFilter from './packages_filter.vue'; import PackageList from './packages_list.vue'; import PackageSort from './packages_sort.vue'; import { PACKAGE_REGISTRY_TABS, DELETE_PACKAGE_SUCCESS_MESSAGE } from '../constants'; import PackagesComingSoon from '../coming_soon/packages_coming_soon.vue'; -import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages/shared/constants'; -import { historyReplaceState } from '~/lib/utils/common_utils'; +import PackageTitle from './package_title.vue'; export default { components: { @@ -22,6 +23,7 @@ export default { PackageList, PackageSort, PackagesComingSoon, + PackageTitle, }, computed: { ...mapState({ @@ -30,6 +32,8 @@ export default { comingSoon: state => state.config.comingSoon, filterQuery: state => state.filterQuery, selectedType: state => state.selectedType, + packageHelpUrl: state => state.config.packageHelpUrl, + packagesCount: state => state.pagination?.total, }), tabsToRender() { return PACKAGE_REGISTRY_TABS; @@ -89,39 +93,43 @@ export default { </script> <template> - <gl-tabs @input="tabChanged"> - <template #tabs-end> - <div - class="gl-display-flex gl-align-self-center gl-py-2 gl-flex-grow-1 gl-justify-content-end" - > - <package-filter class="mr-1" @filter="requestPackagesList" /> - <package-sort @sort:changed="requestPackagesList" /> - </div> - </template> + <div> + <package-title :package-help-url="packageHelpUrl" :packages-count="packagesCount" /> + + <gl-tabs @input="tabChanged"> + <template #tabs-end> + <div + class="gl-display-flex gl-align-self-center gl-py-2 gl-flex-grow-1 gl-justify-content-end" + > + <package-filter class="gl-mr-2" @filter="requestPackagesList" /> + <package-sort @sort:changed="requestPackagesList" /> + </div> + </template> - <gl-tab v-for="(tab, index) in tabsToRender" :key="index" :title="tab.title"> - <package-list @page:changed="onPageChanged" @package:delete="onPackageDeleteRequest"> - <template #empty-state> - <gl-empty-state :title="emptyStateTitle(tab)" :svg-path="emptyListIllustration"> - <template #description> - <gl-sprintf v-if="filterQuery" :message="$options.i18n.widenFilters" /> - <gl-sprintf v-else :message="$options.i18n.noResults"> - <template #noPackagesLink="{content}"> - <gl-link :href="emptyListHelpUrl" target="_blank">{{ content }}</gl-link> - </template> - </gl-sprintf> - </template> - </gl-empty-state> - </template> - </package-list> - </gl-tab> + <gl-tab v-for="(tab, index) in tabsToRender" :key="index" :title="tab.title"> + <package-list @page:changed="onPageChanged" @package:delete="onPackageDeleteRequest"> + <template #empty-state> + <gl-empty-state :title="emptyStateTitle(tab)" :svg-path="emptyListIllustration"> + <template #description> + <gl-sprintf v-if="filterQuery" :message="$options.i18n.widenFilters" /> + <gl-sprintf v-else :message="$options.i18n.noResults"> + <template #noPackagesLink="{content}"> + <gl-link :href="emptyListHelpUrl" target="_blank">{{ content }}</gl-link> + </template> + </gl-sprintf> + </template> + </gl-empty-state> + </template> + </package-list> + </gl-tab> - <gl-tab v-if="comingSoon" :title="__('Coming soon')" lazy> - <packages-coming-soon - :illustration="emptyListIllustration" - :project-path="comingSoon.projectPath" - :suggested-contributions-path="comingSoon.suggestedContributions" - /> - </gl-tab> - </gl-tabs> + <gl-tab v-if="comingSoon" :title="__('Coming soon')" lazy> + <packages-coming-soon + :illustration="emptyListIllustration" + :project-path="comingSoon.projectPath" + :suggested-contributions-path="comingSoon.suggestedContributions" + /> + </gl-tab> + </gl-tabs> + </div> </template> diff --git a/app/assets/javascripts/packages/list/constants.js b/app/assets/javascripts/packages/list/constants.js index 366b674ae72..37242822e35 100644 --- a/app/assets/javascripts/packages/list/constants.js +++ b/app/assets/javascripts/packages/list/constants.js @@ -86,3 +86,9 @@ export const PACKAGE_REGISTRY_TABS = [ type: PackageType.PYPI, }, ]; + +export const LIST_TITLE_TEXT = s__('PackageRegistry|Package Registry'); + +export const LIST_INTRO_TEXT = s__( + 'PackageRegistry|Publish and share packages for a variety of common package managers. %{docLinkStart}More information%{docLinkEnd}', +); diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_actions.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_actions.vue index 208df03b6a4..b90cbfd1a1a 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_actions.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_actions.vue @@ -74,9 +74,6 @@ export default { canBeManuallyRedeployed() { return this.computedDeploymentStatus === FAILED && Boolean(this.redeployPath); }, - shouldShowManualButtons() { - return this.glFeatures.deployFromFooter; - }, hasExternalUrls() { return Boolean(this.deployment.external_url && this.deployment.external_url_formatted); }, @@ -154,7 +151,7 @@ export default { <template> <div> <deployment-action-button - v-if="shouldShowManualButtons && canBeManuallyDeployed" + v-if="canBeManuallyDeployed" :action-in-progress="actionInProgress" :actions-configuration="$options.actionsConfiguration[constants.DEPLOYING]" :computed-deployment-status="computedDeploymentStatus" @@ -165,7 +162,7 @@ export default { <span>{{ $options.actionsConfiguration[constants.DEPLOYING].buttonText }}</span> </deployment-action-button> <deployment-action-button - v-if="shouldShowManualButtons && canBeManuallyRedeployed" + v-if="canBeManuallyRedeployed" :action-in-progress="actionInProgress" :actions-configuration="$options.actionsConfiguration[constants.REDEPLOYING]" :computed-deployment-status="computedDeploymentStatus" diff --git a/app/assets/javascripts/vue_shared/components/gl_mentions.vue b/app/assets/javascripts/vue_shared/components/gl_mentions.vue index b90cc24387c..d15a2b47bff 100644 --- a/app/assets/javascripts/vue_shared/components/gl_mentions.vue +++ b/app/assets/javascripts/vue_shared/components/gl_mentions.vue @@ -65,16 +65,13 @@ const autoCompleteMap = { SidebarMediator.singleton?.store?.assignees?.map(assignee => assignee.username) || []; } - if ( - doesCurrentLineStartWith('/assign', fullText, selectionStart) || - doesCurrentLineStartWith('/reassign', fullText, selectionStart) - ) { + if (doesCurrentLineStartWith('/assign', fullText, selectionStart)) { return this.members.filter( member => member.type === 'User' && !this.assignees.includes(member.username), ); - } - - if (doesCurrentLineStartWith('/unassign', fullText, selectionStart)) { + } else if (doesCurrentLineStartWith('/reassign', fullText, selectionStart)) { + return this.members.filter(member => member.type === 'User'); + } else if (doesCurrentLineStartWith('/unassign', fullText, selectionStart)) { return this.members.filter(member => this.assignees.includes(member.username)); } diff --git a/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss b/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss index 1e239877428..93cb9be4a8f 100644 --- a/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss +++ b/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss @@ -6,9 +6,10 @@ $bs-input-focus-box-shadow: rgba(0, 123, 255, 0.25); a:not(.btn), - .btn-link:hover, - .btn-link:focus, - .btn-link:active { + .gl-button.btn-link, + .gl-button.btn-link:hover, + .gl-button.btn-link:focus, + .gl-button.btn-link:active { color: var(--ide-link-color, $blue-600); } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index a2f8447c0b6..1d7b536f012 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -794,11 +794,6 @@ } .project-buttons { - .stat-text { - @extend .btn; - @extend .btn-default; - } - .nav > li:not(:last-child) { margin-right: $gl-padding-8; } diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb index 7006c23321c..52719e90e04 100644 --- a/app/controllers/clusters/clusters_controller.rb +++ b/app/controllers/clusters/clusters_controller.rb @@ -180,13 +180,20 @@ class Clusters::ClustersController < Clusters::BaseController params.permit(:cleanup) end + def base_permitted_cluster_params + [ + :enabled, + :environment_scope, + :managed, + :namespace_per_environment + ] + end + def update_params if cluster.provided_by_user? params.require(:cluster).permit( - :enabled, + *base_permitted_cluster_params, :name, - :environment_scope, - :managed, :base_domain, :management_project_id, platform_kubernetes_attributes: [ @@ -198,9 +205,7 @@ class Clusters::ClustersController < Clusters::BaseController ) else params.require(:cluster).permit( - :enabled, - :environment_scope, - :managed, + *base_permitted_cluster_params, :base_domain, :management_project_id, platform_kubernetes_attributes: [ @@ -212,10 +217,8 @@ class Clusters::ClustersController < Clusters::BaseController def create_gcp_cluster_params params.require(:cluster).permit( - :enabled, + *base_permitted_cluster_params, :name, - :environment_scope, - :managed, provider_gcp_attributes: [ :gcp_project_id, :zone, @@ -232,10 +235,8 @@ class Clusters::ClustersController < Clusters::BaseController def create_aws_cluster_params params.require(:cluster).permit( - :enabled, + *base_permitted_cluster_params, :name, - :environment_scope, - :managed, provider_aws_attributes: [ :kubernetes_version, :key_name, @@ -255,10 +256,8 @@ class Clusters::ClustersController < Clusters::BaseController def create_user_cluster_params params.require(:cluster).permit( - :enabled, + *base_permitted_cluster_params, :name, - :environment_scope, - :managed, platform_kubernetes_attributes: [ :namespace, :api_url, diff --git a/app/controllers/import/bulk_imports_controller.rb b/app/controllers/import/bulk_imports_controller.rb new file mode 100644 index 00000000000..58b9f8c0fbb --- /dev/null +++ b/app/controllers/import/bulk_imports_controller.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +class Import::BulkImportsController < ApplicationController + before_action :ensure_group_import_enabled + before_action :verify_blocked_uri, only: :status + + def configure + session[access_token_key] = params[access_token_key]&.strip + session[url_key] = params[url_key] + + redirect_to status_import_bulk_import_url + end + + private + + def import_params + params.permit(access_token_key, url_key) + end + + def ensure_group_import_enabled + render_404 unless Feature.enabled?(:bulk_import) + end + + def access_token_key + :bulk_import_gitlab_access_token + end + + def url_key + :bulk_import_gitlab_url + end + + def verify_blocked_uri + Gitlab::UrlBlocker.validate!( + session[url_key], + **{ + allow_localhost: allow_local_requests?, + allow_local_network: allow_local_requests?, + schemes: %w(http https) + } + ) + rescue Gitlab::UrlBlocker::BlockedUrlError => e + session[access_token_key] = nil + session[url_key] = nil + + redirect_to new_group_path, alert: _('Specified URL cannot be used: "%{reason}"') % { reason: e.message } + end + + def allow_local_requests? + Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services? + end +end diff --git a/app/controllers/projects/ci/daily_build_group_report_results_controller.rb b/app/controllers/projects/ci/daily_build_group_report_results_controller.rb index 5fbfd3a9ed9..3d3b62fa797 100644 --- a/app/controllers/projects/ci/daily_build_group_report_results_controller.rb +++ b/app/controllers/projects/ci/daily_build_group_report_results_controller.rb @@ -38,7 +38,7 @@ class Projects::Ci::DailyBuildGroupReportResultsController < Projects::Applicati end def report_results - Ci::DailyBuildGroupReportResultsFinder.new(finder_params).execute + Ci::DailyBuildGroupReportResultsFinder.new(**finder_params).execute end def finder_params diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index e2fa373a88c..c5fa92ebf32 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -27,7 +27,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo before_action :authenticate_user!, only: [:assign_related_issues] before_action :check_user_can_push_to_source_branch!, only: [:rebase] before_action only: [:show] do - push_frontend_feature_flag(:deploy_from_footer, @project, default_enabled: true) push_frontend_feature_flag(:suggest_pipeline) if experiment_enabled?(:suggest_pipeline) push_frontend_feature_flag(:widget_visibility_polling, @project, default_enabled: true) push_frontend_feature_flag(:merge_ref_head_comments, @project, default_enabled: true) diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index 9576fe4c420..7efc78a3b1d 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -8,7 +8,6 @@ module Projects before_action do push_frontend_feature_flag(:new_variables_ui, @project, default_enabled: true) push_frontend_feature_flag(:ajax_new_deploy_token, @project) - push_frontend_feature_flag(:ci_key_autocomplete, default_enabled: true) end def show diff --git a/app/helpers/analytics/navbar_helper.rb b/app/helpers/analytics/navbar_helper.rb index ddf2655c887..bc0b5e7c74f 100644 --- a/app/helpers/analytics/navbar_helper.rb +++ b/app/helpers/analytics/navbar_helper.rb @@ -28,7 +28,7 @@ module Analytics private def navbar_sub_item(args) - NavbarSubItem.new(args) + NavbarSubItem.new(**args) end def cycle_analytics_navbar_link(project, current_user) diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb index 68dbc5b65d1..5457f96d506 100644 --- a/app/helpers/avatars_helper.rb +++ b/app/helpers/avatars_helper.rb @@ -60,7 +60,7 @@ module AvatarsHelper avatar_size = options[:size] || 16 user_name = options[:user].try(:name) || options[:user_name] - avatar_url = user_avatar_url_for(options.merge(size: avatar_size)) + avatar_url = user_avatar_url_for(**options.merge(size: avatar_size)) has_tooltip = options[:has_tooltip].nil? ? true : options[:has_tooltip] data_attributes = options[:data] || {} diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb index 81451e398f2..8cf5cd49322 100644 --- a/app/helpers/namespaces_helper.rb +++ b/app/helpers/namespaces_helper.rb @@ -53,7 +53,7 @@ module NamespacesHelper selected = options.delete(:selected) || :current_user options[:groups] = current_user.manageable_groups_with_routes(include_groups_with_developer_maintainer_access: true) - namespaces_options(selected, options) + namespaces_options(selected, **options) end private diff --git a/app/helpers/packages_helper.rb b/app/helpers/packages_helper.rb index d7ceedefd85..ce20442ff81 100644 --- a/app/helpers/packages_helper.rb +++ b/app/helpers/packages_helper.rb @@ -53,7 +53,8 @@ module PackagesHelper page_type: type, empty_list_help_url: help_page_path('user/packages/package_registry/index'), empty_list_illustration: image_path('illustrations/no-packages.svg'), - coming_soon_json: packages_coming_soon_data(resource).to_json + coming_soon_json: packages_coming_soon_data(resource).to_json, + package_help_url: help_page_path('user/packages/index') } end end diff --git a/app/models/ci/build_pending_state.rb b/app/models/ci/build_pending_state.rb index 45f323adec2..299c67f441d 100644 --- a/app/models/ci/build_pending_state.rb +++ b/app/models/ci/build_pending_state.rb @@ -9,4 +9,10 @@ class Ci::BuildPendingState < ApplicationRecord enum failure_reason: CommitStatus.failure_reasons validates :build, presence: true + + def crc32 + trace_checksum.try do |checksum| + checksum.to_s.split('crc32:').last.to_i(16) + end + end end diff --git a/app/serializers/cluster_entity.rb b/app/serializers/cluster_entity.rb index 9872bbf80b5..b904666971e 100644 --- a/app/serializers/cluster_entity.rb +++ b/app/serializers/cluster_entity.rb @@ -7,6 +7,7 @@ class ClusterEntity < Grape::Entity expose :enabled expose :environment_scope expose :id + expose :namespace_per_environment expose :name expose :nodes expose :provider_type diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb index 70ad18e80eb..3f1a2d1350d 100644 --- a/app/services/ci/create_pipeline_service.rb +++ b/app/services/ci/create_pipeline_service.rb @@ -82,8 +82,7 @@ module Ci schedule_head_pipeline_update if pipeline.persisted? # If pipeline is not persisted, try to recover IID - pipeline.reset_project_iid unless pipeline.persisted? || - Feature.disabled?(:ci_pipeline_rewind_iid, project, default_enabled: true) + pipeline.reset_project_iid unless pipeline.persisted? pipeline end diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb index 405b8fe9c9e..0873c20b99c 100644 --- a/app/services/merge_requests/refresh_service.rb +++ b/app/services/merge_requests/refresh_service.rb @@ -184,7 +184,7 @@ module MergeRequests def abort_auto_merge_with_todo(merge_request, reason) response = abort_auto_merge(merge_request, reason) - response = ServiceResponse.new(response) + response = ServiceResponse.new(**response) return unless response.success? todo_service.merge_request_became_unmergeable(merge_request) diff --git a/app/services/projects/container_repository/delete_tags_service.rb b/app/services/projects/container_repository/delete_tags_service.rb index a23a6a369b2..2dfe86e4d66 100644 --- a/app/services/projects/container_repository/delete_tags_service.rb +++ b/app/services/projects/container_repository/delete_tags_service.rb @@ -23,9 +23,7 @@ module Projects end def delete_service - fast_delete_enabled = Feature.enabled?(:container_registry_fast_tag_delete, default_enabled: true) - - if fast_delete_enabled && @container_repository.client.supports_tag_delete? + if @container_repository.client.supports_tag_delete? ::Projects::ContainerRepository::Gitlab::DeleteTagsService.new(@container_repository, @tag_names) else ::Projects::ContainerRepository::ThirdParty::DeleteTagsService.new(@container_repository, @tag_names) diff --git a/app/views/clusters/clusters/_provider_details_form.html.haml b/app/views/clusters/clusters/_provider_details_form.html.haml index fcb5d4402d6..16891c7fc21 100644 --- a/app/views/clusters/clusters/_provider_details_form.html.haml +++ b/app/views/clusters/clusters/_provider_details_form.html.haml @@ -42,11 +42,17 @@ class: 'js-gl-managed', label_class: 'label-bold' } .form-text.text-muted - = s_('ClusterIntegration|Allow GitLab to manage namespace and service accounts for this cluster.') + = s_('ClusterIntegration|Allow GitLab to manage namespaces and service accounts for this cluster.') = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'gitlab-managed-clusters'), target: '_blank' + .form-group + = field.check_box :namespace_per_environment, { label: s_('ClusterIntegration|Namespace per environment'), label_class: 'label-bold' } + .form-text.text-muted + = s_('ClusterIntegration|Deploy each environment to its own namespace. Otherwise, environments within a project share a project-wide namespace. Note that anyone who can trigger a deployment of a namespace can read its secrets. If modified, existing environments will use their current namespaces until the cluster cache is cleared.') + = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'custom-namespace'), target: '_blank' + - if cluster.allow_user_defined_namespace? - = render('clusters/clusters/namespace', platform_field: platform_field) + = render('clusters/clusters/namespace', platform_field: platform_field, field: field) .form-group = field.submit s_('ClusterIntegration|Save changes'), class: 'btn btn-success' diff --git a/app/views/clusters/clusters/aws/_new.html.haml b/app/views/clusters/clusters/aws/_new.html.haml index 3eab9b46fb3..b1a277faae9 100644 --- a/app/views/clusters/clusters/aws/_new.html.haml +++ b/app/views/clusters/clusters/aws/_new.html.haml @@ -4,6 +4,7 @@ = s_('Amazon authentication is not %{link_start}correctly configured%{link_end}. Ask your GitLab administrator if you want to use this service.').html_safe % { link_start: documentation_link_start, link_end: '<a/>'.html_safe } - else .js-create-eks-cluster-form-container{ data: { 'gitlab-managed-cluster-help-path' => help_page_path('user/project/clusters/index.md', anchor: 'gitlab-managed-clusters'), + 'namespace-per-environment-help-path' => help_page_path('user/project/clusters/index.md', anchor: 'custom-namespace'), 'create-role-path' => clusterable.authorize_aws_role_path, 'create-cluster-path' => clusterable.create_aws_clusters_path, 'account-id' => Gitlab::CurrentSettings.eks_account_id, diff --git a/app/views/clusters/clusters/gcp/_form.html.haml b/app/views/clusters/clusters/gcp/_form.html.haml index 434c02a5c41..ceb6e1d46b0 100644 --- a/app/views/clusters/clusters/gcp/_form.html.haml +++ b/app/views/clusters/clusters/gcp/_form.html.haml @@ -75,9 +75,15 @@ = field.check_box :managed, { label: s_('ClusterIntegration|GitLab-managed cluster'), label_class: 'label-bold' } .form-text.text-muted - = s_('ClusterIntegration|Allow GitLab to manage namespace and service accounts for this cluster.') + = s_('ClusterIntegration|Allow GitLab to manage namespaces and service accounts for this cluster.') = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'gitlab-managed-clusters'), target: '_blank' + .form-group + = field.check_box :namespace_per_environment, { label: s_('ClusterIntegration|Namespace per environment'), label_class: 'label-bold' } + .form-text.text-muted + = s_('ClusterIntegration|Deploy each environment to its own namespace. Otherwise, environments within a project share a project-wide namespace. Note that anyone who can trigger a deployment of a namespace can read its secrets. If modified, existing environments will use their current namespaces until the cluster cache is cleared.') + = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'custom-namespace'), target: '_blank' + .form-group.js-gke-cluster-creation-submit-container = field.submit s_('ClusterIntegration|Create Kubernetes cluster'), class: 'js-gke-cluster-creation-submit btn btn-success', disabled: true diff --git a/app/views/clusters/clusters/user/_form.html.haml b/app/views/clusters/clusters/user/_form.html.haml index 11772107135..a6097038b2e 100644 --- a/app/views/clusters/clusters/user/_form.html.haml +++ b/app/views/clusters/clusters/user/_form.html.haml @@ -46,9 +46,15 @@ class: 'js-gl-managed', label_class: 'label-bold' } .form-text.text-muted - = s_('ClusterIntegration|Allow GitLab to manage namespace and service accounts for this cluster.') + = s_('ClusterIntegration|Allow GitLab to manage namespaces and service accounts for this cluster.') = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'gitlab-managed-clusters'), target: '_blank' + .form-group + = field.check_box :namespace_per_environment, { label: s_('ClusterIntegration|Namespace per environment'), label_class: 'label-bold' } + .form-text.text-muted + = s_('ClusterIntegration|Deploy each environment to its own namespace. Otherwise, environments within a project share a project-wide namespace. Note that anyone who can trigger a deployment of a namespace can read its secrets. If modified, existing environments will use their current namespaces until the cluster cache is cleared.') + = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'custom-namespace'), target: '_blank' + = field.fields_for :platform_kubernetes, @user_cluster.platform_kubernetes do |platform_kubernetes_field| - if @user_cluster.allow_user_defined_namespace? = render('clusters/clusters/namespace', platform_field: platform_kubernetes_field) diff --git a/app/views/import/bulk_imports/status.html.haml b/app/views/import/bulk_imports/status.html.haml new file mode 100644 index 00000000000..d909f6a13f0 --- /dev/null +++ b/app/views/import/bulk_imports/status.html.haml @@ -0,0 +1 @@ +- page_title 'Bulk Import' diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 40ea42091bd..a8b4fee47c4 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -3,7 +3,7 @@ %ul.list-unstyled.navbar-sub-nav - if dashboard_nav_link?(:projects) = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: { id: 'nav-projects-dropdown', class: "home dropdown header-projects qa-projects-dropdown", data: { track_label: "projects_dropdown", track_event: "click_dropdown", track_value: "" } }) do - %button.btn{ type: 'button', data: { toggle: "dropdown" } } + %button{ type: 'button', data: { toggle: "dropdown" } } = _('Projects') = sprite_icon('angle-down', css_class: 'caret-down') .dropdown-menu.frequent-items-dropdown-menu @@ -11,7 +11,7 @@ - if dashboard_nav_link?(:groups) = nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { id: 'nav-groups-dropdown', class: "d-none d-md-block home dropdown header-groups qa-groups-dropdown", data: { track_label: "groups_dropdown", track_event: "click_dropdown", track_value: "" } }) do - %button.btn{ type: 'button', data: { toggle: "dropdown" } } + %button{ type: 'button', data: { toggle: "dropdown" } } = _('Groups') = sprite_icon('angle-down', css_class: 'caret-down') .dropdown-menu.frequent-items-dropdown-menu diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml index 1562cc065f1..81c42de13f0 100644 --- a/app/views/projects/_files.html.haml +++ b/app/views/projects/_files.html.haml @@ -14,7 +14,7 @@ - if is_project_overview .project-buttons.gl-mb-3.js-show-on-project-root - = render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout) + = render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout), project_buttons: true #js-tree-list{ data: vue_file_list_data(project, ref) } - if can_edit_tree? diff --git a/app/views/projects/_stat_anchor_list.html.haml b/app/views/projects/_stat_anchor_list.html.haml index 8e3d759b683..516790fb6d9 100644 --- a/app/views/projects/_stat_anchor_list.html.haml +++ b/app/views/projects/_stat_anchor_list.html.haml @@ -1,8 +1,9 @@ - anchors = local_assigns.fetch(:anchors, []) +- project_buttons = local_assigns.fetch(:project_buttons, false) - return unless anchors.any? %ul.nav - anchors.each do |anchor| %li.nav-item = link_to_if anchor.link, anchor.label, anchor.link, class: anchor.is_link ? 'nav-link stat-link d-flex align-items-center' : "nav-link btn btn-#{anchor.class_modifier || 'missing'} d-flex align-items-center" do - .stat-text.d-flex.align-items-center= anchor.label + .stat-text.d-flex.align-items-center{ class: ('btn btn-default disabled' if project_buttons) }= anchor.label diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index c9edc3c12ec..3f19d4db1cc 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -20,7 +20,7 @@ .project-clone-holder.d-none.d-md-inline-block.mt-2.mr-2.float-left = render "projects/buttons/clone" - = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons + = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons, project_buttons: true - if can?(current_user, :push_code, @project) .empty-wrapper.gl-mt-7 diff --git a/app/views/projects/forks/_fork_button.html.haml b/app/views/projects/forks/_fork_button.html.haml index dd49e8bdb4b..cfef2a19420 100644 --- a/app/views/projects/forks/_fork_button.html.haml +++ b/app/views/projects/forks/_fork_button.html.haml @@ -10,11 +10,11 @@ %h5.gl-mt-3 = namespace.human_name - if forked_project = namespace.find_fork_of(@project) - = link_to _("Go to project"), project_path(forked_project), class: "btn" + = link_to _("Go to project"), project_path(forked_project), class: "btn gl-button btn-default" - else %div{ class: ('has-tooltip' unless can_create_project), title: (_('You have reached your project limit') unless can_create_project) } = link_to _("Select"), project_forks_path(@project, namespace_key: namespace.id), data: { qa_selector: 'fork_namespace_button', qa_name: namespace.human_name }, method: "POST", - class: ["btn btn-success", ("disabled" unless can_create_project)] + class: ["btn gl-button btn-success", ("disabled" unless can_create_project)] diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml index 8384561891a..67dc07fb785 100644 --- a/app/views/projects/forks/index.html.haml +++ b/app/views/projects/forks/index.html.haml @@ -30,11 +30,11 @@ - if current_user && can?(current_user, :fork_project, @project) - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 - = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: _('Go to your fork'), class: 'btn btn-success' do + = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: _('Go to your fork'), class: 'btn gl-button btn-success' do = sprite_icon('fork', size: 12) %span= _('Fork') - else - = link_to new_project_fork_path(@project), title: _("Fork project"), class: 'btn btn-success' do + = link_to new_project_fork_path(@project), title: _("Fork project"), class: 'btn gl-button btn-success' do = sprite_icon('fork', size: 12) %span= _('Fork') diff --git a/changelogs/unreleased/225164-add-a-title-section-to-the-package-registry-ui.yml b/changelogs/unreleased/225164-add-a-title-section-to-the-package-registry-ui.yml new file mode 100644 index 00000000000..866b99a4e61 --- /dev/null +++ b/changelogs/unreleased/225164-add-a-title-section-to-the-package-registry-ui.yml @@ -0,0 +1,5 @@ +--- +title: Add a title section to the Package Registry UI +merge_request: 42963 +author: +type: changed diff --git a/changelogs/unreleased/229284-migrate-bootstrap-button-to-gitlab-ui-glbutton-in-app-assets-javas.yml b/changelogs/unreleased/229284-migrate-bootstrap-button-to-gitlab-ui-glbutton-in-app-assets-javas.yml new file mode 100644 index 00000000000..0e05ddd0a8a --- /dev/null +++ b/changelogs/unreleased/229284-migrate-bootstrap-button-to-gitlab-ui-glbutton-in-app-assets-javas.yml @@ -0,0 +1,5 @@ +--- +title: Update IDE compare changes view button to link style +merge_request: 43403 +author: +type: other diff --git a/changelogs/unreleased/250685-fix-carets.yml b/changelogs/unreleased/250685-fix-carets.yml new file mode 100644 index 00000000000..52885668f88 --- /dev/null +++ b/changelogs/unreleased/250685-fix-carets.yml @@ -0,0 +1,5 @@ +--- +title: Fix caret sizes in navigation +merge_request: 42605 +author: +type: fixed diff --git a/changelogs/unreleased/expose-clusters-namespace-per-environment-flag.yml b/changelogs/unreleased/expose-clusters-namespace-per-environment-flag.yml new file mode 100644 index 00000000000..99c630b5904 --- /dev/null +++ b/changelogs/unreleased/expose-clusters-namespace-per-environment-flag.yml @@ -0,0 +1,6 @@ +--- +title: Expose the option to use namespace-per-project instead of namespace-per-environment + for Kubernetes clusters +merge_request: 42309 +author: +type: changed diff --git a/changelogs/unreleased/gitlab_buttons_projects_forks.yml b/changelogs/unreleased/gitlab_buttons_projects_forks.yml new file mode 100644 index 00000000000..0cb67451146 --- /dev/null +++ b/changelogs/unreleased/gitlab_buttons_projects_forks.yml @@ -0,0 +1,5 @@ +--- +title: Apply GitLab UI button styles to HAML buttons app/views/projects/forks +merge_request: 43101 +author: Andrei Kyrnich @kyrnich +type: other diff --git a/config/feature_flags/development/bulk_import.yml b/config/feature_flags/development/bulk_import.yml new file mode 100644 index 00000000000..0a5a1e5654e --- /dev/null +++ b/config/feature_flags/development/bulk_import.yml @@ -0,0 +1,7 @@ +--- +name: bulk_import +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42704 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/255310 +group: group::import +type: development +default_enabled: false diff --git a/config/feature_flags/development/ci_key_autocomplete.yml b/config/feature_flags/development/ci_key_autocomplete.yml deleted file mode 100644 index 00d21a1ff44..00000000000 --- a/config/feature_flags/development/ci_key_autocomplete.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: ci_key_autocomplete -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29124 -rollout_issue_url: -group: group::progressive delivery -type: development -default_enabled: true diff --git a/config/feature_flags/development/ci_pipeline_rewind_iid.yml b/config/feature_flags/development/ci_pipeline_rewind_iid.yml deleted file mode 100644 index 8b6bb378a0a..00000000000 --- a/config/feature_flags/development/ci_pipeline_rewind_iid.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: ci_pipeline_rewind_iid -introduced_by_url: -rollout_issue_url: -group: -type: development -default_enabled: true diff --git a/config/feature_flags/development/ci_skip_persistent_ref_existence_check.yml b/config/feature_flags/development/ci_skip_persistent_ref_existence_check.yml index a9a79f80512..60c626295ab 100644 --- a/config/feature_flags/development/ci_skip_persistent_ref_existence_check.yml +++ b/config/feature_flags/development/ci_skip_persistent_ref_existence_check.yml @@ -1,7 +1,7 @@ --- name: ci_skip_persistent_ref_existence_check -introduced_by_url: +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32391 rollout_issue_url: -group: +group: group::continuous integration type: development default_enabled: false diff --git a/config/feature_flags/development/ci_synchronous_artifact_parsing.yml b/config/feature_flags/development/ci_synchronous_artifact_parsing.yml index c5d1a44b61f..795ac08c7e9 100644 --- a/config/feature_flags/development/ci_synchronous_artifact_parsing.yml +++ b/config/feature_flags/development/ci_synchronous_artifact_parsing.yml @@ -1,7 +1,7 @@ --- name: ci_synchronous_artifact_parsing -introduced_by_url: +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26247 rollout_issue_url: -group: +group: group::progressive delivery type: development default_enabled: true diff --git a/config/feature_flags/development/container_registry_fast_tag_delete.yml b/config/feature_flags/development/container_registry_fast_tag_delete.yml deleted file mode 100644 index bc318693460..00000000000 --- a/config/feature_flags/development/container_registry_fast_tag_delete.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: container_registry_fast_tag_delete -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23325 -rollout_issue_url: -group: group::package -type: development -default_enabled: true diff --git a/config/feature_flags/development/deploy_from_footer.yml b/config/feature_flags/development/deploy_from_footer.yml deleted file mode 100644 index 07fdcdc6c98..00000000000 --- a/config/feature_flags/development/deploy_from_footer.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: deploy_from_footer -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25427 -rollout_issue_url: -group: group::progressive delivery -type: development -default_enabled: true diff --git a/config/feature_flags/development/group_export_ndjson.yml b/config/feature_flags/development/group_export_ndjson.yml index e156d1e0299..3f7f61672f4 100644 --- a/config/feature_flags/development/group_export_ndjson.yml +++ b/config/feature_flags/development/group_export_ndjson.yml @@ -2,6 +2,6 @@ name: group_export_ndjson introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29590 rollout_issue_url: -group: import +group: group::import type: development default_enabled: true diff --git a/config/feature_flags/development/group_import_ndjson.yml b/config/feature_flags/development/group_import_ndjson.yml index 4a38debe286..98f0d4722e4 100644 --- a/config/feature_flags/development/group_import_ndjson.yml +++ b/config/feature_flags/development/group_import_ndjson.yml @@ -2,6 +2,6 @@ name: group_import_ndjson introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29716 rollout_issue_url: -group: import +group: group::import type: development default_enabled: true diff --git a/config/feature_flags/development/merge_orchestration_service.yml b/config/feature_flags/development/merge_orchestration_service.yml index 134553e7344..2bdfe9b52a4 100644 --- a/config/feature_flags/development/merge_orchestration_service.yml +++ b/config/feature_flags/development/merge_orchestration_service.yml @@ -1,7 +1,7 @@ --- name: merge_orchestration_service -introduced_by_url: +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28532 rollout_issue_url: -group: +group: group::continuous integration type: development default_enabled: true diff --git a/config/feature_flags/development/settings_operations_prometheus_service.yml b/config/feature_flags/development/settings_operations_prometheus_service.yml index 87da3448143..1a4815a3fb9 100644 --- a/config/feature_flags/development/settings_operations_prometheus_service.yml +++ b/config/feature_flags/development/settings_operations_prometheus_service.yml @@ -1,6 +1,6 @@ --- name: settings_operations_prometheus_service -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24727 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24296 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/258560 group: group::health type: development diff --git a/config/routes/import.rb b/config/routes/import.rb index 0d3f202ba55..3ee44aa8659 100644 --- a/config/routes/import.rb +++ b/config/routes/import.rb @@ -69,6 +69,11 @@ namespace :import do post :authorize end + resource :bulk_import, only: [:create] do + post :configure + get :status + end + resource :manifest, only: [:create, :new], controller: :manifest do get :status get :realtime_changes diff --git a/doc/api/container_registry.md b/doc/api/container_registry.md index 71a5cf56684..366c0b3ddc0 100644 --- a/doc/api/container_registry.md +++ b/doc/api/container_registry.md @@ -24,7 +24,6 @@ GET /projects/:id/registry/repositories | --------- | ---- | -------- | ----------- | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) accessible by the authenticated user. | | `tags` | boolean | no | If the parameter is included as true, each repository will include an array of `"tags"` in the response. | -| `name` | string | no | Returns a list of repositories with a name that matches the value. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29763) in GitLab 13.0). | | `tags_count` | boolean | no | If the parameter is included as true, each repository will include `"tags_count"` in the response ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32141) in GitLab 13.1). | ```shell @@ -68,7 +67,6 @@ GET /groups/:id/registry/repositories | --------- | ---- | -------- | ----------- | | `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) accessible by the authenticated user. | | `tags` | boolean | no | If the parameter is included as true, each repository will include an array of `"tags"` in the response. | -| `name` | string | no | Returns a list of repositories with a name that matches the value. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29763) in GitLab 13.0). | | `tags_count` | boolean | no | If the parameter is included as true, each repository will include `"tags_count"` in the response ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32141) in GitLab 13.1). | ```shell diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index 34ca7f8ea7c..a319c5f09ab 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -48,7 +48,6 @@ The simplest approach is to install GitLab Runner in `shell` execution mode. GitLab Runner then executes job scripts as the `gitlab-runner` user. 1. Install [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-runner/#installation). - 1. During GitLab Runner installation select `shell` as method of executing job scripts or use command: ```shell @@ -158,7 +157,6 @@ Runner is installed using the [Helm chart](https://docs.gitlab.com/runner/instal See the [related issue](https://gitlab.com/gitlab-org/charts/gitlab-runner/-/issues/83) for details. 1. Install [GitLab Runner](https://docs.gitlab.com/runner/install/). - 1. Register GitLab Runner from the command line to use `docker` and `privileged` mode: @@ -318,7 +316,6 @@ are done to the services as well, making these incompatible. In order to do that, follow the steps: 1. Install [GitLab Runner](https://docs.gitlab.com/runner/install/). - 1. Register GitLab Runner from the command line to use `docker` and share `/var/run/docker.sock`: ```shell diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index 62b87c2e858..185de159416 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -678,7 +678,6 @@ To add `DOCKER_AUTH_CONFIG` to a runner: - The double quotes included in the `DOCKER_AUTH_CONFIG` data must be escaped with backslashes. This prevents them from being interpreted as TOML. - - The `environment` option is a list. Your runner may have existing entries and you should add this to the list, not replace it. @@ -725,10 +724,8 @@ image which is private and requires you to log in into a private container regis To configure access for `aws_account_id.dkr.ecr.region.amazonaws.com`, follow these steps: 1. Make sure `docker-credential-ecr-login` is available in GitLab Runner's `$PATH`. - 1. Have any of the following [AWS credentials setup](https://github.com/awslabs/amazon-ecr-credential-helper#aws-credentials). Make sure that GitLab Runner can access the credentials. - 1. Make GitLab Runner use it. There are two ways to accomplish this. Either: - Create a [variable](../variables/README.md#gitlab-cicd-environment-variables) diff --git a/doc/ci/pipelines/job_artifacts.md b/doc/ci/pipelines/job_artifacts.md index 750a76bfaa0..39633163ac7 100644 --- a/doc/ci/pipelines/job_artifacts.md +++ b/doc/ci/pipelines/job_artifacts.md @@ -431,6 +431,17 @@ To erase a job: In order to retrieve a job artifact of a different project, you might need to use a private token in order to [authenticate and download](../../api/job_artifacts.md#get-job-artifacts) the artifacts. +## Troubleshooting + +### Error message `No files to upload` + +This is often preceded by other errors or warnings that specify the filename and why it wasn't +generated in the first place. Please check the entire job log for such messages. + +If you find no helpful messages, please retry the failed job after activating +[CI debug logging](../variables/README.md#debug-logging). +This provides useful information to investigate further. + <!-- ## Troubleshooting Include any troubleshooting steps that you can foresee. If you know beforehand what issues diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md index dc1cf3ec466..3a02ea5aa83 100644 --- a/doc/development/documentation/index.md +++ b/doc/development/documentation/index.md @@ -1,4 +1,7 @@ --- +stage: none +group: Documentation Guidelines +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers description: Learn how to contribute to GitLab Documentation. --- @@ -128,7 +131,7 @@ The following metadata should be added when a page is moved to another location: - `redirect_to`: The relative path and filename (with an `.md` extension) of the location to which visitors should be redirected for a moved page. - [Learn more](#changing-document-location). + [Learn more](#move-or-rename-a-page). - `disqus_identifier`: Identifier for Disqus commenting system. Used to keep comments with a page that's been moved to a new URL. [Learn more](#redirections-for-pages-with-disqus-comments). @@ -156,17 +159,18 @@ Nanoc layout), which will be displayed at the top of the page if defined: [algorithm](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/master/lib/helpers/reading_time.rb) to calculate the reading time based on the number of words. -## Changing document location +## Move or rename a page + +Moving or renaming a document is the same as changing its location. This process +requires specific steps to ensure that visitors can find the new +documentation page, whether they're using `/help` from a GitLab instance or by +visiting <https://docs.gitlab.com>. -Changing a document's location requires specific steps to ensure that -users can seamlessly access the new doc page, whether they are accessing content -on a GitLab instance domain at `/help` or at <https://docs.gitlab.com>. Be sure to assign a -technical writer if you have any questions during the process (such as -whether the move is necessary), and ensure that a technical writer reviews this -change prior to merging. +Be sure to assign a technical writer to a page move or rename MR. Technical +Writers can help with any questions and can review your change. -If you indeed need to change a document's location, do not remove the old -document, but instead replace all of its content with the following: +To change a document's location, don't remove the old document, but instead +replace all of its content with the following: ```markdown --- @@ -176,14 +180,18 @@ redirect_to: '../path/to/file/index.md' This document was moved to [another location](../path/to/file/index.md). ``` -Where `../path/to/file/index.md` is usually the relative path to the old document. +Replace `../path/to/file/index.md` with the relative path to the old document. + +The `redirect_to` variable supports both full and relative URLs; for example: + +- `https://docs.gitlab.com/ee/path/to/file.html` +- `../path/to/file.html` +- `path/to/file.md` -The `redirect_to` variable supports both full and relative URLs, for example -`https://docs.gitlab.com/ee/path/to/file.html`, `../path/to/file.html`, `path/to/file.md`. -It ensures that the redirect will work for <https://docs.gitlab.com> and any `*.md` paths -will be compiled to `*.html`. -The new line underneath the front matter informs the user that the document -changed location and is useful for someone that browses that file from the repository. +The redirect works for <https://docs.gitlab.com>, and any `*.md` paths are +changed to `*.html`. The description line following the `redirect_to` code +informs the visitor that the document changed location if the redirect process +doesn't complete successfully. For example, if you move `doc/workflow/lfs/index.md` to `doc/administration/lfs.md`, then the steps would be: diff --git a/doc/topics/autodevops/customize.md b/doc/topics/autodevops/customize.md index 13aa8f7e035..45083dfeb64 100644 --- a/doc/topics/autodevops/customize.md +++ b/doc/topics/autodevops/customize.md @@ -332,7 +332,7 @@ applications. | `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME` | From GitLab 11.11, used to set a username to connect to the Helm repository. Defaults to no credentials. Also set `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD`. | | `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD` | From GitLab 11.11, used to set a password to connect to the Helm repository. Defaults to no credentials. Also set `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME`. | | `AUTO_DEVOPS_DEPLOY_DEBUG` | From GitLab 13.1, if this variable is present, Helm will output debug logs. | -| `AUTO_DEVOPS_ALLOW_TO_FORCE_DEPLOY_V<N>` | From [auto-deploy-image](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image) v1.0.0, if this variable is present, a new major version of chart is forcibly deployed. [More details](upgrading_chart.md#ignore-warning-and-continue-deploying) | +| `AUTO_DEVOPS_ALLOW_TO_FORCE_DEPLOY_V<N>` | From [auto-deploy-image](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image) v1.0.0, if this variable is present, a new major version of chart is forcibly deployed. For more information, see [Ignore warnings and continue deploying](upgrading_auto_deploy_dependencies.md#ignore-warnings-and-continue-deploying). | | `AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE` | From GitLab 12.5, used in combination with [ModSecurity feature flag](../../user/clusters/applications.md#web-application-firewall-modsecurity) to toggle [ModSecurity's `SecRuleEngine`](https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual-(v2.x)#SecRuleEngine) behavior. Defaults to `DetectionOnly`. | | `BUILDPACK_URL` | Buildpack's full URL. Can point to either [a Git repository URL or a tarball URL](#custom-buildpacks). | | `CANARY_ENABLED` | From GitLab 11.0, used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). | diff --git a/doc/topics/autodevops/stages.md b/doc/topics/autodevops/stages.md index b58c369714e..0e9f0812a9a 100644 --- a/doc/topics/autodevops/stages.md +++ b/doc/topics/autodevops/stages.md @@ -466,7 +466,7 @@ application runs. ### Upgrade auto-deploy-app Chart -You can upgrade auto-deploy-app chart by following the [upgrade guide](upgrading_chart.md). +You can upgrade the auto-deploy-app chart by following the [upgrade guide](upgrading_auto_deploy_dependencies.md). ### Workers diff --git a/doc/topics/autodevops/upgrading_auto_deploy_dependencies.md b/doc/topics/autodevops/upgrading_auto_deploy_dependencies.md new file mode 100644 index 00000000000..1aefb6b34df --- /dev/null +++ b/doc/topics/autodevops/upgrading_auto_deploy_dependencies.md @@ -0,0 +1,262 @@ +--- +stage: Release +group: Progressive Delivery +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +type: reference +--- + +# Upgrading deployments for newer Auto Deploy dependencies (Auto Deploy template, auto-deploy-image and auto-deploy-app chart) + +[Auto Deploy](stages.md#auto-deploy) is a feature that deploys your application to a Kubernetes cluster. +It consists of several dependencies: + +- [Auto Deploy template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml) is a set of pipeline jobs and scripts that makes use of `auto-deploy-image`. +- [`auto-deploy-image`](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image) is the executable image that communicates with the Kubernetes cluster. +- [`auto-deploy-app chart`](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app) is the Helm chart for deploying your application. + +The `auto-deploy-image` and `auto-deploy-app` charts use [Semantic Versioning](https://semver.org/). +By default, your Auto DevOps project keeps using the stable and non-breaking version. +However, these dependencies could be upgraded in a major version release of GitLab +with breaking changes requiring you to upgrade your deployments. + +This guide explains how to upgrade your deployments with newer or different major versions of Auto Deploy dependencies. + +## Verify dependency versions + +The process to check the current versions differs depending on which template you +are using. First verify which template is in use: + +- For self-managed instances, the [stable Auto Deploy template bundled with the GitLab package](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml) + is being used. +- [The GitLab.com stable Auto Deploy template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml) + is being used if **one** of the following is true: + - Your Auto DevOps project doesn't have a `.gitlab-ci.yml` file. + - Your Auto DevOps project has a `.gitlab-ci.yml` and [includes](../../ci/yaml/README.md#includetemplate) + the `Auto-DevOps.gitlab-ci.yml` template. +- [The latest Auto Deploy template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml) + is being used if **both** of the following is true: + - Your Auto DevOps project has a `.gitlab-ci.yml` file and [includes](../../ci/yaml/README.md#includetemplate) + the `Auto-DevOps.gitlab-ci.yml` template. + - It also includes [the latest Auto Deploy template](#early-adopters) + +If you know what template is being used: + +- The `auto-deploy-image` version is in the template (for example `auto-deploy-image:v1.0.3`). +- The `auto-deploy-app` chart version is [in the auto-deploy-image repository](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/blob/v1.0.3/assets/auto-deploy-app/Chart.yaml) + (for example `version: 1.0.3`). + +## Compatibility + +The following table explains the version compatibility between GitLab and Auto Deploy dependencies: + +| GitLab version | `auto-deploy-image` version | Notes | +|------------------|-----------------------------|-------| +| v10.0 to v14.0 | v0.1.0 to v2.0.0 | v0 and v1 auto-deploy-image are backwards compatible. | +| v13.4 and higher | v2.0.0 and higher | v2 auto-deploy-image contains breaking changes, as explained in the [upgrade guide](#upgrade-deployments-to-the-v2-auto-deploy-image). | + +You can find the current stable version of auto-deploy-image in the [Auto Deploy stable template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml). + +## Upgrade Guide + +Projects using Auto DevOps must use the unmodified chart managed by GitLab. +[Customized charts](customize.md#custom-helm-chart) are unsupported. + +### Upgrade deployments to the v1 `auto-deploy-image` + +The v1 chart is backward compatible with the v0 chart, so no configuration changes are needed. + +### Upgrade deployments to the v2 `auto-deploy-image` + +The v2 auto-deploy-image contains multiple dependency and architectural changes. +If your Auto DevOps project has an active environment deployed with the v1 `auto-deploy-image`, +please proceed with the following upgrade guide. Otherwise, you can skip this process. + +#### Helm 3 + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/228609) in GitLab 13.4. + +The `auto-deploy-image` uses the Helm binary to manipulate the releases. +Previously, `auto-deploy-image` used Helm v2, which used Tiller in a cluster. +In the v2 `auto-deploy-image`, it uses Helm v3 that doesn't require Tiller anymore. + +If your Auto DevOps project has an active environment that was deployed with the v1 +`auto-deploy-image`, use the following steps to upgrade to v2, which uses Helm 3: + +1. Modify your `.gitlab-ci.yml` with: + + ```yaml + include: + - template: Auto-DevOps.gitlab-ci.yml + - remote: https://gitlab.com/hfyngvason/ci-templates/-/raw/master/Helm-2to3.gitlab-ci.yml + + variables: + # If this variable is not present, the migration jobs will not show up + MIGRATE_HELM_2TO3: "true" + + .auto-deploy: + image: registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v2.0.0-beta.1 + variables: + AUTO_DEVOPS_FORCE_DEPLOY_V2: 1 + ``` + +1. Run the `<environment-name>:helm-2to3:migrate` job. +1. Deploy your environment as usual. This deployment uses Helm 3. +1. If the deployment succeeds, you can safely run `environment:helm-2to3:cleanup`. + This deletes all Helm 2 release data from the namespace. + + If you accidentally delete the Helm 2 releases before you are ready, the `<environment-name>:helm2to3:migrate` + job saves a backup for 1 week in a job artifact called `helm-2-release-backups`. + The backup is in a Kubernetes manifest file that can be restored using + `kubectl apply -f $backup`. +1. Remove the `MIGRATE_HELM_2TO3` variable. + +#### Traffic routing change for canary deployments and incremental rollouts + +> [Introduced](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/merge_requests/109) in GitLab 13.4. + +Auto Deploy supports advanced deployment strategies such as [canary deployments](customize.md#deploy-policy-for-canary-environments) +and [incremental rollouts](../../ci/environments/incremental_rollouts.md). + +Previously, `auto-deploy-image` created one service to balance the traffic between +unstable and stable tracks by changing the replica ratio. In the v2 `auto-deploy-image`, +it controls the traffic with [Canary Ingress](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#canary). + +For more details, see the [v2 `auto-deploy-app` chart resource architecture](#v2-chart-resource-architecture). + +If your Auto DevOps project has active `canary` or `rollout` track releases in the +`production` environment deployed with the v1 `auto-deploy-image`, use the following +steps to upgrade to v2: + +1. Verify your project is [using the v1 `auto-deploy-image`](#verify-dependency-versions). + If not, [specify the version](#use-a-specific-version-of-auto-deploy-dependencies). +1. If you're in the process of deploying `canary` or `rollout` deployments, promote + them to `production` first to delete the unstable tracks. +1. Verify your project is [using the v2 `auto-deploy-image`](#verify-dependency-versions). + If not, [specify the version](#use-a-specific-version-of-auto-deploy-dependencies). +1. Add an `AUTO_DEVOPS_FORCE_DEPLOY_V2` environment variable with a value of `true` + in the GitLab CI/CD settings. +1. Create a new pipeline and run the `production` job to renew the resource architecture + with the v2 `auto-deploy-app chart`. +1. Remove the `AUTO_DEVOPS_FORCE_DEPLOY_V2` environment variable. + +### Use a specific version of Auto Deploy dependencies + +To use a specifc version of Auto Deploy dependencies, specify the previous Auto Deploy +stable template that contains the [desired version of `auto-deploy-image` and `auto-deploy-app`](#verify-dependency-versions). + +For example, if the template is bundled in GitLab v13.3, change your `.gitlab-ci.yml` to: + +```yaml +include: + - template: Auto-DevOps.gitlab-ci.yml + - remote: https://gitlab.com/gitlab-org/gitlab/-/blob/v13.3.0-ee/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml +``` + +### Ignore warnings and continue deploying + +If you are certain that the new chart version is safe to be deployed, you can add +the `AUTO_DEVOPS_FORCE_DEPLOY_V<major-version-number>` [environment variable](customize.md#build-and-deployment) +to force the deployment to continue. + +For example, if you want to deploy the `v2.0.0` chart on a deployment that previously +used the `v0.17.0` chart, add `AUTO_DEVOPS_FORCE_DEPLOY_V2`. + +## Early adopters + +If you want to use the latest beta or unstable version of `auto-deploy-image`, include +the latest Auto Deploy template into your `.gitlab-ci.yml`: + +```yaml +include: + - template: Auto-DevOps.gitlab-ci.yml + - remote: https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml +``` + +CAUTION: **Warning:** +Using a beta or unstable `auto-deploy-image` could cause unrecoverable damage to +your environments. Do not test it with important projects or environments. + +The next stable template update is [planned for GitLab v14.0](https://gitlab.com/gitlab-org/gitlab/-/issues/232788). + +## Resource Architectures of the `auto-deploy-app` chart + +### v0 and v1 chart resource architecture + +```mermaid +graph TD; +subgraph gl-managed-app +Z[Nginx Ingress] +end +Z[Nginx Ingress] --> A(Ingress); +Z[Nginx Ingress] --> B(Ingress); +subgraph stg namespace +B[Ingress] --> H(...); +end +subgraph prd namespace +A[Ingress] --> D(Service); +D[Service] --> E(Deployment:Pods:app:stable); +D[Service] --> F(Deployment:Pods:app:canary); +D[Service] --> I(Deployment:Pods:app:rollout); +E(Deployment:Pods:app:stable)---id1[(Pods:Postgres)] +F(Deployment:Pods:app:canary)---id1[(Pods:Postgres)] +I(Deployment:Pods:app:rollout)---id1[(Pods:Postgres)] +end +``` + +### v2 chart resource architecture + +```mermaid +graph TD; +subgraph gl-managed-app +Z[Nginx Ingress] +end +Z[Nginx Ingress] --> A(Ingress); +Z[Nginx Ingress] --> B(Ingress); +Z[Nginx Ingress] --> |If canary is present or incremental rollout/|J(Canary Ingress); +subgraph stg namespace +B[Ingress] --> H(...); +end +subgraph prd namespace +subgraph stable track +A[Ingress] --> D[Service]; +D[Service] --> E(Deployment:Pods:app:stable); +end +subgraph canary track +J(Canary Ingress) --> K[Service] +K[Service] --> F(Deployment:Pods:app:canary); +end +E(Deployment:Pods:app:stable)---id1[(Pods:Postgres)] +F(Deployment:Pods:app:canary)---id1[(Pods:Postgres)] +end +``` + +## Troubleshooting + +### Major version mismatch warning + +If deploying a chart that has a major version that is different from the previous one, +the new chart might not be correctly deployed. This could be due to an architectural +change. If that happens, the deployment job fails with a message similar to: + +```plaintext +************************************************************************************* + [WARNING] +Detected a major version difference between the the chart that is currently deploying (auto-deploy-app-v0.7.0), and the previously deployed chart (auto-deploy-app-v1.0.0). +A new major version might not be backward compatible with the current release (production). The deployment could fail or be stuck in an unrecoverable status. +... +``` + +To clear this error message and resume deployments, you must do one of the following: + +- Manually [upgrade the chart version](#upgrade-guide). +- [Use a specific chart version](#use-a-specific-version-of-auto-deploy-dependencies). + +### Error: `missing key "app.kubernetes.io/managed-by": must be set to "Helm"` + +If your cluster has a deployment that was deployed with the v1 `auto-deploy-image`, +you might encounter the following error: + +- `Error: rendered manifests contain a resource that already exists. Unable to continue with install: Secret "production-postgresql" in namespace "<project-name>-production" exists and cannot be imported into the current release: invalid ownership metadata; label validation error: missing key "app.kubernetes.io/managed-by": must be set to "Helm"; annotation validation error: missing key "meta.helm.sh/release-name": must be set to "production-postgresql"; annotation validation error: missing key "meta.helm.sh/release-namespace": must be set to "<project-name>-production"` + +This is because the previous deployment was deployed with Helm2, which is not compatible with Helm3. +To resolve the problem, please follow the [upgrade guide](#upgrade-deployments-to-the-v2-auto-deploy-image). diff --git a/doc/topics/autodevops/upgrading_chart.md b/doc/topics/autodevops/upgrading_chart.md index ffa485f6d2c..e4fb84d4509 100644 --- a/doc/topics/autodevops/upgrading_chart.md +++ b/doc/topics/autodevops/upgrading_chart.md @@ -1,72 +1,5 @@ -# Upgrading auto-deploy-app chart for Auto DevOps +--- +redirect_to: 'upgrading_auto_deploy_dependencies.md' +--- -Auto DevOps provides the auto-deploy-app chart for deploying your application to the -Kubernetes cluster with Helm/Tiller. Major version changes of this chart could have -a significantly different resource architecture, and may not be backwards compatible. - -This guide provides instructions on how to upgrade your deployments to use the latest -chart and resource architecture. - -## Compatibility - -The following table lists the version compatibility between GitLab and [auto-deploy-image](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image) (with the [auto-deploy-app chart](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image/-/tree/master/assets/auto-deploy-app)). - -| GitLab version | auto-deploy-image version | Notes | -|------------------|---------------------------|--------------------------------------------| -| v10.0 and higher | v0.1.0 and higher | v0 and v1 charts are backwards compatible. | - -## Upgrade Guide - -The Auto DevOps project must use the unmodified chart managed by GitLab. -[Customized charts](customize.md#custom-helm-chart) are unsupported. - -### v1 chart - -The v1 chart is backward compatible with the v0 chart, so no configuration changes are needed. - -## Troubleshooting - -### Major version mismatch warning - -If deploying a chart that has a major version that is different from the previous one, -the new chart might not be correctly deployed. This could be due to an architectural -change. If that happens, the deployment job fails with a message similar to: - -```plaintext -************************************************************************************* - [WARNING] -Detected a major version difference between the the chart that is currently deploying (auto-deploy-app-v0.7.0), and the previously deployed chart (auto-deploy-app-v1.0.0). -A new major version might not be backward compatible with the current release (production). The deployment could fail or be stuck in an unrecoverable status. -... -``` - -To clear this error message and resume deployments, you must do one of the following: - -- Manually [upgrade the chart version](#upgrade-guide). -- [Use a specific chart version](#use-a-specific-chart-version). - -#### Use a specific chart version - -To use a specific chart version, you must specify a corresponding version of [auto-deploy-image](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image). -Do this by [customizing the image in your `.gitlab-ci.yml`](customize.md#customizing-gitlab-ciyml). - -For example, create the following `.gitlab-ci.yml` file in the project. It configures Auto DevOps -to use [auto-deploy-image](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image) version `v0.17.0` -for deployment jobs. It will download the chart from [chart repository](https://charts.gitlab.io/): - -```yaml -include: - - template: Auto-DevOps.gitlab-ci.yml - -.auto-deploy: - image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v0.17.0" -``` - -#### Ignore warning and continue deploying - -If you are certain that the new chart version is safe to be deployed, -you can add the `AUTO_DEVOPS_FORCE_DEPLOY_V<N>` [environment variable](customize.md#build-and-deployment) -to force the deployment to continue, where `<N>` is the major version. - -For example, if you want to deploy the v2.0.0 chart on a deployment that previously -used the v0.17.0 chart, add `AUTO_DEVOPS_FORCE_DEPLOY_V2`. +This document was moved to [another location](upgrading_auto_deploy_dependencies.md). diff --git a/doc/user/application_security/container_scanning/index.md b/doc/user/application_security/container_scanning/index.md index d207edf3656..fec90a2bdf3 100644 --- a/doc/user/application_security/container_scanning/index.md +++ b/doc/user/application_security/container_scanning/index.md @@ -433,3 +433,7 @@ This is a result of a bug in Docker which is now [fixed](https://github.com/cont To prevent the error, ensure the Docker version that the runner is using is `18.09.03` or higher. For more information, see [issue #10241](https://gitlab.com/gitlab-org/gitlab/-/issues/10241 "Investigate why Container Scanning is not working with NFS mounts"). + +### Getting warning message `gl-container-scanning-report.json: no matching files` + +For information on this, see the [general Application Security troubleshooting section](../../../ci/pipelines/job_artifacts.md#error-message-no-files-to-upload). diff --git a/doc/user/application_security/dast/index.md b/doc/user/application_security/dast/index.md index dea4665f5f1..1eeba0ceb0e 100644 --- a/doc/user/application_security/dast/index.md +++ b/doc/user/application_security/dast/index.md @@ -897,6 +897,10 @@ Change the number after `-Xmx` to the required memory amount. If your DAST job exceeds the job timeout and you need to reduce the scan duration, we shared some tips for optimizing DAST scans in a [blog post](https://about.gitlab.com/blog/2020/08/31/how-to-configure-dast-full-scans-for-complex-web-applications/). +### Getting warning message `gl-dast-report.json: no matching files` + +For information on this, see the [general Application Security troubleshooting section](../../../ci/pipelines/job_artifacts.md#error-message-no-files-to-upload). + <!-- ## Troubleshooting Include any troubleshooting steps that you can foresee. If you know beforehand what issues diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md index 88a498641f6..67803f2f16d 100644 --- a/doc/user/application_security/dependency_scanning/index.md +++ b/doc/user/application_security/dependency_scanning/index.md @@ -485,6 +485,10 @@ Consider updating to Docker `19.03.1` or greater. Older versions are not affected. Read more in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/13830#note_211354992 "Current SAST container fails"). +### Getting warning message `gl-dependency-scanning-report.json: no matching files` + +For information on this, see the [general Application Security troubleshooting section](../../../ci/pipelines/job_artifacts.md#error-message-no-files-to-upload). + ### Limitation when using rules:exists The [dependency scanning CI template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml) diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md index c5b729aa9e7..413a9f894e2 100644 --- a/doc/user/application_security/index.md +++ b/doc/user/application_security/index.md @@ -458,6 +458,15 @@ To fix this issue, you can either: [Learn more on overriding SAST jobs](sast/index.md#overriding-sast-jobs). All the security scanning tools define their stage, so this error can occur with all of them. +### Getting warning messages `… report.json: no matching files` + +This is often followed by the [error `No files to upload`](../../ci/pipelines/job_artifacts.md#error-message-no-files-to-upload), +and preceded by other errors or warnings that indicate why the JSON report wasn't generated. Please +check the entire job log for such messages. If you don't find these messages, retry the failed job +after setting `SECURE_LOG_LEVEL: "debug"` as a +[custom environment variable](../../ci/variables/README.md#custom-environment-variables). +This provides useful information to investigate further. + ### Getting error message `sast job: config key may not be used with 'rules': only/except` When [including](../../ci/yaml/README.md#includetemplate) a `.gitlab-ci.yml` template diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md index be01dca8d9a..d1e3c2b5b15 100644 --- a/doc/user/application_security/sast/index.md +++ b/doc/user/application_security/sast/index.md @@ -547,3 +547,7 @@ This error occurs when the Docker version that runs the SAST job is `19.03.0`. Consider updating to Docker `19.03.1` or greater. Older versions are not affected. Read more in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/13830#note_211354992 "Current SAST container fails"). + +### Getting warning message `gl-sast-report.json: no matching files` + +For information on this, see the [general Application Security troubleshooting section](../../../ci/pipelines/job_artifacts.md#error-message-no-files-to-upload). diff --git a/doc/user/application_security/secret_detection/index.md b/doc/user/application_security/secret_detection/index.md index f3e411cdc16..3c1e9e0f3cd 100644 --- a/doc/user/application_security/secret_detection/index.md +++ b/doc/user/application_security/secret_detection/index.md @@ -197,3 +197,9 @@ We have created a [short video walkthrough](https://youtu.be/wDtc_K00Y0A) showca <figure class="video-container"> <iframe src="https://www.youtube.com/embed/wDtc_K00Y0A" frameborder="0" allowfullscreen="true"> </iframe> </figure> + +## Troubleshooting + +### Getting warning message `gl-secret-detection-report.json: no matching files` + +For information on this, see the [general Application Security troubleshooting section](../../../ci/pipelines/job_artifacts.md#error-message-no-files-to-upload). diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index 0b755a81616..b647c162152 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -270,20 +270,43 @@ If your cluster was created before GitLab 12.2, default `KUBE_NAMESPACE` will be ### Custom namespace -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27630) in GitLab 12.6. - -The Kubernetes integration defaults to project-environment-specific namespaces -of the form `<project_name>-<project_id>-<environment>` (see [Deployment -variables](#deployment-variables)). - -For **non**-GitLab-managed clusters, the namespace can be customized using -[`environment:kubernetes:namespace`](../../../ci/environments/index.md#configuring-kubernetes-deployments) -in `.gitlab-ci.yml`. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27630) in GitLab 12.6. +> - An option to use project-wide namespaces [was added](https://gitlab.com/gitlab-org/gitlab/-/issues/38054) in GitLab 13.5. + +The Kubernetes integration provides a `KUBECONFIG` with an auto-generated namespace +to deployment jobs. It defaults to using project-environment specific namespaces +of the form `<prefix>-<environment>`, where `<prefix>` is of the form +`<project_name>-<project_id>`. To learn more, read [Deployment variables](#deployment-variables). + +You can customize the deployment namespace in a few ways: + +- You can choose between a **namespace per [environment](../../../ci/environments/index.md)** + or a **namespace per project**. A namespace per environment is the default and recommended + setting, as it prevents the mixing of resources between production and non-production environments. +- When using a project-level cluster, you can additionally customize the namespace prefix. + When using namespace-per-environment, the deployment namespace is `<prefix>-<environment>`, + but otherwise just `<prefix>`. +- For **non-managed** clusters, the auto-generated namespace is set in the `KUBECONFIG`, + but the user is responsible for ensuring its existence. You can fully customize + this value using + [`environment:kubernetes:namespace`](../../../ci/environments/index.md#configuring-kubernetes-deployments) + in `.gitlab-ci.yml`. NOTE: **Note:** -When using a [GitLab-managed cluster](#gitlab-managed-clusters), the -namespaces are created automatically prior to deployment and [can not be -customized](https://gitlab.com/gitlab-org/gitlab/-/issues/38054). +When you customize the namespace, existing environments remain linked to their current +namespaces until you [clear the cluster cache](#clearing-the-cluster-cache). + +CAUTION: **Warning:** +By default, anyone who can create a deployment job can access any CI variable within +an environment's deployment job. This includes `KUBECONFIG`, which gives access to +any secret available to the associated service account in your cluster. +To keep your production credentials safe, consider using +[Protected Environments](../../../ci/environments/protected_environments.md), +combined with either + +- a GitLab-managed cluster and namespace per environment, +- *or*, an environment-scoped cluster per protected environment. The same cluster + can be added multiple times with multiple restricted service accounts. ### Integrations diff --git a/lib/api/admin/instance_clusters.rb b/lib/api/admin/instance_clusters.rb index 8208d10c089..0db2321199a 100644 --- a/lib/api/admin/instance_clusters.rb +++ b/lib/api/admin/instance_clusters.rb @@ -37,6 +37,7 @@ module API requires :name, type: String, desc: 'Cluster name' optional :enabled, type: Boolean, default: true, desc: 'Determines if cluster is active or not, defaults to true' optional :environment_scope, default: '*', type: String, desc: 'The associated environment to the cluster' + optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace' optional :domain, type: String, desc: 'Cluster base domain' optional :management_project_id, type: Integer, desc: 'The ID of the management project' optional :managed, type: Boolean, default: true, desc: 'Determines if GitLab will manage namespaces and service accounts for this cluster, defaults to true' @@ -70,6 +71,7 @@ module API optional :name, type: String, desc: 'Cluster name' optional :enabled, type: Boolean, desc: 'Enable or disable Gitlab\'s connection to your Kubernetes cluster' optional :environment_scope, type: String, desc: 'The associated environment to the cluster' + optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace' optional :domain, type: String, desc: 'Cluster base domain' optional :management_project_id, type: Integer, desc: 'The ID of the management project' optional :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do diff --git a/lib/api/entities/cluster.rb b/lib/api/entities/cluster.rb index 4cb54e988ce..67459092a33 100644 --- a/lib/api/entities/cluster.rb +++ b/lib/api/entities/cluster.rb @@ -4,7 +4,7 @@ module API module Entities class Cluster < Grape::Entity expose :id, :name, :created_at, :domain - expose :provider_type, :platform_type, :environment_scope, :cluster_type + expose :provider_type, :platform_type, :environment_scope, :cluster_type, :namespace_per_environment expose :user, using: Entities::UserBasic expose :platform_kubernetes, using: Entities::Platform::Kubernetes expose :provider_gcp, using: Entities::Provider::Gcp diff --git a/lib/api/group_clusters.rb b/lib/api/group_clusters.rb index ae41d9f13b8..77095ee62e0 100644 --- a/lib/api/group_clusters.rb +++ b/lib/api/group_clusters.rb @@ -41,6 +41,7 @@ module API requires :name, type: String, desc: 'Cluster name' optional :enabled, type: Boolean, default: true, desc: 'Determines if cluster is active or not, defaults to true' optional :environment_scope, default: '*', type: String, desc: 'The associated environment to the cluster' + optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace' optional :domain, type: String, desc: 'Cluster base domain' optional :management_project_id, type: Integer, desc: 'The ID of the management project' optional :managed, type: Boolean, default: true, desc: 'Determines if GitLab will manage namespaces and service accounts for this cluster, defaults to true' @@ -74,6 +75,7 @@ module API optional :name, type: String, desc: 'Cluster name' optional :domain, type: String, desc: 'Cluster base domain' optional :environment_scope, type: String, desc: 'The associated environment to the cluster' + optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace' optional :management_project_id, type: Integer, desc: 'The ID of the management project' optional :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do optional :api_url, type: String, desc: 'URL to access the Kubernetes API' diff --git a/lib/api/project_clusters.rb b/lib/api/project_clusters.rb index 0e5605984e6..6f189110d76 100644 --- a/lib/api/project_clusters.rb +++ b/lib/api/project_clusters.rb @@ -45,6 +45,7 @@ module API optional :enabled, type: Boolean, default: true, desc: 'Determines if cluster is active or not, defaults to true' optional :domain, type: String, desc: 'Cluster base domain' optional :environment_scope, default: '*', type: String, desc: 'The associated environment to the cluster' + optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace' optional :management_project_id, type: Integer, desc: 'The ID of the management project' optional :managed, type: Boolean, default: true, desc: 'Determines if GitLab will manage namespaces and service accounts for this cluster, defaults to true' requires :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do @@ -78,6 +79,7 @@ module API optional :name, type: String, desc: 'Cluster name' optional :domain, type: String, desc: 'Cluster base domain' optional :environment_scope, type: String, desc: 'The associated environment to the cluster' + optional :namespace_per_environment, default: true, type: Boolean, desc: 'Deploy each environment to a separate Kubernetes namespace' optional :management_project_id, type: Integer, desc: 'The ID of the management project' optional :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do optional :api_url, type: String, desc: 'URL to access the Kubernetes API' diff --git a/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml index 829fd7a722f..8b921305c11 100644 --- a/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml @@ -1,5 +1,5 @@ .auto-deploy: - image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v1.0.3" + image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v2.0.0-beta.2" dependencies: [] review: @@ -91,7 +91,7 @@ canary: - auto-deploy ensure_namespace - auto-deploy initialize_tiller - auto-deploy create_secret - - auto-deploy deploy canary + - auto-deploy deploy canary 50 environment: name: production url: http://$CI_PROJECT_PATH_SLUG.$KUBE_INGRESS_BASE_DOMAIN @@ -114,7 +114,6 @@ canary: - auto-deploy create_secret - auto-deploy deploy - auto-deploy delete canary - - auto-deploy delete rollout - auto-deploy persist_environment_url environment: name: production @@ -163,9 +162,7 @@ production_manual: - auto-deploy ensure_namespace - auto-deploy initialize_tiller - auto-deploy create_secret - - auto-deploy deploy rollout $ROLLOUT_PERCENTAGE - - auto-deploy scale stable $((100-ROLLOUT_PERCENTAGE)) - - auto-deploy delete canary + - auto-deploy deploy canary $ROLLOUT_PERCENTAGE - auto-deploy persist_environment_url environment: name: production diff --git a/lib/gitlab/ci/trace/checksum.rb b/lib/gitlab/ci/trace/checksum.rb index ae1df058954..b01136a6d24 100644 --- a/lib/gitlab/ci/trace/checksum.rb +++ b/lib/gitlab/ci/trace/checksum.rb @@ -24,17 +24,13 @@ module Gitlab end def valid? - return false unless state_crc32 > 0 + return false unless state_crc32.present? state_crc32 == chunks_crc32 end def state_crc32 - strong_memoize(:crc32) do - build.pending_state&.trace_checksum.then do |checksum| - checksum.to_s.split('crc32:').last.to_i - end - end + strong_memoize(:crc32) { build.pending_state&.crc32 } end def chunks_crc32 diff --git a/lib/gitlab/danger/roulette.rb b/lib/gitlab/danger/roulette.rb index 1b87b7d2864..e67e4a45bfe 100644 --- a/lib/gitlab/danger/roulette.rb +++ b/lib/gitlab/danger/roulette.rb @@ -147,6 +147,7 @@ module Gitlab spin_role_for_category(team, role, project, category) end hungry_reviewers = reviewers.select { |member| member.hungry } + hungry_traintainers = traintainers.select { |member| member.hungry } # TODO: take CODEOWNERS into account? # https://gitlab.com/gitlab-org/gitlab/issues/26723 @@ -156,7 +157,7 @@ module Gitlab # Make hungry traintainers have 4x the chance to be picked as a reviewer # Make traintainers have 3x the chance to be picked as a reviewer # Make hungry reviewers have 2x the chance to be picked as a reviewer - weighted_reviewers = reviewers + hungry_reviewers + traintainers + traintainers + weighted_reviewers = reviewers + hungry_reviewers + traintainers + traintainers + traintainers + hungry_traintainers reviewer = spin_for_person(weighted_reviewers, random: random, timezone_experiment: timezone_experiment) maintainer = spin_for_person(maintainers, random: random, timezone_experiment: timezone_experiment) diff --git a/lib/gitlab/danger/teammate.rb b/lib/gitlab/danger/teammate.rb index 6c185dc0ed2..4481977db15 100644 --- a/lib/gitlab/danger/teammate.rb +++ b/lib/gitlab/danger/teammate.rb @@ -32,10 +32,8 @@ module Gitlab projects&.has_key?(name) end - # Traintainers also count as reviewers def reviewer?(project, category, labels) - has_capability?(project, category, :reviewer, labels) || - traintainer?(project, category, labels) + has_capability?(project, category, :reviewer, labels) end def traintainer?(project, category, labels) diff --git a/lib/gitlab/middleware/multipart.rb b/lib/gitlab/middleware/multipart.rb index 8e6ac7610f2..e7e18b3bb82 100644 --- a/lib/gitlab/middleware/multipart.rb +++ b/lib/gitlab/middleware/multipart.rb @@ -137,6 +137,7 @@ module Gitlab # TODO this class is meant to replace Handler when the feature flag # upload_middleware_jwt_params_handler is removed + # See https://gitlab.com/gitlab-org/gitlab/-/issues/233895#roll-out-steps class HandlerForJWTParams < Handler def with_open_files @rewritten_fields.keys.each do |field| diff --git a/lib/uploaded_file.rb b/lib/uploaded_file.rb index cd5943b552e..9b034d1c6c2 100644 --- a/lib/uploaded_file.rb +++ b/lib/uploaded_file.rb @@ -42,6 +42,9 @@ class UploadedFile @remote_id = remote_id end + # TODO this function is meant to replace .from_params when the feature flag + # upload_middleware_jwt_params_handler is removed + # See https://gitlab.com/gitlab-org/gitlab/-/issues/233895#roll-out-steps def self.from_params_without_field(params, upload_paths) path = params['path'] remote_id = params['remote_id'] @@ -68,6 +71,10 @@ class UploadedFile ) end + # Deprecated. Don't use it. + # .from_params_without_field will replace this one + # See .from_params_without_field and + # https://gitlab.com/gitlab-org/gitlab/-/issues/233895#roll-out-steps def self.from_params(params, field, upload_paths, path_override = nil) path = path_override || params["#{field}.path"] remote_id = params["#{field}.remote_id"] diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 4f3624e9eee..ce05f0f4929 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -84,6 +84,11 @@ msgid_plural "%d Approvals" msgstr[0] "" msgstr[1] "" +msgid "%d Package" +msgid_plural "%d Packages" +msgstr[0] "" +msgstr[1] "" + msgid "%d Scanned URL" msgid_plural "%d Scanned URLs" msgstr[0] "" @@ -4068,9 +4073,6 @@ msgstr "" msgid "Blog" msgstr "" -msgid "Board name" -msgstr "" - msgid "Board scope" msgstr "" @@ -5379,10 +5381,10 @@ msgstr "" msgid "ClusterIntegration|All installed applications and related resources" msgstr "" -msgid "ClusterIntegration|Allow GitLab to manage namespace and service accounts for this cluster." +msgid "ClusterIntegration|Allow GitLab to manage namespace and service accounts for this cluster. %{startLink}More information%{endLink}" msgstr "" -msgid "ClusterIntegration|Allow GitLab to manage namespace and service accounts for this cluster. %{startLink}More information%{endLink}" +msgid "ClusterIntegration|Allow GitLab to manage namespaces and service accounts for this cluster." msgstr "" msgid "ClusterIntegration|Alternatively, " @@ -5565,6 +5567,12 @@ msgstr "" msgid "ClusterIntegration|Deletes all GitLab resources attached to this cluster during removal" msgstr "" +msgid "ClusterIntegration|Deploy each environment to its own namespace. Otherwise, environments within a project share a project-wide namespace. Note that anyone who can trigger a deployment of a namespace can read its secrets. If modified, existing environments will use their current namespaces until the cluster cache is cleared." +msgstr "" + +msgid "ClusterIntegration|Deploy each environment to its own namespace. Otherwise, environments within a project share a project-wide namespace. Note that anyone who can trigger a deployment of a namespace can read its secrets. If modified, existing environments will use their current namespaces until the cluster cache is cleared. %{startLink}More information%{endLink}" +msgstr "" + msgid "ClusterIntegration|Did you know?" msgstr "" @@ -5826,6 +5834,9 @@ msgstr "" msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{provider_link}" msgstr "" +msgid "ClusterIntegration|Namespace per environment" +msgstr "" + msgid "ClusterIntegration|No IAM Roles found" msgstr "" @@ -6683,6 +6694,9 @@ msgstr "" msgid "Configure the way a user creates a new account." msgstr "" +msgid "Configure which lists are shown for anyone who visits this board" +msgstr "" + msgid "Confirm" msgstr "" @@ -15150,6 +15164,9 @@ msgstr "" msgid "List of all merge commits" msgstr "" +msgid "List options" +msgstr "" + msgid "List settings" msgstr "" @@ -18170,12 +18187,18 @@ msgstr "" msgid "PackageRegistry|NuGet Command" msgstr "" +msgid "PackageRegistry|Package Registry" +msgstr "" + msgid "PackageRegistry|Pip Command" msgstr "" msgid "PackageRegistry|Pipeline %{link} triggered %{datetime} by %{author}" msgstr "" +msgid "PackageRegistry|Publish and share packages for a variety of common package managers. %{docLinkStart}More information%{docLinkEnd}" +msgstr "" + msgid "PackageRegistry|Published to the %{project} Package Registry %{datetime}" msgstr "" @@ -23518,6 +23541,12 @@ msgstr "" msgid "Show parent subgroups" msgstr "" +msgid "Show the Closed list" +msgstr "" + +msgid "Show the Open list" +msgstr "" + msgid "Show whitespace changes" msgstr "" @@ -26884,9 +26913,6 @@ msgstr "" msgid "Toggle navigation" msgstr "" -msgid "Toggle project" -msgstr "" - msgid "Toggle sidebar" msgstr "" diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh index 862c3b4bb62..1a1cfbbd5f6 100755 --- a/scripts/review_apps/review-apps.sh +++ b/scripts/review_apps/review-apps.sh @@ -48,7 +48,13 @@ function delete_release() { return fi - helm_delete_release "${namespace}" "${release}" + # Check if helm release exists before attempting to delete + # There may be situation where k8s resources exist, but helm release does not, + # for example, following a failed helm install. + # In such cases, we still want to continue to clean up k8s resources. + if deploy_exists "${namespace}" "${release}"; then + helm_delete_release "${namespace}" "${release}" + fi kubectl_cleanup_release "${namespace}" "${release}" } diff --git a/spec/controllers/admin/clusters_controller_spec.rb b/spec/controllers/admin/clusters_controller_spec.rb index d2a569a9d48..69bdc79c5f5 100644 --- a/spec/controllers/admin/clusters_controller_spec.rb +++ b/spec/controllers/admin/clusters_controller_spec.rb @@ -416,6 +416,7 @@ RSpec.describe Admin::ClustersController do expect(cluster).to be_user expect(cluster).to be_kubernetes expect(cluster).to be_platform_kubernetes_rbac + expect(cluster).to be_namespace_per_environment end end end @@ -585,6 +586,7 @@ RSpec.describe Admin::ClustersController do enabled: false, name: 'my-new-cluster-name', managed: false, + namespace_per_environment: false, base_domain: domain } } @@ -599,6 +601,7 @@ RSpec.describe Admin::ClustersController do expect(cluster.enabled).to be_falsey expect(cluster.name).to eq('my-new-cluster-name') expect(cluster).not_to be_managed + expect(cluster).not_to be_namespace_per_environment expect(cluster.domain).to eq('test-domain.com') end @@ -624,6 +627,7 @@ RSpec.describe Admin::ClustersController do enabled: false, name: 'my-new-cluster-name', managed: false, + namespace_per_environment: false, domain: domain } } @@ -637,6 +641,7 @@ RSpec.describe Admin::ClustersController do expect(cluster.enabled).to be_falsey expect(cluster.name).to eq('my-new-cluster-name') expect(cluster).not_to be_managed + expect(cluster).not_to be_namespace_per_environment end end diff --git a/spec/controllers/groups/clusters_controller_spec.rb b/spec/controllers/groups/clusters_controller_spec.rb index 81d5bc7770f..140b7b0f2a8 100644 --- a/spec/controllers/groups/clusters_controller_spec.rb +++ b/spec/controllers/groups/clusters_controller_spec.rb @@ -271,6 +271,7 @@ RSpec.describe Groups::ClustersController do expect(cluster).to be_kubernetes expect(cluster.provider_gcp).to be_legacy_abac expect(cluster).to be_managed + expect(cluster).to be_namespace_per_environment end context 'when legacy_abac param is false' do @@ -358,6 +359,7 @@ RSpec.describe Groups::ClustersController do expect(cluster).to be_user expect(cluster).to be_kubernetes expect(cluster).to be_managed + expect(cluster).to be_namespace_per_environment end end @@ -387,6 +389,7 @@ RSpec.describe Groups::ClustersController do expect(cluster).to be_user expect(cluster).to be_kubernetes expect(cluster).to be_platform_kubernetes_rbac + expect(cluster).to be_namespace_per_environment end end @@ -716,6 +719,7 @@ RSpec.describe Groups::ClustersController do enabled: false, name: 'my-new-cluster-name', managed: false, + namespace_per_environment: false, domain: domain } } @@ -729,6 +733,7 @@ RSpec.describe Groups::ClustersController do expect(cluster.enabled).to be_falsey expect(cluster.name).to eq('my-new-cluster-name') expect(cluster).not_to be_managed + expect(cluster).not_to be_namespace_per_environment end end diff --git a/spec/controllers/import/bulk_imports_controller_spec.rb b/spec/controllers/import/bulk_imports_controller_spec.rb new file mode 100644 index 00000000000..9fe15162158 --- /dev/null +++ b/spec/controllers/import/bulk_imports_controller_spec.rb @@ -0,0 +1,137 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Import::BulkImportsController do + let_it_be(:user) { create(:user) } + + before do + sign_in(user) + end + + context 'when user is signed in' do + context 'when bulk_import feature flag is enabled' do + before do + stub_feature_flags(bulk_import: true) + end + + describe 'POST configure' do + context 'when no params are passed in' do + it 'clears out existing session' do + post :configure + + expect(session[:bulk_import_gitlab_access_token]).to be_nil + expect(session[:bulk_import_gitlab_url]).to be_nil + + expect(response).to have_gitlab_http_status(:found) + expect(response).to redirect_to(status_import_bulk_import_url) + end + end + + it 'sets the session variables' do + token = 'token' + url = 'https://gitlab.example' + + post :configure, params: { bulk_import_gitlab_access_token: token, bulk_import_gitlab_url: url } + + expect(session[:bulk_import_gitlab_access_token]).to eq(token) + expect(session[:bulk_import_gitlab_url]).to eq(url) + expect(response).to have_gitlab_http_status(:found) + expect(response).to redirect_to(status_import_bulk_import_url) + end + + it 'strips access token with spaces' do + token = 'token' + + post :configure, params: { bulk_import_gitlab_access_token: " #{token} " } + + expect(session[:bulk_import_gitlab_access_token]).to eq(token) + expect(controller).to redirect_to(status_import_bulk_import_url) + end + end + + describe 'GET status' do + context 'when host url is local or not http' do + %w[https://localhost:3000 http://192.168.0.1 ftp://testing].each do |url| + before do + stub_application_setting(allow_local_requests_from_web_hooks_and_services: false) + + session[:bulk_import_gitlab_access_token] = 'test' + session[:bulk_import_gitlab_url] = url + end + + it 'denies network request' do + get :status + + expect(controller).to redirect_to(new_group_path) + expect(flash[:alert]).to eq('Specified URL cannot be used: "Only allowed schemes are http, https"') + end + end + + context 'when local requests are allowed' do + %w[https://localhost:3000 http://192.168.0.1].each do |url| + before do + stub_application_setting(allow_local_requests_from_web_hooks_and_services: true) + + session[:bulk_import_gitlab_access_token] = 'test' + session[:bulk_import_gitlab_url] = url + end + + it 'allows network request' do + get :status + + expect(response).to have_gitlab_http_status(:ok) + end + end + end + end + end + end + + context 'when gitlab_api_imports feature flag is disabled' do + before do + stub_feature_flags(bulk_import: false) + end + + context 'POST configure' do + it 'returns 404' do + post :configure + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'GET status' do + it 'returns 404' do + get :status + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + end + + context 'when user is signed out' do + before do + sign_out(user) + end + + context 'POST configure' do + it 'redirects to sign in page' do + post :configure + + expect(response).to have_gitlab_http_status(:found) + expect(response).to redirect_to(new_user_session_path) + end + end + + context 'GET status' do + it 'redirects to sign in page' do + get :status + + expect(response).to have_gitlab_http_status(:found) + expect(response).to redirect_to(new_user_session_path) + end + end + end +end diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb index 51a451570c5..52cd6869b04 100644 --- a/spec/controllers/projects/clusters_controller_spec.rb +++ b/spec/controllers/projects/clusters_controller_spec.rb @@ -251,6 +251,7 @@ RSpec.describe Projects::ClustersController do cluster: { name: 'new-cluster', managed: '1', + namespace_per_environment: '0', provider_gcp_attributes: { gcp_project_id: 'gcp-project-12345', legacy_abac: legacy_abac_param @@ -278,6 +279,7 @@ RSpec.describe Projects::ClustersController do expect(project.clusters.first).to be_kubernetes expect(project.clusters.first.provider_gcp).to be_legacy_abac expect(project.clusters.first.managed?).to be_truthy + expect(project.clusters.first.namespace_per_environment?).to be_falsy end context 'when legacy_abac param is false' do @@ -369,6 +371,7 @@ RSpec.describe Projects::ClustersController do expect(project.clusters.first).to be_user expect(project.clusters.first).to be_kubernetes + expect(project.clusters.first).to be_namespace_per_environment end end @@ -400,6 +403,7 @@ RSpec.describe Projects::ClustersController do expect(cluster).to be_user expect(cluster).to be_kubernetes expect(cluster).to be_platform_kubernetes_rbac + expect(cluster).to be_namespace_per_environment end end @@ -726,6 +730,7 @@ RSpec.describe Projects::ClustersController do enabled: false, name: 'my-new-cluster-name', managed: false, + namespace_per_environment: false, platform_kubernetes_attributes: { namespace: 'my-namespace' } @@ -742,6 +747,7 @@ RSpec.describe Projects::ClustersController do expect(cluster.enabled).to be_falsey expect(cluster.name).to eq('my-new-cluster-name') expect(cluster).not_to be_managed + expect(cluster).not_to be_namespace_per_environment expect(cluster.platform_kubernetes.namespace).to eq('my-namespace') end diff --git a/spec/factories/ci/build_pending_states.rb b/spec/factories/ci/build_pending_states.rb index 765b7f005b9..eddd74b1068 100644 --- a/spec/factories/ci/build_pending_states.rb +++ b/spec/factories/ci/build_pending_states.rb @@ -3,7 +3,7 @@ FactoryBot.define do factory :ci_build_pending_state, class: 'Ci::BuildPendingState' do build factory: :ci_build - trace_checksum { 'crc32:12345678' } + trace_checksum { 'crc32:bc614e' } state { 'success' } end end diff --git a/spec/features/groups/clusters/user_spec.rb b/spec/features/groups/clusters/user_spec.rb index 90253451d6b..0a1d2284831 100644 --- a/spec/features/groups/clusters/user_spec.rb +++ b/spec/features/groups/clusters/user_spec.rb @@ -66,6 +66,10 @@ RSpec.describe 'User Cluster', :js do expect(page.find_field('cluster[platform_kubernetes_attributes][authorization_type]', disabled: true)).to be_checked end end + + it 'user sees namespace per environment is enabled by default' do + expect(page).to have_checked_field('Namespace per environment') + end end context 'when user filled form with invalid parameters' do diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb index 9d0dc65093e..97d2f204036 100644 --- a/spec/features/projects/clusters/user_spec.rb +++ b/spec/features/projects/clusters/user_spec.rb @@ -52,6 +52,10 @@ RSpec.describe 'User Cluster', :js do it 'user sees RBAC is enabled by default' do expect(page).to have_checked_field('RBAC-enabled cluster') end + + it 'user sees namespace per environment is enabled by default' do + expect(page).to have_checked_field('Namespace per environment') + end end context 'when user filled form with invalid parameters' do diff --git a/spec/frontend/boards/boards_store_spec.js b/spec/frontend/boards/boards_store_spec.js index 41971137b95..7fac30c5616 100644 --- a/spec/frontend/boards/boards_store_spec.js +++ b/spec/frontend/boards/boards_store_spec.js @@ -1,7 +1,7 @@ import AxiosMockAdapter from 'axios-mock-adapter'; import { TEST_HOST } from 'helpers/test_constants'; import axios from '~/lib/utils/axios_utils'; -import boardsStore from '~/boards/stores/boards_store'; +import boardsStore, { gqlClient } from '~/boards/stores/boards_store'; import eventHub from '~/boards/eventhub'; import { listObj, listObjDuplicate } from './mock_data'; @@ -503,11 +503,15 @@ describe('boardsStore', () => { beforeEach(() => { requestSpy = jest.fn(); axiosMock.onPut(url).replyOnce(config => requestSpy(config)); + jest.spyOn(gqlClient, 'mutate').mockReturnValue(Promise.resolve({})); }); it('makes a request to update the board', () => { requestSpy.mockReturnValue([200, dummyResponse]); - const expectedResponse = expect.objectContaining({ data: dummyResponse }); + const expectedResponse = [ + expect.objectContaining({ data: dummyResponse }), + expect.objectContaining({}), + ]; return expect( boardsStore.createBoard({ @@ -555,11 +559,12 @@ describe('boardsStore', () => { beforeEach(() => { requestSpy = jest.fn(); axiosMock.onPost(url).replyOnce(config => requestSpy(config)); + jest.spyOn(gqlClient, 'mutate').mockReturnValue(Promise.resolve({})); }); it('makes a request to create a new board', () => { requestSpy.mockReturnValue([200, dummyResponse]); - const expectedResponse = expect.objectContaining({ data: dummyResponse }); + const expectedResponse = dummyResponse; return expect(boardsStore.createBoard(board)) .resolves.toEqual(expectedResponse) diff --git a/spec/frontend/boards/components/board_configuration_options_spec.js b/spec/frontend/boards/components/board_configuration_options_spec.js new file mode 100644 index 00000000000..e9a1cb6a4e8 --- /dev/null +++ b/spec/frontend/boards/components/board_configuration_options_spec.js @@ -0,0 +1,59 @@ +import { shallowMount } from '@vue/test-utils'; +import BoardConfigurationOptions from '~/boards/components/board_configuration_options.vue'; + +describe('BoardConfigurationOptions', () => { + let wrapper; + const board = { hide_backlog_list: false, hide_closed_list: false }; + + const defaultProps = { + currentBoard: board, + board, + isNewForm: false, + }; + + const createComponent = () => { + wrapper = shallowMount(BoardConfigurationOptions, { + propsData: { ...defaultProps }, + }); + }; + + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + const backlogListCheckbox = el => el.find('[data-testid="backlog-list-checkbox"]'); + const closedListCheckbox = el => el.find('[data-testid="closed-list-checkbox"]'); + + const checkboxAssert = (backlogCheckbox, closedCheckbox) => { + expect(backlogListCheckbox(wrapper).attributes('checked')).toEqual( + backlogCheckbox ? undefined : 'true', + ); + expect(closedListCheckbox(wrapper).attributes('checked')).toEqual( + closedCheckbox ? undefined : 'true', + ); + }; + + it.each` + backlogCheckboxValue | closedCheckboxValue + ${true} | ${true} + ${true} | ${false} + ${false} | ${true} + ${false} | ${false} + `( + 'renders two checkbox when one is $backlogCheckboxValue and other is $closedCheckboxValue', + async ({ backlogCheckboxValue, closedCheckboxValue }) => { + await wrapper.setData({ + hideBacklogList: backlogCheckboxValue, + hideClosedList: closedCheckboxValue, + }); + + return wrapper.vm.$nextTick().then(() => { + checkboxAssert(backlogCheckboxValue, closedCheckboxValue); + }); + }, + ); +}); diff --git a/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js b/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js index ab32fb12058..5c2d096418d 100644 --- a/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js +++ b/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js @@ -1,6 +1,6 @@ import Vuex from 'vuex'; import { createLocalVue, shallowMount, mount } from '@vue/test-utils'; -import { GlButton, GlFormCombobox } from '@gitlab/ui'; +import { GlButton } from '@gitlab/ui'; import { AWS_ACCESS_KEY_ID } from '~/ci_variable_list/constants'; import CiVariableModal from '~/ci_variable_list/components/ci_variable_modal.vue'; import createStore from '~/ci_variable_list/store'; @@ -18,7 +18,6 @@ describe('Ci variable modal', () => { store = createStore(); wrapper = method(CiVariableModal, { attachToDocument: true, - provide: { glFeatures: { ciKeyAutocomplete: true } }, stubs: { GlModal: ModalStub, }, @@ -42,27 +41,6 @@ describe('Ci variable modal', () => { wrapper.destroy(); }); - describe('Feature flag', () => { - describe('when off', () => { - beforeEach(() => { - createComponent(shallowMount, { provide: { glFeatures: { ciKeyAutocomplete: false } } }); - }); - - it('does not render the autocomplete dropdown', () => { - expect(wrapper.find(GlFormCombobox).exists()).toBe(false); - }); - }); - - describe('when on', () => { - beforeEach(() => { - createComponent(shallowMount); - }); - it('renders the autocomplete dropdown', () => { - expect(wrapper.find(GlFormCombobox).exists()).toBe(true); - }); - }); - }); - describe('Basic interactions', () => { beforeEach(() => { createComponent(shallowMount); diff --git a/spec/frontend/create_cluster/eks_cluster/components/create_eks_cluster_spec.js b/spec/frontend/create_cluster/eks_cluster/components/create_eks_cluster_spec.js index 4bf3ac430f5..e0913fe2e88 100644 --- a/spec/frontend/create_cluster/eks_cluster/components/create_eks_cluster_spec.js +++ b/spec/frontend/create_cluster/eks_cluster/components/create_eks_cluster_spec.js @@ -12,6 +12,7 @@ describe('CreateEksCluster', () => { let vm; let state; const gitlabManagedClusterHelpPath = 'gitlab-managed-cluster-help-path'; + const namespacePerEnvironmentHelpPath = 'namespace-per-environment-help-path'; const accountAndExternalIdsHelpPath = 'account-and-external-id-help-path'; const createRoleArnHelpPath = 'role-arn-help-path'; const kubernetesIntegrationHelpPath = 'kubernetes-integration'; @@ -26,6 +27,7 @@ describe('CreateEksCluster', () => { vm = shallowMount(CreateEksCluster, { propsData: { gitlabManagedClusterHelpPath, + namespacePerEnvironmentHelpPath, accountAndExternalIdsHelpPath, createRoleArnHelpPath, externalLinkIcon, @@ -53,6 +55,12 @@ describe('CreateEksCluster', () => { ); }); + it('help url for namespace per environment cluster documentation', () => { + expect(vm.find(EksClusterConfigurationForm).props('namespacePerEnvironmentHelpPath')).toBe( + namespacePerEnvironmentHelpPath, + ); + }); + it('help url for gitlab managed cluster documentation', () => { expect(vm.find(EksClusterConfigurationForm).props('kubernetesIntegrationHelpPath')).toBe( kubernetesIntegrationHelpPath, diff --git a/spec/frontend/create_cluster/eks_cluster/components/eks_cluster_configuration_form_spec.js b/spec/frontend/create_cluster/eks_cluster/components/eks_cluster_configuration_form_spec.js index d7dd7072f67..2600415fc9f 100644 --- a/spec/frontend/create_cluster/eks_cluster/components/eks_cluster_configuration_form_spec.js +++ b/spec/frontend/create_cluster/eks_cluster/components/eks_cluster_configuration_form_spec.js @@ -169,6 +169,7 @@ describe('EksClusterConfigurationForm', () => { store, propsData: { gitlabManagedClusterHelpPath: '', + namespacePerEnvironmentHelpPath: '', kubernetesIntegrationHelpPath: '', externalLinkIcon: '', }, diff --git a/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js b/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js index ed753888790..f929216689a 100644 --- a/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js +++ b/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js @@ -14,6 +14,7 @@ import { SET_ROLE, SET_SECURITY_GROUP, SET_GITLAB_MANAGED_CLUSTER, + SET_NAMESPACE_PER_ENVIRONMENT, SET_INSTANCE_TYPE, SET_NODE_COUNT, REQUEST_CREATE_ROLE, @@ -40,6 +41,7 @@ describe('EKS Cluster Store Actions', () => { let instanceType; let nodeCount; let gitlabManagedCluster; + let namespacePerEnvironment; let mock; let state; let newClusterUrl; @@ -57,6 +59,7 @@ describe('EKS Cluster Store Actions', () => { instanceType = 'small-1'; nodeCount = '5'; gitlabManagedCluster = true; + namespacePerEnvironment = true; newClusterUrl = '/clusters/1'; @@ -76,19 +79,20 @@ describe('EKS Cluster Store Actions', () => { }); it.each` - action | mutation | payload | payloadDescription - ${'setClusterName'} | ${SET_CLUSTER_NAME} | ${{ clusterName }} | ${'cluster name'} - ${'setEnvironmentScope'} | ${SET_ENVIRONMENT_SCOPE} | ${{ environmentScope }} | ${'environment scope'} - ${'setKubernetesVersion'} | ${SET_KUBERNETES_VERSION} | ${{ kubernetesVersion }} | ${'kubernetes version'} - ${'setRole'} | ${SET_ROLE} | ${{ role }} | ${'role'} - ${'setRegion'} | ${SET_REGION} | ${{ region }} | ${'region'} - ${'setKeyPair'} | ${SET_KEY_PAIR} | ${{ keyPair }} | ${'key pair'} - ${'setVpc'} | ${SET_VPC} | ${{ vpc }} | ${'vpc'} - ${'setSubnet'} | ${SET_SUBNET} | ${{ subnet }} | ${'subnet'} - ${'setSecurityGroup'} | ${SET_SECURITY_GROUP} | ${{ securityGroup }} | ${'securityGroup'} - ${'setInstanceType'} | ${SET_INSTANCE_TYPE} | ${{ instanceType }} | ${'instance type'} - ${'setNodeCount'} | ${SET_NODE_COUNT} | ${{ nodeCount }} | ${'node count'} - ${'setGitlabManagedCluster'} | ${SET_GITLAB_MANAGED_CLUSTER} | ${gitlabManagedCluster} | ${'gitlab managed cluster'} + action | mutation | payload | payloadDescription + ${'setClusterName'} | ${SET_CLUSTER_NAME} | ${{ clusterName }} | ${'cluster name'} + ${'setEnvironmentScope'} | ${SET_ENVIRONMENT_SCOPE} | ${{ environmentScope }} | ${'environment scope'} + ${'setKubernetesVersion'} | ${SET_KUBERNETES_VERSION} | ${{ kubernetesVersion }} | ${'kubernetes version'} + ${'setRole'} | ${SET_ROLE} | ${{ role }} | ${'role'} + ${'setRegion'} | ${SET_REGION} | ${{ region }} | ${'region'} + ${'setKeyPair'} | ${SET_KEY_PAIR} | ${{ keyPair }} | ${'key pair'} + ${'setVpc'} | ${SET_VPC} | ${{ vpc }} | ${'vpc'} + ${'setSubnet'} | ${SET_SUBNET} | ${{ subnet }} | ${'subnet'} + ${'setSecurityGroup'} | ${SET_SECURITY_GROUP} | ${{ securityGroup }} | ${'securityGroup'} + ${'setInstanceType'} | ${SET_INSTANCE_TYPE} | ${{ instanceType }} | ${'instance type'} + ${'setNodeCount'} | ${SET_NODE_COUNT} | ${{ nodeCount }} | ${'node count'} + ${'setGitlabManagedCluster'} | ${SET_GITLAB_MANAGED_CLUSTER} | ${gitlabManagedCluster} | ${'gitlab managed cluster'} + ${'setNamespacePerEnvironment'} | ${SET_NAMESPACE_PER_ENVIRONMENT} | ${namespacePerEnvironment} | ${'namespace per environment'} `(`$action commits $mutation with $payloadDescription payload`, data => { const { action, mutation, payload } = data; @@ -179,6 +183,7 @@ describe('EKS Cluster Store Actions', () => { name: clusterName, environment_scope: environmentScope, managed: gitlabManagedCluster, + namespace_per_environment: namespacePerEnvironment, provider_aws_attributes: { kubernetes_version: kubernetesVersion, region, @@ -204,6 +209,7 @@ describe('EKS Cluster Store Actions', () => { selectedInstanceType: instanceType, nodeCount, gitlabManagedCluster, + namespacePerEnvironment, }); }); diff --git a/spec/frontend/helpers/wait_for_text.js b/spec/frontend/helpers/wait_for_text.js new file mode 100644 index 00000000000..6bed8a90a98 --- /dev/null +++ b/spec/frontend/helpers/wait_for_text.js @@ -0,0 +1,3 @@ +import { findByText } from '@testing-library/dom'; + +export const waitForText = async (text, container = document) => findByText(container, text); diff --git a/spec/frontend/packages/list/components/__snapshots__/packages_list_app_spec.js.snap b/spec/frontend/packages/list/components/__snapshots__/packages_list_app_spec.js.snap index 6ff9376565a..794e583a487 100644 --- a/spec/frontend/packages/list/components/__snapshots__/packages_list_app_spec.js.snap +++ b/spec/frontend/packages/list/components/__snapshots__/packages_list_app_spec.js.snap @@ -1,457 +1,463 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`packages_list_app renders 1`] = ` -<b-tabs-stub - activenavitemclass="gl-tab-nav-item-active gl-tab-nav-item-active-indigo" - class="gl-tabs" - contentclass=",gl-tab-content" - navclass="gl-tabs-nav" - nofade="true" - nonavstyle="true" - tag="div" -> - <template> - - <b-tab-stub - tag="div" - title="All" - titlelinkclass="gl-tab-nav-item" - > - <template> - <div> - <section - class="row empty-state text-center" - > - <div - class="col-12" +<div> + <package-title-stub + packagehelpurl="foo" + /> + + <b-tabs-stub + activenavitemclass="gl-tab-nav-item-active gl-tab-nav-item-active-indigo" + class="gl-tabs" + contentclass=",gl-tab-content" + navclass="gl-tabs-nav" + nofade="true" + nonavstyle="true" + tag="div" + > + <template> + + <b-tab-stub + tag="div" + title="All" + titlelinkclass="gl-tab-nav-item" + > + <template> + <div> + <section + class="row empty-state text-center" > <div - class="svg-250 svg-content" + class="col-12" > - <img - alt="There are no packages yet" - class="gl-max-w-full" - src="helpSvg" - /> + <div + class="svg-250 svg-content" + > + <img + alt="There are no packages yet" + class="gl-max-w-full" + src="helpSvg" + /> + </div> </div> - </div> - - <div - class="col-12" - > + <div - class="text-content gl-mx-auto gl-my-0 gl-p-5" + class="col-12" > - <h1 - class="h4" + <div + class="text-content gl-mx-auto gl-my-0 gl-p-5" > - There are no packages yet - </h1> - - <p> - Learn how to - <b-link-stub - class="gl-link" - event="click" - href="helpUrl" - routertag="a" - target="_blank" + <h1 + class="h4" > - publish and share your packages - </b-link-stub> - with GitLab. - </p> - - <div> - <!----> + There are no packages yet + </h1> - <!----> + <p> + Learn how to + <b-link-stub + class="gl-link" + event="click" + href="helpUrl" + routertag="a" + target="_blank" + > + publish and share your packages + </b-link-stub> + with GitLab. + </p> + + <div> + <!----> + + <!----> + </div> </div> </div> - </div> - </section> - </div> - </template> - </b-tab-stub> - <b-tab-stub - tag="div" - title="Composer" - titlelinkclass="gl-tab-nav-item" - > - <template> - <div> - <section - class="row empty-state text-center" - > - <div - class="col-12" + </section> + </div> + </template> + </b-tab-stub> + <b-tab-stub + tag="div" + title="Composer" + titlelinkclass="gl-tab-nav-item" + > + <template> + <div> + <section + class="row empty-state text-center" > <div - class="svg-250 svg-content" + class="col-12" > - <img - alt="There are no Composer packages yet" - class="gl-max-w-full" - src="helpSvg" - /> + <div + class="svg-250 svg-content" + > + <img + alt="There are no Composer packages yet" + class="gl-max-w-full" + src="helpSvg" + /> + </div> </div> - </div> - - <div - class="col-12" - > + <div - class="text-content gl-mx-auto gl-my-0 gl-p-5" + class="col-12" > - <h1 - class="h4" + <div + class="text-content gl-mx-auto gl-my-0 gl-p-5" > - There are no Composer packages yet - </h1> - - <p> - Learn how to - <b-link-stub - class="gl-link" - event="click" - href="helpUrl" - routertag="a" - target="_blank" + <h1 + class="h4" > - publish and share your packages - </b-link-stub> - with GitLab. - </p> - - <div> - <!----> + There are no Composer packages yet + </h1> - <!----> + <p> + Learn how to + <b-link-stub + class="gl-link" + event="click" + href="helpUrl" + routertag="a" + target="_blank" + > + publish and share your packages + </b-link-stub> + with GitLab. + </p> + + <div> + <!----> + + <!----> + </div> </div> </div> - </div> - </section> - </div> - </template> - </b-tab-stub> - <b-tab-stub - tag="div" - title="Conan" - titlelinkclass="gl-tab-nav-item" - > - <template> - <div> - <section - class="row empty-state text-center" - > - <div - class="col-12" + </section> + </div> + </template> + </b-tab-stub> + <b-tab-stub + tag="div" + title="Conan" + titlelinkclass="gl-tab-nav-item" + > + <template> + <div> + <section + class="row empty-state text-center" > <div - class="svg-250 svg-content" + class="col-12" > - <img - alt="There are no Conan packages yet" - class="gl-max-w-full" - src="helpSvg" - /> + <div + class="svg-250 svg-content" + > + <img + alt="There are no Conan packages yet" + class="gl-max-w-full" + src="helpSvg" + /> + </div> </div> - </div> - - <div - class="col-12" - > + <div - class="text-content gl-mx-auto gl-my-0 gl-p-5" + class="col-12" > - <h1 - class="h4" + <div + class="text-content gl-mx-auto gl-my-0 gl-p-5" > - There are no Conan packages yet - </h1> - - <p> - Learn how to - <b-link-stub - class="gl-link" - event="click" - href="helpUrl" - routertag="a" - target="_blank" + <h1 + class="h4" > - publish and share your packages - </b-link-stub> - with GitLab. - </p> - - <div> - <!----> + There are no Conan packages yet + </h1> - <!----> + <p> + Learn how to + <b-link-stub + class="gl-link" + event="click" + href="helpUrl" + routertag="a" + target="_blank" + > + publish and share your packages + </b-link-stub> + with GitLab. + </p> + + <div> + <!----> + + <!----> + </div> </div> </div> - </div> - </section> - </div> - </template> - </b-tab-stub> - <b-tab-stub - tag="div" - title="Maven" - titlelinkclass="gl-tab-nav-item" - > - <template> - <div> - <section - class="row empty-state text-center" - > - <div - class="col-12" + </section> + </div> + </template> + </b-tab-stub> + <b-tab-stub + tag="div" + title="Maven" + titlelinkclass="gl-tab-nav-item" + > + <template> + <div> + <section + class="row empty-state text-center" > <div - class="svg-250 svg-content" + class="col-12" > - <img - alt="There are no Maven packages yet" - class="gl-max-w-full" - src="helpSvg" - /> + <div + class="svg-250 svg-content" + > + <img + alt="There are no Maven packages yet" + class="gl-max-w-full" + src="helpSvg" + /> + </div> </div> - </div> - - <div - class="col-12" - > + <div - class="text-content gl-mx-auto gl-my-0 gl-p-5" + class="col-12" > - <h1 - class="h4" + <div + class="text-content gl-mx-auto gl-my-0 gl-p-5" > - There are no Maven packages yet - </h1> - - <p> - Learn how to - <b-link-stub - class="gl-link" - event="click" - href="helpUrl" - routertag="a" - target="_blank" + <h1 + class="h4" > - publish and share your packages - </b-link-stub> - with GitLab. - </p> - - <div> - <!----> + There are no Maven packages yet + </h1> + + <p> + Learn how to + <b-link-stub + class="gl-link" + event="click" + href="helpUrl" + routertag="a" + target="_blank" + > + publish and share your packages + </b-link-stub> + with GitLab. + </p> - <!----> + <div> + <!----> + + <!----> + </div> </div> </div> - </div> - </section> - </div> - </template> - </b-tab-stub> - <b-tab-stub - tag="div" - title="NPM" - titlelinkclass="gl-tab-nav-item" - > - <template> - <div> - <section - class="row empty-state text-center" - > - <div - class="col-12" + </section> + </div> + </template> + </b-tab-stub> + <b-tab-stub + tag="div" + title="NPM" + titlelinkclass="gl-tab-nav-item" + > + <template> + <div> + <section + class="row empty-state text-center" > <div - class="svg-250 svg-content" + class="col-12" > - <img - alt="There are no NPM packages yet" - class="gl-max-w-full" - src="helpSvg" - /> + <div + class="svg-250 svg-content" + > + <img + alt="There are no NPM packages yet" + class="gl-max-w-full" + src="helpSvg" + /> + </div> </div> - </div> - - <div - class="col-12" - > + <div - class="text-content gl-mx-auto gl-my-0 gl-p-5" + class="col-12" > - <h1 - class="h4" + <div + class="text-content gl-mx-auto gl-my-0 gl-p-5" > - There are no NPM packages yet - </h1> - - <p> - Learn how to - <b-link-stub - class="gl-link" - event="click" - href="helpUrl" - routertag="a" - target="_blank" + <h1 + class="h4" > - publish and share your packages - </b-link-stub> - with GitLab. - </p> - - <div> - <!----> + There are no NPM packages yet + </h1> + + <p> + Learn how to + <b-link-stub + class="gl-link" + event="click" + href="helpUrl" + routertag="a" + target="_blank" + > + publish and share your packages + </b-link-stub> + with GitLab. + </p> - <!----> + <div> + <!----> + + <!----> + </div> </div> </div> - </div> - </section> - </div> - </template> - </b-tab-stub> - <b-tab-stub - tag="div" - title="NuGet" - titlelinkclass="gl-tab-nav-item" - > - <template> - <div> - <section - class="row empty-state text-center" - > - <div - class="col-12" + </section> + </div> + </template> + </b-tab-stub> + <b-tab-stub + tag="div" + title="NuGet" + titlelinkclass="gl-tab-nav-item" + > + <template> + <div> + <section + class="row empty-state text-center" > <div - class="svg-250 svg-content" + class="col-12" > - <img - alt="There are no NuGet packages yet" - class="gl-max-w-full" - src="helpSvg" - /> + <div + class="svg-250 svg-content" + > + <img + alt="There are no NuGet packages yet" + class="gl-max-w-full" + src="helpSvg" + /> + </div> </div> - </div> - - <div - class="col-12" - > + <div - class="text-content gl-mx-auto gl-my-0 gl-p-5" + class="col-12" > - <h1 - class="h4" + <div + class="text-content gl-mx-auto gl-my-0 gl-p-5" > - There are no NuGet packages yet - </h1> - - <p> - Learn how to - <b-link-stub - class="gl-link" - event="click" - href="helpUrl" - routertag="a" - target="_blank" + <h1 + class="h4" > - publish and share your packages - </b-link-stub> - with GitLab. - </p> - - <div> - <!----> + There are no NuGet packages yet + </h1> - <!----> + <p> + Learn how to + <b-link-stub + class="gl-link" + event="click" + href="helpUrl" + routertag="a" + target="_blank" + > + publish and share your packages + </b-link-stub> + with GitLab. + </p> + + <div> + <!----> + + <!----> + </div> </div> </div> - </div> - </section> - </div> - </template> - </b-tab-stub> - <b-tab-stub - tag="div" - title="PyPi" - titlelinkclass="gl-tab-nav-item" - > - <template> - <div> - <section - class="row empty-state text-center" - > - <div - class="col-12" + </section> + </div> + </template> + </b-tab-stub> + <b-tab-stub + tag="div" + title="PyPi" + titlelinkclass="gl-tab-nav-item" + > + <template> + <div> + <section + class="row empty-state text-center" > <div - class="svg-250 svg-content" + class="col-12" > - <img - alt="There are no PyPi packages yet" - class="gl-max-w-full" - src="helpSvg" - /> + <div + class="svg-250 svg-content" + > + <img + alt="There are no PyPi packages yet" + class="gl-max-w-full" + src="helpSvg" + /> + </div> </div> - </div> - - <div - class="col-12" - > + <div - class="text-content gl-mx-auto gl-my-0 gl-p-5" + class="col-12" > - <h1 - class="h4" + <div + class="text-content gl-mx-auto gl-my-0 gl-p-5" > - There are no PyPi packages yet - </h1> - - <p> - Learn how to - <b-link-stub - class="gl-link" - event="click" - href="helpUrl" - routertag="a" - target="_blank" + <h1 + class="h4" > - publish and share your packages - </b-link-stub> - with GitLab. - </p> - - <div> - <!----> + There are no PyPi packages yet + </h1> + + <p> + Learn how to + <b-link-stub + class="gl-link" + event="click" + href="helpUrl" + routertag="a" + target="_blank" + > + publish and share your packages + </b-link-stub> + with GitLab. + </p> - <!----> + <div> + <!----> + + <!----> + </div> </div> </div> - </div> - </section> - </div> - </template> - </b-tab-stub> - - <!----> - </template> - <template> - <div - class="gl-display-flex gl-align-self-center gl-py-2 gl-flex-grow-1 gl-justify-content-end" - > - <package-filter-stub - class="mr-1" - /> + </section> + </div> + </template> + </b-tab-stub> - <package-sort-stub /> - </div> - </template> -</b-tabs-stub> + <!----> + </template> + <template> + <div + class="gl-display-flex gl-align-self-center gl-py-2 gl-flex-grow-1 gl-justify-content-end" + > + <package-filter-stub + class="gl-mr-2" + /> + + <package-sort-stub /> + </div> + </template> + </b-tabs-stub> +</div> `; diff --git a/spec/frontend/packages/list/components/packages_list_app_spec.js b/spec/frontend/packages/list/components/packages_list_app_spec.js index 19ff4290f50..217096f822a 100644 --- a/spec/frontend/packages/list/components/packages_list_app_spec.js +++ b/spec/frontend/packages/list/components/packages_list_app_spec.js @@ -36,6 +36,7 @@ describe('packages_list_app', () => { resourceId: 'project_id', emptyListIllustration: 'helpSvg', emptyListHelpUrl, + packageHelpUrl: 'foo', }, filterQuery, }, diff --git a/spec/frontend/packages/list/components/packages_title_spec.js b/spec/frontend/packages/list/components/packages_title_spec.js new file mode 100644 index 00000000000..5e9ebd8ecb0 --- /dev/null +++ b/spec/frontend/packages/list/components/packages_title_spec.js @@ -0,0 +1,71 @@ +import { shallowMount } from '@vue/test-utils'; +import PackageTitle from '~/packages/list/components/package_title.vue'; +import TitleArea from '~/vue_shared/components/registry/title_area.vue'; +import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue'; +import { LIST_INTRO_TEXT, LIST_TITLE_TEXT } from '~/packages/list//constants'; + +describe('PackageTitle', () => { + let wrapper; + let store; + + const findTitleArea = () => wrapper.find(TitleArea); + const findMetadataItem = () => wrapper.find(MetadataItem); + + const mountComponent = (propsData = { packageHelpUrl: 'foo' }) => { + wrapper = shallowMount(PackageTitle, { + store, + propsData, + stubs: { + TitleArea, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('title area', () => { + it('exists', () => { + mountComponent(); + + expect(findTitleArea().exists()).toBe(true); + }); + + it('has the correct props', () => { + mountComponent(); + + expect(findTitleArea().props()).toMatchObject({ + title: LIST_TITLE_TEXT, + infoMessages: [{ text: LIST_INTRO_TEXT, link: 'foo' }], + }); + }); + }); + + describe.each` + packagesCount | exist | text + ${null} | ${false} | ${''} + ${undefined} | ${false} | ${''} + ${0} | ${true} | ${'0 Packages'} + ${1} | ${true} | ${'1 Package'} + ${2} | ${true} | ${'2 Packages'} + `('when packagesCount is $packagesCount metadata item', ({ packagesCount, exist, text }) => { + beforeEach(() => { + mountComponent({ packagesCount, packageHelpUrl: 'foo' }); + }); + + it(`is ${exist} that it exists`, () => { + expect(findMetadataItem().exists()).toBe(exist); + }); + + if (exist) { + it('has the correct props', () => { + expect(findMetadataItem().props()).toMatchObject({ + icon: 'package', + text, + }); + }); + } + }); +}); diff --git a/spec/frontend/vue_mr_widget/deployment/deployment_actions_spec.js b/spec/frontend/vue_mr_widget/deployment/deployment_actions_spec.js index 1711efb5512..13c0665f929 100644 --- a/spec/frontend/vue_mr_widget/deployment/deployment_actions_spec.js +++ b/spec/frontend/vue_mr_widget/deployment/deployment_actions_spec.js @@ -31,10 +31,7 @@ describe('DeploymentAction component', () => { wrapper.destroy(); } - wrapper = mount(DeploymentActions, { - ...options, - provide: { glFeatures: { deployFromFooter: true } }, - }); + wrapper = mount(DeploymentActions, options); }; const findStopButton = () => wrapper.find('.js-stop-env'); diff --git a/spec/frontend/vue_mr_widget/deployment/deployment_spec.js b/spec/frontend/vue_mr_widget/deployment/deployment_spec.js index ce395de3b5d..17d7fcc4bff 100644 --- a/spec/frontend/vue_mr_widget/deployment/deployment_spec.js +++ b/spec/frontend/vue_mr_widget/deployment/deployment_spec.js @@ -19,10 +19,7 @@ describe('Deployment component', () => { if (wrapper && wrapper.destroy) { wrapper.destroy(); } - wrapper = mount(DeploymentComponent, { - ...options, - provide: { glFeatures: { deployFromFooter: true } }, - }); + wrapper = mount(DeploymentComponent, options); }; beforeEach(() => { diff --git a/spec/frontend_integration/.eslintrc.yml b/spec/frontend_integration/.eslintrc.yml index 26b6f935ffb..2460e218f59 100644 --- a/spec/frontend_integration/.eslintrc.yml +++ b/spec/frontend_integration/.eslintrc.yml @@ -4,3 +4,5 @@ settings: import/resolver: jest: jestConfigFile: 'jest.config.integration.js' +globals: + mockServer: false diff --git a/spec/frontend_integration/ide/ide_helper.js b/spec/frontend_integration/ide/ide_helper.js new file mode 100644 index 00000000000..a43695fea8f --- /dev/null +++ b/spec/frontend_integration/ide/ide_helper.js @@ -0,0 +1,102 @@ +import { findAllByText, fireEvent, getByLabelText, screen } from '@testing-library/dom'; + +const isFileRowOpen = row => row.matches('.is-open'); + +const getLeftSidebar = () => screen.getByTestId('left-sidebar'); + +const clickOnLeftSidebarTab = name => { + const sidebar = getLeftSidebar(); + + const button = getByLabelText(sidebar, name); + + button.click(); +}; + +const findMonacoEditor = () => + screen.findByLabelText(/Editor content;/).then(x => x.closest('.monaco-editor')); + +const findAndSetEditorValue = async value => { + const editor = await findMonacoEditor(); + const uri = editor.getAttribute('data-uri'); + + window.monaco.editor.getModel(uri).setValue(value); +}; + +const findTreeBody = () => screen.findByTestId('ide-tree-body', {}, { timeout: 5000 }); + +const findFileRowContainer = (row = null) => + row ? Promise.resolve(row.parentElement) : findTreeBody(); + +const findFileChild = async (row, name, index = 0) => { + const container = await findFileRowContainer(row); + const children = await findAllByText(container, name, { selector: '.file-row-name' }); + + return children.map(x => x.closest('.file-row')).find(x => x.dataset.level === index.toString()); +}; + +const openFileRow = row => { + if (!row || isFileRowOpen(row)) { + return; + } + + row.click(); +}; + +const findAndTraverseToPath = async (path, index = 0, row = null) => { + if (!path) { + return row; + } + + const [name, ...restOfPath] = path.split('/'); + + openFileRow(row); + + const child = await findFileChild(row, name, index); + + return findAndTraverseToPath(restOfPath.join('/'), index + 1, child); +}; + +const clickFileRowAction = (row, name) => { + fireEvent.mouseOver(row); + + const dropdownButton = getByLabelText(row, 'Create new file or directory'); + dropdownButton.click(); + + const dropdownAction = getByLabelText(dropdownButton.parentNode, name); + dropdownAction.click(); +}; + +const findAndSetFileName = async value => { + const nameField = await screen.findByTestId('file-name-field'); + fireEvent.input(nameField, { target: { value } }); + + const createButton = screen.getByText('Create file'); + createButton.click(); +}; + +export const createFile = async (path, content) => { + const parentPath = path + .split('/') + .slice(0, -1) + .join('/'); + + const parentRow = await findAndTraverseToPath(parentPath); + clickFileRowAction(parentRow, 'New file'); + + await findAndSetFileName(path); + await findAndSetEditorValue(content); +}; + +export const deleteFile = async path => { + const row = await findAndTraverseToPath(path); + clickFileRowAction(row, 'Delete'); +}; + +export const commit = async () => { + clickOnLeftSidebarTab('Commit'); + screen.getByTestId('begin-commit-button').click(); + + await screen.findByLabelText(/Commit to .+ branch/).then(x => x.click()); + + screen.getByText('Commit').click(); +}; diff --git a/spec/frontend_integration/ide/ide_integration_spec.js b/spec/frontend_integration/ide/ide_integration_spec.js index 91d89c26ec1..c4d0c4df8de 100644 --- a/spec/frontend_integration/ide/ide_integration_spec.js +++ b/spec/frontend_integration/ide/ide_integration_spec.js @@ -1,17 +1,10 @@ -/** - * WARNING: WIP - * - * Please do not copy from this spec or use it as an example for anything. - * - * This is in place to iteratively set up the frontend integration testing environment - * and will be improved upon in a later iteration. - * - * See https://gitlab.com/gitlab-org/gitlab/-/issues/208800 for more information. - */ import { TEST_HOST } from 'helpers/test_constants'; +import { waitForText } from 'helpers/wait_for_text'; import { useOverclockTimers } from 'test_helpers/utils/overclock_timers'; +import { createCommitId } from 'test_helpers/factories/commit_id'; import { initIde } from '~/ide'; import extendStore from '~/ide/stores/extend'; +import * as ideHelper from './ide_helper'; const TEST_DATASET = { emptyStateSvgPath: '/test/empty_state.svg', @@ -59,4 +52,38 @@ describe('WebIDE', () => { expect(root).toMatchSnapshot(); }); + + it('user commits changes', async () => { + createComponent(); + + await ideHelper.createFile('foo/bar/test.txt', 'Lorem ipsum dolar sit'); + await ideHelper.deleteFile('foo/bar/.gitkeep'); + await ideHelper.commit(); + + const commitId = createCommitId(1); + const commitShortId = commitId.slice(0, 8); + + await waitForText('All changes are committed'); + await waitForText(commitShortId); + + expect(mockServer.db.branches.findBy({ name: 'master' }).commit).toMatchObject({ + short_id: commitShortId, + id: commitId, + message: 'Update foo/bar/test.txt\nDeleted foo/bar/.gitkeep', + __actions: [ + { + action: 'create', + content: 'Lorem ipsum dolar sit\n', + encoding: 'text', + file_path: 'foo/bar/test.txt', + last_commit_id: '', + }, + { + action: 'delete', + encoding: 'text', + file_path: 'foo/bar/.gitkeep', + }, + ], + }); + }); }); diff --git a/spec/frontend_integration/test_helpers/setup/setup_mock_server.js b/spec/frontend_integration/test_helpers/setup/setup_mock_server.js index 343aeebf88e..43a21deed25 100644 --- a/spec/frontend_integration/test_helpers/setup/setup_mock_server.js +++ b/spec/frontend_integration/test_helpers/setup/setup_mock_server.js @@ -1,13 +1,12 @@ import { createMockServer } from '../mock_server'; beforeEach(() => { + if (global.mockServer) { + global.mockServer.shutdown(); + } + const server = createMockServer(); server.logging = false; global.mockServer = server; }); - -afterEach(() => { - global.mockServer.shutdown(); - global.mockServer = null; -}); diff --git a/spec/lib/gitlab/ci/trace/checksum_spec.rb b/spec/lib/gitlab/ci/trace/checksum_spec.rb index 4bd96aad4e8..794794c3f69 100644 --- a/spec/lib/gitlab/ci/trace/checksum_spec.rb +++ b/spec/lib/gitlab/ci/trace/checksum_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Gitlab::Ci::Trace::Checksum do context 'when build pending state exists' do before do - create(:ci_build_pending_state, build: build, trace_checksum: 'crc32:3564598592') + create(:ci_build_pending_state, build: build, trace_checksum: 'crc32:d4777540') end context 'when matching persisted trace chunks exist' do @@ -70,8 +70,8 @@ RSpec.describe Gitlab::Ci::Trace::Checksum do context 'when build pending state is missing' do describe '#state_crc32' do - it 'returns zero' do - expect(subject.state_crc32).to be_zero + it 'returns nil' do + expect(subject.state_crc32).to be_nil end end diff --git a/spec/models/ci/build_pending_state_spec.rb b/spec/models/ci/build_pending_state_spec.rb new file mode 100644 index 00000000000..a546d2aff65 --- /dev/null +++ b/spec/models/ci/build_pending_state_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::BuildPendingState do + describe '#crc32' do + context 'when checksum does not exist' do + let(:pending_state) do + build(:ci_build_pending_state, trace_checksum: nil) + end + + it 'returns nil' do + expect(pending_state.crc32).to be_nil + end + end + + context 'when checksum is in hexadecimal' do + let(:pending_state) do + build(:ci_build_pending_state, trace_checksum: 'crc32:75bcd15') + end + + it 'returns decimal representation of the checksum' do + expect(pending_state.crc32).to eq 123456789 + end + end + end +end diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index 024539e34ec..dd9b96f39ad 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -47,6 +47,7 @@ RSpec.describe Clusters::Cluster, :use_clean_rails_memory_store_caching do it { is_expected.to delegate_method(:external_hostname).to(:application_ingress).with_prefix } it { is_expected.to respond_to :project } + it { is_expected.to be_namespace_per_environment } describe 'applications have inverse_of: :cluster option' do let(:cluster) { create(:cluster) } diff --git a/spec/requests/api/admin/instance_clusters_spec.rb b/spec/requests/api/admin/instance_clusters_spec.rb index b68541b5d92..9d0661089a9 100644 --- a/spec/requests/api/admin/instance_clusters_spec.rb +++ b/spec/requests/api/admin/instance_clusters_spec.rb @@ -162,6 +162,7 @@ RSpec.describe ::API::Admin::InstanceClusters do name: 'test-instance-cluster', domain: 'domain.example.com', managed: false, + namespace_per_environment: false, platform_kubernetes_attributes: platform_kubernetes_attributes, clusterable: clusterable } @@ -206,6 +207,7 @@ RSpec.describe ::API::Admin::InstanceClusters do expect(cluster_result.enabled).to eq(true) expect(platform_kubernetes.authorization_type).to eq('rbac') expect(cluster_result.managed).to be_falsy + expect(cluster_result.namespace_per_environment).to eq(false) expect(platform_kubernetes.api_url).to eq("https://example.com") expect(platform_kubernetes.token).to eq('sample-token') end @@ -235,6 +237,22 @@ RSpec.describe ::API::Admin::InstanceClusters do end end + context 'when namespace_per_environment is not set' do + let(:cluster_params) do + { + name: 'test-cluster', + domain: 'domain.example.com', + platform_kubernetes_attributes: platform_kubernetes_attributes + } + end + + it 'defaults to true' do + cluster_result = Clusters::Cluster.find(json_response['id']) + + expect(cluster_result).to be_namespace_per_environment + end + end + context 'when an instance cluster already exists' do it 'allows user to add multiple clusters' do post api('/admin/clusters/add', admin_user), params: multiple_cluster_params diff --git a/spec/requests/api/group_clusters_spec.rb b/spec/requests/api/group_clusters_spec.rb index 068af1485e2..eb21ae9468c 100644 --- a/spec/requests/api/group_clusters_spec.rb +++ b/spec/requests/api/group_clusters_spec.rb @@ -172,6 +172,7 @@ RSpec.describe API::GroupClusters do name: 'test-cluster', domain: 'domain.example.com', managed: false, + namespace_per_environment: false, platform_kubernetes_attributes: platform_kubernetes_attributes, management_project_id: management_project_id } @@ -206,6 +207,7 @@ RSpec.describe API::GroupClusters do expect(cluster_result.domain).to eq('domain.example.com') expect(cluster_result.managed).to be_falsy expect(cluster_result.management_project_id).to eq management_project_id + expect(cluster_result.namespace_per_environment).to eq(false) expect(platform_kubernetes.rbac?).to be_truthy expect(platform_kubernetes.api_url).to eq(api_url) expect(platform_kubernetes.token).to eq('sample-token') @@ -237,6 +239,22 @@ RSpec.describe API::GroupClusters do end end + context 'when namespace_per_environment is not set' do + let(:cluster_params) do + { + name: 'test-cluster', + domain: 'domain.example.com', + platform_kubernetes_attributes: platform_kubernetes_attributes + } + end + + it 'defaults to true' do + cluster_result = Clusters::Cluster.find(json_response['id']) + + expect(cluster_result).to be_namespace_per_environment + end + end + context 'current user does not have access to management_project_id' do let(:management_project_id) { create(:project).id } diff --git a/spec/requests/api/project_clusters_spec.rb b/spec/requests/api/project_clusters_spec.rb index ff35e380476..7b37862af74 100644 --- a/spec/requests/api/project_clusters_spec.rb +++ b/spec/requests/api/project_clusters_spec.rb @@ -171,6 +171,7 @@ RSpec.describe API::ProjectClusters do name: 'test-cluster', domain: 'domain.example.com', managed: false, + namespace_per_environment: false, platform_kubernetes_attributes: platform_kubernetes_attributes, management_project_id: management_project_id } @@ -202,6 +203,7 @@ RSpec.describe API::ProjectClusters do expect(cluster_result.domain).to eq('domain.example.com') expect(cluster_result.managed).to be_falsy expect(cluster_result.management_project_id).to eq management_project_id + expect(cluster_result.namespace_per_environment).to eq(false) expect(platform_kubernetes.rbac?).to be_truthy expect(platform_kubernetes.api_url).to eq(api_url) expect(platform_kubernetes.namespace).to eq(namespace) @@ -235,6 +237,22 @@ RSpec.describe API::ProjectClusters do end end + context 'when namespace_per_environment is not set' do + let(:cluster_params) do + { + name: 'test-cluster', + domain: 'domain.example.com', + platform_kubernetes_attributes: platform_kubernetes_attributes + } + end + + it 'defaults to true' do + cluster_result = Clusters::Cluster.find(json_response['id']) + + expect(cluster_result).to be_namespace_per_environment + end + end + context 'current user does not have access to management_project_id' do let(:management_project_id) { create(:project).id } diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index e0893ed6de3..c28c3449485 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -731,30 +731,11 @@ RSpec.describe Ci::CreatePipelineService do .and_call_original end - context 'when ci_pipeline_rewind_iid is enabled' do - before do - stub_feature_flags(ci_pipeline_rewind_iid: true) - end - - it 'rewinds iid' do - result = execute_service - - expect(result).not_to be_persisted - expect(internal_id.last_value).to eq(0) - end - end - - context 'when ci_pipeline_rewind_iid is disabled' do - before do - stub_feature_flags(ci_pipeline_rewind_iid: false) - end - - it 'does not rewind iid' do - result = execute_service + it 'rewinds iid' do + result = execute_service - expect(result).not_to be_persisted - expect(internal_id.last_value).to eq(1) - end + expect(result).not_to be_persisted + expect(internal_id.last_value).to eq(0) end end end diff --git a/spec/services/ci/update_build_state_service_spec.rb b/spec/services/ci/update_build_state_service_spec.rb index 751f9f77908..80735985406 100644 --- a/spec/services/ci/update_build_state_service_spec.rb +++ b/spec/services/ci/update_build_state_service_spec.rb @@ -125,7 +125,7 @@ RSpec.describe Ci::UpdateBuildStateService do end context 'when trace checksum is valid' do - let(:params) { { checksum: 'crc32:3984772369', state: 'success' } } + let(:params) { { checksum: 'crc32:ed82cd11', state: 'success' } } it 'does not increment invalid trace metric' do execute_with_stubbed_metrics! diff --git a/spec/services/projects/container_repository/delete_tags_service_spec.rb b/spec/services/projects/container_repository/delete_tags_service_spec.rb index 5116427dad2..001c76eb4af 100644 --- a/spec/services/projects/container_repository/delete_tags_service_spec.rb +++ b/spec/services/projects/container_repository/delete_tags_service_spec.rb @@ -104,59 +104,35 @@ RSpec.describe Projects::ContainerRepository::DeleteTagsService do end context 'when the registry supports fast delete' do - context 'and the feature is enabled' do - before do - allow(repository.client).to receive(:supports_tag_delete?).and_return(true) - end - - it_behaves_like 'calling the correct delete tags service', ::Projects::ContainerRepository::Gitlab::DeleteTagsService - - it_behaves_like 'handling invalid params' + before do + allow(repository.client).to receive(:supports_tag_delete?).and_return(true) + end - context 'with the real service' do - before do - stub_delete_reference_requests(tags) - expect_delete_tag_by_names(tags) - end + it_behaves_like 'calling the correct delete tags service', ::Projects::ContainerRepository::Gitlab::DeleteTagsService - it { is_expected.to include(status: :success) } + it_behaves_like 'handling invalid params' - it_behaves_like 'logging a success response' + context 'with the real service' do + before do + stub_delete_reference_requests(tags) + expect_delete_tag_by_names(tags) end - context 'with a timeout error' do - before do - expect_next_instance_of(::Projects::ContainerRepository::Gitlab::DeleteTagsService) do |delete_service| - expect(delete_service).to receive(:delete_tags).and_raise(::Projects::ContainerRepository::Gitlab::DeleteTagsService::TimeoutError) - end - end - - it { is_expected.to include(status: :error, message: 'timeout while deleting tags') } + it { is_expected.to include(status: :success) } - it_behaves_like 'logging an error response', message: 'timeout while deleting tags' - end + it_behaves_like 'logging a success response' end - context 'and the feature is disabled' do + context 'with a timeout error' do before do - stub_feature_flags(container_registry_fast_tag_delete: false) - end - - it_behaves_like 'calling the correct delete tags service', ::Projects::ContainerRepository::ThirdParty::DeleteTagsService - - it_behaves_like 'handling invalid params' - - context 'with the real service' do - before do - stub_upload('sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3') - tags.each { |tag| stub_put_manifest_request(tag) } - expect_delete_tag_by_digest('sha256:dummy') + expect_next_instance_of(::Projects::ContainerRepository::Gitlab::DeleteTagsService) do |delete_service| + expect(delete_service).to receive(:delete_tags).and_raise(::Projects::ContainerRepository::Gitlab::DeleteTagsService::TimeoutError) end + end - it { is_expected.to include(status: :success) } + it { is_expected.to include(status: :error, message: 'timeout while deleting tags') } - it_behaves_like 'logging a success response' - end + it_behaves_like 'logging an error response', message: 'timeout while deleting tags' end end |