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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-15 15:09:29 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-15 15:09:29 +0300
commit6986c1adc235859111e45593bb0bd61e70892d3c (patch)
tree99912b55cd4c39c7ce24120269dfc870ff8f3705
parentc5b1e86b43f136d8a43cab867ddc49a02751c45a (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/issue_templates/Basic Proposal.md2
-rw-r--r--.gitlab/issue_templates/Feature proposal.md2
-rw-r--r--.gitlab/issue_templates/Lean Feature Proposal.md2
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.vue13
-rw-r--r--app/assets/javascripts/issuable_list/components/issuable_item.vue19
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue32
-rw-r--r--app/assets/javascripts/pipelines/mixins/pipelines_mixin.js (renamed from app/assets/javascripts/pipelines/mixins/pipelines.js)49
-rw-r--r--app/assets/javascripts/pipelines/mixins/pipelines_pagination_api_mixin.js66
-rw-r--r--app/assets/javascripts/terraform/components/states_table.vue10
-rw-r--r--app/assets/javascripts/terraform/components/states_table_actions.vue14
-rw-r--r--app/assets/javascripts/terraform/index.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/toggle_button.vue88
-rw-r--r--app/finders/labels_finder.rb2
-rw-r--r--app/graphql/resolvers/group_labels_resolver.rb17
-rw-r--r--app/graphql/resolvers/labels_resolver.rb46
-rw-r--r--app/graphql/types/group_type.rb13
-rw-r--r--app/graphql/types/packages/package_type_enum.rb2
-rw-r--r--app/graphql/types/project_type.rb13
-rw-r--r--app/models/concerns/enums/ci/pipeline.rb3
-rw-r--r--changelogs/unreleased/292713-remove-loading-tf.yml5
-rw-r--r--changelogs/unreleased/add-labels-resolver.yml6
-rw-r--r--changelogs/unreleased/count-implicit-auto-devops-inclusions-separately.yml5
-rw-r--r--changelogs/unreleased/vs-fix-alignment-rebase-in-progress.yml5
-rw-r--r--config/feature_flags/development/ci_mini_pipeline_gl_dropdown.yml2
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql29
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json94
-rw-r--r--doc/api/graphql/reference/index.md7
-rw-r--r--doc/user/application_security/sast/analyzers.md36
-rw-r--r--doc/user/compliance/license_compliance/index.md19
-rw-r--r--lib/gitlab/ci/pipeline/chain/config/content.rb8
-rw-r--r--lib/gitlab/ci/pipeline/chain/template_usage.rb2
-rw-r--r--lib/gitlab/url_blocker.rb5
-rw-r--r--lib/gitlab/usage_data_counters/ci_template_unique_counter.rb11
-rw-r--r--lib/gitlab/usage_data_counters/known_events/ci_templates.yml91
-rw-r--r--lib/gitlab/usage_data_counters/known_events/common.yml51
-rw-r--r--locale/gitlab.pot12
-rw-r--r--spec/features/projects/terraform_spec.rb2
-rw-r--r--spec/frontend/issuable_list/components/issuable_item_spec.js46
-rw-r--r--spec/frontend/terraform/components/states_table_actions_spec.js75
-rw-r--r--spec/frontend/terraform/components/states_table_spec.js27
-rw-r--r--spec/frontend/vue_shared/components/toggle_button_spec.js96
-rw-r--r--spec/graphql/resolvers/group_labels_resolver_spec.rb96
-rw-r--r--spec/graphql/resolvers/labels_resolver_spec.rb96
-rw-r--r--spec/graphql/types/group_type_spec.rb4
-rw-r--r--spec/graphql/types/project_type_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb2
-rw-r--r--spec/lib/gitlab/url_blocker_spec.rb41
-rw-r--r--spec/lib/gitlab/usage_data_counters/ci_template_unique_counter_spec.rb86
-rw-r--r--spec/support/helpers/dns_helpers.rb35
-rw-r--r--spec/support/helpers/stub_requests.rb19
-rw-r--r--spec/support/shared_examples/graphql/label_fields.rb2
53 files changed, 911 insertions, 507 deletions
diff --git a/.gitlab/issue_templates/Basic Proposal.md b/.gitlab/issue_templates/Basic Proposal.md
index 4232561354c..8d47e87f8a3 100644
--- a/.gitlab/issue_templates/Basic Proposal.md
+++ b/.gitlab/issue_templates/Basic Proposal.md
@@ -6,6 +6,6 @@
<!-- Consider adding related issues and epics to this issue. You can also reference the Feature Proposal Template (https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/Feature%20proposal.md) for additional details to consider adding to this issue. Additionally, as a data oriented organization, when your feature exits planning breakdown, consider adding the `What does success look like, and how can we measure that?` section.
-/label ~"group::" ~"section::" ~"Category::" ~"GitLab Core"/~"GitLab Starter"/~"GitLab Premium"/~"GitLab Ultimate"
+/label ~"group::" ~"section::" ~"Category::" ~"GitLab Core"/~"GitLab Premium"/~"GitLab Ultimate"
-->
diff --git a/.gitlab/issue_templates/Feature proposal.md b/.gitlab/issue_templates/Feature proposal.md
index 1090a04ad36..2cdf2341c88 100644
--- a/.gitlab/issue_templates/Feature proposal.md
+++ b/.gitlab/issue_templates/Feature proposal.md
@@ -97,7 +97,7 @@ Create tracking issue using the the Snowplow event tracking template. See https:
### What is the type of buyer?
<!-- What is the buyer persona for this feature? See https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/buyer-persona/
-In which enterprise tier should this feature go? See https://about.gitlab.com/handbook/product/pricing/#four-tiers -->
+In which enterprise tier should this feature go? See https://about.gitlab.com/handbook/product/pricing/#three-tiers -->
### Is this a cross-stage feature?
diff --git a/.gitlab/issue_templates/Lean Feature Proposal.md b/.gitlab/issue_templates/Lean Feature Proposal.md
index a1f217fc624..fb9ac306f31 100644
--- a/.gitlab/issue_templates/Lean Feature Proposal.md
+++ b/.gitlab/issue_templates/Lean Feature Proposal.md
@@ -96,7 +96,7 @@ Define both the success metrics and acceptance criteria. Note that success metri
### What is the type of buyer?
What is the buyer persona for this feature? See https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/buyer-persona/
-In which enterprise tier should this feature go? See https://about.gitlab.com/handbook/product/pricing/#four-tiers
+In which enterprise tier should this feature go? See https://about.gitlab.com/handbook/product/pricing/#three-tiers
### Is this a cross-stage feature?
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index c675e6a366e..f690344ee76 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-f319db221de49c979f3606e3934bd76c218f813d
+2d3b680ab7d7652377c17c962c2628d98a6ffc9a
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
index 4e93bd8dcf1..4362263b0dd 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.vue
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
@@ -2,21 +2,24 @@
import { GlButton, GlLoadingIcon, GlModal, GlLink } from '@gitlab/ui';
import { getParameterByName } from '~/lib/utils/common_utils';
import eventHub from '~/pipelines/event_hub';
-import pipelinesMixin from '~/pipelines/mixins/pipelines';
-import PipelinesPaginationApiMixin from '~/pipelines/mixins/pipelines_pagination_api_mixin';
+import PipelinesMixin from '~/pipelines/mixins/pipelines_mixin';
import PipelinesService from '~/pipelines/services/pipelines_service';
import PipelineStore from '~/pipelines/stores/pipelines_store';
+import PipelinesTableComponent from '~/pipelines/components/pipelines_list/pipelines_table.vue';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
+import SvgBlankState from '~/pipelines/components/pipelines_list/blank_state.vue';
export default {
components: {
- TablePagination,
GlButton,
+ GlLink,
GlLoadingIcon,
GlModal,
- GlLink,
+ PipelinesTableComponent,
+ TablePagination,
+ SvgBlankState,
},
- mixins: [pipelinesMixin, PipelinesPaginationApiMixin],
+ mixins: [PipelinesMixin],
props: {
endpoint: {
type: String,
diff --git a/app/assets/javascripts/issuable_list/components/issuable_item.vue b/app/assets/javascripts/issuable_list/components/issuable_item.vue
index b114f6e7278..39852eba71a 100644
--- a/app/assets/javascripts/issuable_list/components/issuable_item.vue
+++ b/app/assets/javascripts/issuable_list/components/issuable_item.vue
@@ -4,6 +4,7 @@ import { GlLink, GlIcon, GlLabel, GlFormCheckbox, GlTooltipDirective } from '@gi
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { isScopedLabel } from '~/lib/utils/common_utils';
import { getTimeago } from '~/lib/utils/datetime_utility';
+import { isExternal, setUrlFragment } from '~/lib/utils/url_utility';
import { __, sprintf } from '~/locale';
import IssuableAssignees from '~/vue_shared/components/issue/issue_assignees.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
@@ -47,17 +48,14 @@ export default {
author() {
return this.issuable.author;
},
+ webUrl() {
+ return this.issuable.gitlabWebUrl || this.issuable.webUrl;
+ },
authorId() {
return getIdFromGraphQLId(`${this.author.id}`);
},
isIssuableUrlExternal() {
- // Check if URL is relative, which means it is internal.
- if (!/^https?:\/\//g.test(this.issuable.webUrl)) {
- return false;
- }
- // In case URL is absolute, it may or may not be internal,
- // hence use `gon.gitlab_url` which is current instance domain.
- return !this.issuable.webUrl.includes(gon.gitlab_url);
+ return isExternal(this.webUrl);
},
labels() {
return this.issuable.labels?.nodes || this.issuable.labels || [];
@@ -91,6 +89,9 @@ export default {
this.hasSlotContents('status') || this.showDiscussions || this.issuable.assignees,
);
},
+ issuableNotesLink() {
+ return setUrlFragment(this.webUrl, 'notes');
+ },
},
methods: {
hasSlotContents(slotName) {
@@ -144,7 +145,7 @@ export default {
name="eye-slash"
:title="__('Confidential')"
/>
- <gl-link :href="issuable.webUrl" v-bind="issuableTitleProps"
+ <gl-link :href="webUrl" v-bind="issuableTitleProps"
>{{ issuable.title
}}<gl-icon v-if="isIssuableUrlExternal" name="external-link" class="gl-ml-2"
/></gl-link>
@@ -206,7 +207,7 @@ export default {
<gl-link
v-gl-tooltip:tooltipcontainer.top
:title="__('Comments')"
- :href="`${issuable.webUrl}#notes`"
+ :href="issuableNotesLink"
:class="{ 'no-comments': !issuable.userDiscussionsCount }"
class="gl-reset-color!"
>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
index 3a9d181066c..a45adc766e3 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
@@ -1,5 +1,5 @@
<script>
-import { GlIcon } from '@gitlab/ui';
+import { GlIcon, GlLoadingIcon } from '@gitlab/ui';
import { isEqual } from 'lodash';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { getParameterByName } from '~/lib/utils/common_utils';
@@ -7,22 +7,28 @@ import { __, s__ } from '~/locale';
import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
import { ANY_TRIGGER_AUTHOR, RAW_TEXT_WARNING, FILTER_TAG_IDENTIFIER } from '../../constants';
-import pipelinesMixin from '../../mixins/pipelines';
-import PipelinesPaginationApiMixin from '../../mixins/pipelines_pagination_api_mixin';
+import PipelinesMixin from '../../mixins/pipelines_mixin';
import PipelinesService from '../../services/pipelines_service';
import { validateParams } from '../../utils';
+import EmptyState from './empty_state.vue';
import NavigationControls from './nav_controls.vue';
import PipelinesFilteredSearch from './pipelines_filtered_search.vue';
+import PipelinesTableComponent from './pipelines_table.vue';
+import SvgBlankState from './blank_state.vue';
export default {
components: {
- TablePagination,
+ EmptyState,
+ GlIcon,
+ GlLoadingIcon,
NavigationTabs,
NavigationControls,
PipelinesFilteredSearch,
- GlIcon,
+ PipelinesTableComponent,
+ SvgBlankState,
+ TablePagination,
},
- mixins: [pipelinesMixin, PipelinesPaginationApiMixin],
+ mixins: [PipelinesMixin],
props: {
store: {
type: Object,
@@ -217,6 +223,20 @@ export default {
this.requestData = { page: this.page, scope: this.scope, ...this.validatedParams };
},
methods: {
+ onChangeTab(scope) {
+ if (this.scope === scope) {
+ return;
+ }
+
+ let params = {
+ scope,
+ page: '1',
+ };
+
+ params = this.onChangeWithFilter(params);
+
+ this.updateContent(params);
+ },
successCallback(resp) {
// Because we are polling & the user is interacting verify if the response received
// matches the last request made
diff --git a/app/assets/javascripts/pipelines/mixins/pipelines.js b/app/assets/javascripts/pipelines/mixins/pipelines_mixin.js
index bff986261ee..8444d43513b 100644
--- a/app/assets/javascripts/pipelines/mixins/pipelines.js
+++ b/app/assets/javascripts/pipelines/mixins/pipelines_mixin.js
@@ -1,21 +1,13 @@
-import { GlLoadingIcon } from '@gitlab/ui';
import Visibility from 'visibilityjs';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import Poll from '~/lib/utils/poll';
+import { historyPushState, buildUrlWithCurrentLocation } from '~/lib/utils/common_utils';
+import { validateParams } from '~/pipelines/utils';
import { __ } from '~/locale';
-import SvgBlankState from '../components/pipelines_list/blank_state.vue';
-import EmptyState from '../components/pipelines_list/empty_state.vue';
-import PipelinesTableComponent from '../components/pipelines_list/pipelines_table.vue';
import { CANCEL_REQUEST } from '../constants';
import eventHub from '../event_hub';
export default {
- components: {
- PipelinesTableComponent,
- SvgBlankState,
- EmptyState,
- GlLoadingIcon,
- },
data() {
return {
isLoading: false,
@@ -76,6 +68,25 @@ export default {
this.poll.stop();
},
methods: {
+ updateInternalState(parameters) {
+ this.poll.stop();
+
+ const queryString = Object.keys(parameters)
+ .map((parameter) => {
+ const value = parameters[parameter];
+ // update internal state for UI
+ this[parameter] = value;
+ return `${parameter}=${encodeURIComponent(value)}`;
+ })
+ .join('&');
+
+ // update polling parameters
+ this.requestData = parameters;
+
+ historyPushState(buildUrlWithCurrentLocation(`?${queryString}`));
+
+ this.isLoading = true;
+ },
/**
* Handles URL and query parameter changes.
* When the user uses the pagination or the tabs,
@@ -184,5 +195,23 @@ export default {
})
.finally(() => this.store.toggleIsRunningPipeline(false));
},
+ onChangePage(page) {
+ /* URLS parameters are strings, we need to parse to match types */
+ let params = {
+ page: Number(page).toString(),
+ };
+
+ if (this.scope) {
+ params.scope = this.scope;
+ }
+
+ params = this.onChangeWithFilter(params);
+
+ this.updateContent(params);
+ },
+
+ onChangeWithFilter(params) {
+ return { ...params, ...validateParams(this.requestData) };
+ },
},
};
diff --git a/app/assets/javascripts/pipelines/mixins/pipelines_pagination_api_mixin.js b/app/assets/javascripts/pipelines/mixins/pipelines_pagination_api_mixin.js
deleted file mode 100644
index b62fe196a6f..00000000000
--- a/app/assets/javascripts/pipelines/mixins/pipelines_pagination_api_mixin.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * API callbacks for pagination and tabs
- *
- * Components need to have `scope`, `page` and `requestData`
- */
-import { validateParams } from '~/pipelines/utils';
-import { historyPushState, buildUrlWithCurrentLocation } from '../../lib/utils/common_utils';
-
-export default {
- methods: {
- onChangeTab(scope) {
- if (this.scope === scope) {
- return;
- }
-
- let params = {
- scope,
- page: '1',
- };
-
- params = this.onChangeWithFilter(params);
-
- this.updateContent(params);
- },
-
- onChangePage(page) {
- /* URLS parameters are strings, we need to parse to match types */
- let params = {
- page: Number(page).toString(),
- };
-
- if (this.scope) {
- params.scope = this.scope;
- }
-
- params = this.onChangeWithFilter(params);
-
- this.updateContent(params);
- },
-
- onChangeWithFilter(params) {
- return { ...params, ...validateParams(this.requestData) };
- },
-
- updateInternalState(parameters) {
- // stop polling
- this.poll.stop();
-
- const queryString = Object.keys(parameters)
- .map((parameter) => {
- const value = parameters[parameter];
- // update internal state for UI
- this[parameter] = value;
- return `${parameter}=${encodeURIComponent(value)}`;
- })
- .join('&');
-
- // update polling parameters
- this.requestData = parameters;
-
- historyPushState(buildUrlWithCurrentLocation(`?${queryString}`));
-
- this.isLoading = true;
- },
- },
-};
diff --git a/app/assets/javascripts/terraform/components/states_table.vue b/app/assets/javascripts/terraform/components/states_table.vue
index e4b5a66c3fc..2577664a5e8 100644
--- a/app/assets/javascripts/terraform/components/states_table.vue
+++ b/app/assets/javascripts/terraform/components/states_table.vue
@@ -80,6 +80,7 @@ export default {
lockingState: s__('Terraform|Locking state'),
name: s__('Terraform|Name'),
pipeline: s__('Terraform|Pipeline'),
+ removing: s__('Terraform|Removing'),
unknownUser: s__('Terraform|Unknown User'),
unlockingState: s__('Terraform|Unlocking state'),
updatedUser: s__('Terraform|%{user} updated %{timeAgo}'),
@@ -141,6 +142,15 @@ export default {
</p>
</div>
+ <div v-else-if="item.loadingRemove" class="gl-mx-3">
+ <p
+ class="gl-display-flex gl-justify-content-start gl-align-items-baseline gl-m-0 gl-text-red-500"
+ >
+ <gl-loading-icon class="gl-pr-1" />
+ {{ $options.i18n.removing }}
+ </p>
+ </div>
+
<div
v-else-if="item.lockedAt"
:id="`terraformLockedBadgeContainer${item.name}`"
diff --git a/app/assets/javascripts/terraform/components/states_table_actions.vue b/app/assets/javascripts/terraform/components/states_table_actions.vue
index 65dcc430f9d..c4fd97188de 100644
--- a/app/assets/javascripts/terraform/components/states_table_actions.vue
+++ b/app/assets/javascripts/terraform/components/states_table_actions.vue
@@ -9,7 +9,7 @@ import {
GlModal,
GlSprintf,
} from '@gitlab/ui';
-import { s__ } from '~/locale';
+import { s__, sprintf } from '~/locale';
import addDataToState from '../graphql/mutations/add_data_to_state.mutation.graphql';
import lockState from '../graphql/mutations/lock_state.mutation.graphql';
import removeState from '../graphql/mutations/remove_state.mutation.graphql';
@@ -52,6 +52,7 @@ export default {
),
modalRemove: s__('Terraform|Remove'),
remove: s__('Terraform|Remove state file and versions'),
+ removeSuccessful: s__('Terraform|%{name} successfully removed'),
unlock: s__('Terraform|Unlock'),
},
computed: {
@@ -121,10 +122,13 @@ export default {
loadingRemove: true,
});
- this.stateActionMutation(removeState);
+ this.stateActionMutation(
+ removeState,
+ sprintf(this.$options.i18n.removeSuccessful, { name: this.state.name }),
+ );
}
},
- stateActionMutation(mutation) {
+ stateActionMutation(mutation, successMessage = null) {
let errorMessages = [];
this.$apollo
@@ -143,6 +147,10 @@ export default {
data?.terraformStateLock?.errors ||
data?.terraformStateUnlock?.errors ||
[];
+
+ if (errorMessages.length === 0 && successMessage) {
+ this.$toast.show(successMessage);
+ }
})
.catch(() => {
errorMessages = [this.$options.i18n.errorUpdate];
diff --git a/app/assets/javascripts/terraform/index.js b/app/assets/javascripts/terraform/index.js
index f0a924d8a58..2288bab2fa8 100644
--- a/app/assets/javascripts/terraform/index.js
+++ b/app/assets/javascripts/terraform/index.js
@@ -1,10 +1,12 @@
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
+import { GlToast } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import TerraformList from './components/terraform_list.vue';
import resolvers from './graphql/resolvers';
+Vue.use(GlToast);
Vue.use(VueApollo);
export default () => {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
index 99aa8395f3d..d15794c71b1 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
@@ -159,7 +159,7 @@ export default {
<div class="rebase-state-find-class-convention media media-body space-children">
<span
v-if="rebaseInProgress || isMakingRequest"
- class="gl-font-weight-bold gl-ml-0!"
+ class="gl-font-weight-bold"
data-testid="rebase-message"
>{{ __('Rebase in progress') }}</span
>
diff --git a/app/assets/javascripts/vue_shared/components/toggle_button.vue b/app/assets/javascripts/vue_shared/components/toggle_button.vue
deleted file mode 100644
index 861661d3519..00000000000
--- a/app/assets/javascripts/vue_shared/components/toggle_button.vue
+++ /dev/null
@@ -1,88 +0,0 @@
-<script>
-import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
-import { s__ } from '../../locale';
-
-const ICON_ON = 'status_success_borderless';
-const ICON_OFF = 'status_failed_borderless';
-const LABEL_ON = s__('ToggleButton|Toggle Status: ON');
-const LABEL_OFF = s__('ToggleButton|Toggle Status: OFF');
-
-export default {
- components: {
- GlIcon,
- GlLoadingIcon,
- },
-
- model: {
- prop: 'value',
- event: 'change',
- },
-
- props: {
- name: {
- type: String,
- required: false,
- default: null,
- },
- value: {
- type: Boolean,
- required: false,
- default: null,
- },
- disabledInput: {
- type: Boolean,
- required: false,
- default: false,
- },
- isLoading: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
-
- computed: {
- toggleIcon() {
- return this.value ? ICON_ON : ICON_OFF;
- },
- ariaLabel() {
- return this.value ? LABEL_ON : LABEL_OFF;
- },
- },
-
- methods: {
- toggleFeature() {
- if (!this.disabledInput) this.$emit('change', !this.value);
- },
- },
-};
-</script>
-
-<template>
- <label class="gl-mt-2">
- <input v-if="name" :name="name" :value="value" type="hidden" />
- <button
- type="button"
- role="switch"
- class="project-feature-toggle"
- :aria-label="ariaLabel"
- :aria-checked="value"
- :class="{
- 'is-checked': value,
- 'gl-blue-500': value,
- 'is-disabled': disabledInput,
- 'is-loading': isLoading,
- }"
- @click.prevent="toggleFeature"
- >
- <gl-loading-icon class="loading-icon" />
- <span class="toggle-icon">
- <gl-icon
- :size="18"
- :name="toggleIcon"
- :class="value ? 'gl-text-blue-500' : 'gl-text-gray-400'"
- />
- </span>
- </button>
- </label>
-</template>
diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb
index 17450da6e68..bedd6891d02 100644
--- a/app/finders/labels_finder.rb
+++ b/app/finders/labels_finder.rb
@@ -177,7 +177,7 @@ class LabelsFinder < UnionFinder
end
if group?
- @projects = if params[:include_subgroups]
+ @projects = if params[:include_descendant_groups]
@projects.in_namespace(group.self_and_descendants.select(:id))
else
@projects.in_namespace(group.id)
diff --git a/app/graphql/resolvers/group_labels_resolver.rb b/app/graphql/resolvers/group_labels_resolver.rb
new file mode 100644
index 00000000000..5c2f950bbc0
--- /dev/null
+++ b/app/graphql/resolvers/group_labels_resolver.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class GroupLabelsResolver < LabelsResolver
+ type Types::LabelType.connection_type, null: true
+
+ argument :include_descendant_groups, GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: 'Include labels from descendant groups.',
+ default_value: false
+
+ argument :only_group_labels, GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: 'Include only group level labels.',
+ default_value: false
+ end
+end
diff --git a/app/graphql/resolvers/labels_resolver.rb b/app/graphql/resolvers/labels_resolver.rb
new file mode 100644
index 00000000000..1b523b8a240
--- /dev/null
+++ b/app/graphql/resolvers/labels_resolver.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class LabelsResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ authorize :read_label
+
+ type Types::LabelType.connection_type, null: true
+
+ argument :search_term, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'A search term to find labels with.'
+
+ argument :include_ancestor_groups, GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: 'Include labels from ancestor groups.',
+ default_value: false
+
+ def resolve(**args)
+ return Label.none if parent.nil?
+
+ authorize!(parent)
+
+ # LabelsFinder uses `search` param, so we transform `search_term` into `search`
+ args[:search] = args.delete(:search_term)
+ LabelsFinder.new(current_user, parent_param.merge(args)).execute
+ end
+
+ private
+
+ def parent
+ object.respond_to?(:sync) ? object.sync : object
+ end
+
+ def parent_param
+ key = case parent
+ when Group then :group
+ when Project then :project
+ else raise "Unexpected parent type: #{parent.class}"
+ end
+
+ { "#{key}": parent }
+ end
+ end
+end
diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb
index 42391ec1d98..0aaeb8d20df 100644
--- a/app/graphql/types/group_type.rb
+++ b/app/graphql/types/group_type.rb
@@ -107,17 +107,8 @@ module Types
field :labels,
Types::LabelType.connection_type,
null: true,
- description: 'Labels available on this group.' do
- argument :search_term, GraphQL::STRING_TYPE,
- required: false,
- description: 'A search term to find labels with.'
- end
-
- def labels(search_term: nil)
- LabelsFinder
- .new(current_user, group: group, search: search_term)
- .execute
- end
+ description: 'Labels available on this group.',
+ resolver: Resolvers::GroupLabelsResolver
def avatar_url
object.avatar_url(only_path: false)
diff --git a/app/graphql/types/packages/package_type_enum.rb b/app/graphql/types/packages/package_type_enum.rb
index 9713c9d49b1..e2b5cf3163e 100644
--- a/app/graphql/types/packages/package_type_enum.rb
+++ b/app/graphql/types/packages/package_type_enum.rb
@@ -5,7 +5,7 @@ module Types
class PackageTypeEnum < BaseEnum
PACKAGE_TYPE_NAMES = {
pypi: 'PyPI',
- npm: 'NPM'
+ npm: 'npm'
}.freeze
::Packages::Package.package_types.keys.each do |package_type|
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 9ef451cbe3c..7205c615271 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -337,17 +337,8 @@ module Types
field :labels,
Types::LabelType.connection_type,
null: true,
- description: 'Labels available on this project.' do
- argument :search_term, GraphQL::STRING_TYPE,
- required: false,
- description: 'A search term to find labels with.'
- end
-
- def labels(search_term: nil)
- LabelsFinder
- .new(current_user, project: project, search: search_term)
- .execute
- end
+ description: 'Labels available on this project.',
+ resolver: Resolvers::LabelsResolver
def avatar_url
object.avatar_url(only_path: false)
diff --git a/app/models/concerns/enums/ci/pipeline.rb b/app/models/concerns/enums/ci/pipeline.rb
index e1cf579eefc..f8314d8b429 100644
--- a/app/models/concerns/enums/ci/pipeline.rb
+++ b/app/models/concerns/enums/ci/pipeline.rb
@@ -74,7 +74,8 @@ module Enums
remote_source: 4,
external_project_source: 5,
bridge_source: 6,
- parameter_source: 7
+ parameter_source: 7,
+ compliance_source: 8
}
end
end
diff --git a/changelogs/unreleased/292713-remove-loading-tf.yml b/changelogs/unreleased/292713-remove-loading-tf.yml
new file mode 100644
index 00000000000..e5f11a2cad6
--- /dev/null
+++ b/changelogs/unreleased/292713-remove-loading-tf.yml
@@ -0,0 +1,5 @@
+---
+title: Display loading when removing Terraform state
+merge_request: 53897
+author:
+type: changed
diff --git a/changelogs/unreleased/add-labels-resolver.yml b/changelogs/unreleased/add-labels-resolver.yml
new file mode 100644
index 00000000000..1594c35de19
--- /dev/null
+++ b/changelogs/unreleased/add-labels-resolver.yml
@@ -0,0 +1,6 @@
+---
+title: Adds only_group_labels and include_ancestor_labels and include_descendant_groups
+ arguments to the project and group labels resolvers respectively
+merge_request: 53639
+author:
+type: fixed
diff --git a/changelogs/unreleased/count-implicit-auto-devops-inclusions-separately.yml b/changelogs/unreleased/count-implicit-auto-devops-inclusions-separately.yml
new file mode 100644
index 00000000000..cbf81e90e8e
--- /dev/null
+++ b/changelogs/unreleased/count-implicit-auto-devops-inclusions-separately.yml
@@ -0,0 +1,5 @@
+---
+title: Track YAML-less Auto DevOps inclusions separately
+merge_request: 53383
+author:
+type: changed
diff --git a/changelogs/unreleased/vs-fix-alignment-rebase-in-progress.yml b/changelogs/unreleased/vs-fix-alignment-rebase-in-progress.yml
new file mode 100644
index 00000000000..ed2989dfcb0
--- /dev/null
+++ b/changelogs/unreleased/vs-fix-alignment-rebase-in-progress.yml
@@ -0,0 +1,5 @@
+---
+title: Fix alignment of 'Rebase in progress' label
+merge_request: 54189
+author:
+type: fixed
diff --git a/config/feature_flags/development/ci_mini_pipeline_gl_dropdown.yml b/config/feature_flags/development/ci_mini_pipeline_gl_dropdown.yml
index fccebf552a9..50e1e6715df 100644
--- a/config/feature_flags/development/ci_mini_pipeline_gl_dropdown.yml
+++ b/config/feature_flags/development/ci_mini_pipeline_gl_dropdown.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/300400
milestone: '13.9'
type: development
group: group::continuous integration
-default_enabled: false
+default_enabled: true
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index 6ffe440e7df..c863864f75b 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -3973,7 +3973,7 @@ type ComplianceFramework {
"""
Full path of the compliance pipeline configuration stored in a project
- repository, such as `.gitlab/compliance/soc2/.gitlab-ci.yml`.
+ repository, such as `.gitlab/.compliance-gitlab-ci.yml@compliance/hippa`.
"""
pipelineConfigurationFullPath: String
}
@@ -4031,7 +4031,7 @@ input ComplianceFrameworkInput {
"""
Full path of the compliance pipeline configuration stored in a project
- repository, such as `.gitlab/compliance/soc2/.gitlab-ci.yml`.
+ repository, such as `.gitlab/.compliance-gitlab-ci.yml@compliance/hippa`.
"""
pipelineConfigurationFullPath: String
}
@@ -11620,11 +11620,26 @@ type Group {
first: Int
"""
+ Include labels from ancestor groups.
+ """
+ includeAncestorGroups: Boolean = false
+
+ """
+ Include labels from descendant groups.
+ """
+ includeDescendantGroups: Boolean = false
+
+ """
Returns the last _n_ elements from the list.
"""
last: Int
"""
+ Include only group level labels.
+ """
+ onlyGroupLabels: Boolean = false
+
+ """
A search term to find labels with.
"""
searchTerm: String
@@ -18179,7 +18194,7 @@ enum PackageTypeEnum {
MAVEN
"""
- Packages from the NPM package manager
+ Packages from the npm package manager
"""
NPM
@@ -18383,7 +18398,7 @@ type Pipeline {
"""
Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE,
AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE,
- BRIDGE_SOURCE, PARAMETER_SOURCE)
+ BRIDGE_SOURCE, PARAMETER_SOURCE, COMPLIANCE_SOURCE)
"""
configSource: PipelineConfigSourceEnum
@@ -18654,6 +18669,7 @@ type PipelineCancelPayload {
enum PipelineConfigSourceEnum {
AUTO_DEVOPS_SOURCE
BRIDGE_SOURCE
+ COMPLIANCE_SOURCE
EXTERNAL_PROJECT_SOURCE
PARAMETER_SOURCE
REMOTE_SOURCE
@@ -19834,6 +19850,11 @@ type Project {
first: Int
"""
+ Include labels from ancestor groups.
+ """
+ includeAncestorGroups: Boolean = false
+
+ """
Returns the last _n_ elements from the list.
"""
last: Int
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 379bc2349a8..9fcc113b288 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -10821,7 +10821,7 @@
},
{
"name": "pipelineConfigurationFullPath",
- "description": "Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/compliance/soc2/.gitlab-ci.yml`.",
+ "description": "Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/.compliance-gitlab-ci.yml@compliance/hippa`.",
"args": [
],
@@ -10991,7 +10991,7 @@
},
{
"name": "pipelineConfigurationFullPath",
- "description": "Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/compliance/soc2/.gitlab-ci.yml`.",
+ "description": "Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/.compliance-gitlab-ci.yml@compliance/hippa`.",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -31573,6 +31573,46 @@
"description": "Labels available on this group.",
"args": [
{
+ "name": "searchTerm",
+ "description": "A search term to find labels with.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "includeAncestorGroups",
+ "description": "Include labels from ancestor groups.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ },
+ "defaultValue": "false"
+ },
+ {
+ "name": "includeDescendantGroups",
+ "description": "Include labels from descendant groups.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ },
+ "defaultValue": "false"
+ },
+ {
+ "name": "onlyGroupLabels",
+ "description": "Include only group level labels.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ },
+ "defaultValue": "false"
+ },
+ {
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
@@ -31611,16 +31651,6 @@
"ofType": null
},
"defaultValue": null
- },
- {
- "name": "searchTerm",
- "description": "A search term to find labels with.",
- "type": {
- "kind": "SCALAR",
- "name": "String",
- "ofType": null
- },
- "defaultValue": null
}
],
"type": {
@@ -53370,7 +53400,7 @@
},
{
"name": "NPM",
- "description": "Packages from the NPM package manager",
+ "description": "Packages from the npm package manager",
"isDeprecated": false,
"deprecationReason": null
},
@@ -53950,7 +53980,7 @@
},
{
"name": "configSource",
- "description": "Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE)",
+ "description": "Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE, COMPLIANCE_SOURCE)",
"args": [
],
@@ -54847,6 +54877,12 @@
"description": null,
"isDeprecated": false,
"deprecationReason": null
+ },
+ {
+ "name": "COMPLIANCE_SOURCE",
+ "description": null,
+ "isDeprecated": false,
+ "deprecationReason": null
}
],
"possibleTypes": null
@@ -57712,6 +57748,26 @@
"description": "Labels available on this project.",
"args": [
{
+ "name": "searchTerm",
+ "description": "A search term to find labels with.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "includeAncestorGroups",
+ "description": "Include labels from ancestor groups.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Boolean",
+ "ofType": null
+ },
+ "defaultValue": "false"
+ },
+ {
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
@@ -57750,16 +57806,6 @@
"ofType": null
},
"defaultValue": null
- },
- {
- "name": "searchTerm",
- "description": "A search term to find labels with.",
- "type": {
- "kind": "SCALAR",
- "name": "String",
- "ofType": null
- },
- "defaultValue": null
}
],
"type": {
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 472cb202ea2..c1fe265a821 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -630,7 +630,7 @@ Represents a ComplianceFramework associated with a Project.
| `description` | String! | Description of the compliance framework. |
| `id` | ID! | Compliance framework ID. |
| `name` | String! | Name of the compliance framework. |
-| `pipelineConfigurationFullPath` | String | Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/compliance/soc2/.gitlab-ci.yml`. |
+| `pipelineConfigurationFullPath` | String | Full path of the compliance pipeline configuration stored in a project repository, such as `.gitlab/.compliance-gitlab-ci.yml@compliance/hippa`. |
### ComposerMetadata
@@ -2785,7 +2785,7 @@ Information about pagination in a connection..
| `beforeSha` | String | Base SHA of the source branch. |
| `cancelable` | Boolean! | Specifies if a pipeline can be canceled. |
| `committedAt` | Time | Timestamp of the pipeline's commit. |
-| `configSource` | PipelineConfigSourceEnum | Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE) |
+| `configSource` | PipelineConfigSourceEnum | Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE, COMPLIANCE_SOURCE) |
| `coverage` | Float | Coverage percentage. |
| `createdAt` | Time! | Timestamp of the pipeline's creation. |
| `detailedStatus` | DetailedStatus! | Detailed status of the pipeline. |
@@ -5203,7 +5203,7 @@ Rotation length unit of an on-call rotation.
| `GENERIC` | Packages from the Generic package manager |
| `GOLANG` | Packages from the Golang package manager |
| `MAVEN` | Packages from the Maven package manager |
-| `NPM` | Packages from the NPM package manager |
+| `NPM` | Packages from the npm package manager |
| `NUGET` | Packages from the Nuget package manager |
| `PYPI` | Packages from the PyPI package manager |
| `RUBYGEMS` | Packages from the Rubygems package manager |
@@ -5214,6 +5214,7 @@ Rotation length unit of an on-call rotation.
| ----- | ----------- |
| `AUTO_DEVOPS_SOURCE` | |
| `BRIDGE_SOURCE` | |
+| `COMPLIANCE_SOURCE` | |
| `EXTERNAL_PROJECT_SOURCE` | |
| `PARAMETER_SOURCE` | |
| `REMOTE_SOURCE` | |
diff --git a/doc/user/application_security/sast/analyzers.md b/doc/user/application_security/sast/analyzers.md
index b43fb9a77df..83f85951388 100644
--- a/doc/user/application_security/sast/analyzers.md
+++ b/doc/user/application_security/sast/analyzers.md
@@ -154,24 +154,24 @@ The [Security Scanner Integration](../../../development/integrations/secure.md)
## Analyzers Data
-| Property / Tool | Apex | Bandit | Brakeman | ESLint security | SpotBugs | Flawfinder | Gosec | Kubesec Scanner | MobSF | NodeJsScan | PHP CS Security Audit | Security code Scan (.NET) | Semgrep | Sobelow |
-| --------------------------------------- | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :---------------------: | :-------------------------: | :-------------------------: | :----------------: |
-| Severity | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ⚠ | ✗ |
-| Title | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
-| Description | ✓ | ✗ | ✗ | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ |
-| File | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
-| Start line | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
-| End line | ✓ | ✓ | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
-| Start column | ✓ | ✗ | ✗ | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | ✓ | ✓ | ✓ | ✗ |
-| End column | ✓ | ✗ | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
-| External ID (for example, CVE) | ✗ | ✗ | ⚠ | ✗ | ⚠ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ⚠ | ✗ |
-| URLs | ✓ | ✗ | ✓ | ✗ | ⚠ | ✗ | ⚠ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
-| Internal doc/explanation | ✓ | ⚠ | ✓ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ |
-| Solution | ✓ | ✗ | ✗ | ✗ | ⚠ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ⚠ | ✗ |
-| Affected item (for example, class or package) | ✓ | ✗ | ✓ | ✗ | ✓ | ✓ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
-| Confidence | ✗ | ✓ | ✓ | ✗ | ✓ | x | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ⚠ | ✓ |
-| Source code extract | ✗ | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
-| Internal ID | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | ✓ | ✓ | ✓ | ✓ |
+| Property / Tool | Apex | Bandit | Brakeman | ESLint security | SpotBugs | Flawfinder | Gosec | Kubesec Scanner | MobSF | NodeJsScan | PHP CS Security Audit | Security code Scan (.NET) | Semgrep | Sobelow |
+|--------------------------------|------|--------|----------|-----------------|----------|------------|-------|-----------------|-------|------------|-----------------------|---------------------------|---------|---------|
+| Affected item (for example, class or package) | ✓ | ✗ | ✓ | ✗ | ✓ | ✓ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
+| Confidence | ✗ | ✓ | ✓ | ✗ | ✓ | x | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ⚠ | ✓ |
+| Description | ✓ | ✗ | ✗ | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ |
+| End column | ✓ | ✗ | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
+| End line | ✓ | ✓ | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
+| External ID (for example, CVE) | ✗ | ✗ | ⚠ | ✗ | ⚠ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ⚠ | ✗ |
+| File | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Internal doc/explanation | ✓ | ⚠ | ✓ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ |
+| Internal ID | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | ✓ | ✓ | ✓ | ✓ |
+| Severity | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ⚠ | ✗ |
+| Solution | ✓ | ✗ | ✗ | ✗ | ⚠ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ⚠ | ✗ |
+| Source code extract | ✗ | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
+| Start column | ✓ | ✗ | ✗ | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | ✓ | ✓ | ✓ | ✗ |
+| Start line | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Title | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
+| URLs | ✓ | ✗ | ✓ | ✗ | ⚠ | ✗ | ⚠ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
- ✓ => we have that data
- âš  => we have that data but it's partially reliable, or we need to extract it from unstructured content
diff --git a/doc/user/compliance/license_compliance/index.md b/doc/user/compliance/license_compliance/index.md
index 2c9a32607e1..74c608fcc38 100644
--- a/doc/user/compliance/license_compliance/index.md
+++ b/doc/user/compliance/license_compliance/index.md
@@ -657,15 +657,16 @@ license_scanning:
The License Compliance job should now use local copies of the License Compliance analyzers to scan
your code and generate security reports, without requiring internet access.
-Additional configuration may be needed for connecting to
-[private Bower registries](#using-private-bower-registries),
-[private Bundler registries](#using-private-bundler-registries),
-[private Conan registries](#using-private-bower-registries),
-[private Go registries](#using-private-go-registries),
-[private Maven repositories](#using-private-maven-repositories),
-[private npm registries](#using-private-npm-registries),
-[private Python repositories](#using-private-python-repositories),
-and [private Yarn registries](#using-private-yarn-registries).
+Additional configuration may be needed for connecting to private registries for:
+
+- [Bower](#using-private-bower-registries),
+- [Bundler](#using-private-bundler-registries),
+- [Conan](#using-private-bower-registries),
+- [Go](#using-private-go-registries),
+- [Maven repositories](#using-private-maven-repositories),
+- [npm](#using-private-npm-registries),
+- [Python repositories](#using-private-python-repositories),
+- [Yarn](#using-private-yarn-registries).
### SPDX license list name matching
diff --git a/lib/gitlab/ci/pipeline/chain/config/content.rb b/lib/gitlab/ci/pipeline/chain/config/content.rb
index 5314fd471c3..a7680f6e593 100644
--- a/lib/gitlab/ci/pipeline/chain/config/content.rb
+++ b/lib/gitlab/ci/pipeline/chain/config/content.rb
@@ -34,16 +34,22 @@ module Gitlab
private
def find_config
- SOURCES.each do |source|
+ sources.each do |source|
config = source.new(@pipeline, @command)
return config if config.exists?
end
nil
end
+
+ def sources
+ SOURCES
+ end
end
end
end
end
end
end
+
+Gitlab::Ci::Pipeline::Chain::Config::Content.prepend_if_ee('EE::Gitlab::Ci::Pipeline::Chain::Config::Content')
diff --git a/lib/gitlab/ci/pipeline/chain/template_usage.rb b/lib/gitlab/ci/pipeline/chain/template_usage.rb
index c1a7b4ed453..2fcf1740b5f 100644
--- a/lib/gitlab/ci/pipeline/chain/template_usage.rb
+++ b/lib/gitlab/ci/pipeline/chain/template_usage.rb
@@ -19,7 +19,7 @@ module Gitlab
def track_event(template)
Gitlab::UsageDataCounters::CiTemplateUniqueCounter
- .track_unique_project_event(project_id: pipeline.project_id, template: template)
+ .track_unique_project_event(project_id: pipeline.project_id, template: template, config_source: pipeline.config_source)
end
def included_templates
diff --git a/lib/gitlab/url_blocker.rb b/lib/gitlab/url_blocker.rb
index 10822f943b6..5403017d710 100644
--- a/lib/gitlab/url_blocker.rb
+++ b/lib/gitlab/url_blocker.rb
@@ -7,6 +7,9 @@ module Gitlab
class UrlBlocker
BlockedUrlError = Class.new(StandardError)
+ GETADDRINFO_TIMEOUT_SECONDS = 15
+ private_constant :GETADDRINFO_TIMEOUT_SECONDS
+
class << self
# Validates the given url according to the constraints specified by arguments.
#
@@ -110,7 +113,7 @@ module Gitlab
end
def get_address_info(uri, dns_rebind_protection)
- Addrinfo.getaddrinfo(uri.hostname, get_port(uri), nil, :STREAM).map do |addr|
+ Addrinfo.getaddrinfo(uri.hostname, get_port(uri), nil, :STREAM, timeout: GETADDRINFO_TIMEOUT_SECONDS).map do |addr|
addr.ipv6_v4mapped? ? addr.ipv6_to_ipv4 : addr
end
rescue SocketError
diff --git a/lib/gitlab/usage_data_counters/ci_template_unique_counter.rb b/lib/gitlab/usage_data_counters/ci_template_unique_counter.rb
index 933e358509b..772a4623280 100644
--- a/lib/gitlab/usage_data_counters/ci_template_unique_counter.rb
+++ b/lib/gitlab/usage_data_counters/ci_template_unique_counter.rb
@@ -4,6 +4,7 @@ module Gitlab::UsageDataCounters
class CiTemplateUniqueCounter
REDIS_SLOT = 'ci_templates'.freeze
+ # NOTE: Events originating from implicit Auto DevOps pipelines get prefixed with `implicit_`
TEMPLATE_TO_EVENT = {
'5-Minute-Production-App.gitlab-ci.yml' => '5_min_production_app',
'Auto-DevOps.gitlab-ci.yml' => 'auto_devops',
@@ -18,19 +19,21 @@ module Gitlab::UsageDataCounters
}.freeze
class << self
- def track_unique_project_event(project_id:, template:)
+ def track_unique_project_event(project_id:, template:, config_source:)
return if Feature.disabled?(:usage_data_track_ci_templates_unique_projects, default_enabled: :yaml)
- if event = unique_project_event(template)
+ if event = unique_project_event(template, config_source)
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(event, values: project_id)
end
end
private
- def unique_project_event(template)
+ def unique_project_event(template, config_source)
if name = TEMPLATE_TO_EVENT[template]
- "p_#{REDIS_SLOT}_#{name}"
+ prefix = 'implicit_' if config_source.to_s == 'auto_devops_source'
+
+ "p_#{REDIS_SLOT}_#{prefix}#{name}"
end
end
end
diff --git a/lib/gitlab/usage_data_counters/known_events/ci_templates.yml b/lib/gitlab/usage_data_counters/known_events/ci_templates.yml
new file mode 100644
index 00000000000..9c19c9e8b8c
--- /dev/null
+++ b/lib/gitlab/usage_data_counters/known_events/ci_templates.yml
@@ -0,0 +1,91 @@
+# Implicit Auto DevOps pipeline events
+- name: p_ci_templates_implicit_auto_devops
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
+ feature_flag: usage_data_track_ci_templates_unique_projects
+
+- name: p_ci_templates_implicit_auto_devops_build
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
+ feature_flag: usage_data_track_ci_templates_unique_projects
+
+- name: p_ci_templates_implicit_auto_devops_deploy
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
+ feature_flag: usage_data_track_ci_templates_unique_projects
+
+- name: p_ci_templates_implicit_security_sast
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
+ feature_flag: usage_data_track_ci_templates_unique_projects
+
+- name: p_ci_templates_implicit_security_secret_detection
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
+ feature_flag: usage_data_track_ci_templates_unique_projects
+
+# Explicit include:template pipeline events
+- name: p_ci_templates_5_min_production_app
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
+ feature_flag: usage_data_track_ci_templates_unique_projects
+
+- name: p_ci_templates_auto_devops
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
+ feature_flag: usage_data_track_ci_templates_unique_projects
+
+- name: p_ci_templates_aws_cf_deploy_ec2
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
+ feature_flag: usage_data_track_ci_templates_unique_projects
+
+- name: p_ci_templates_aws_deploy_ecs
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
+ feature_flag: usage_data_track_ci_templates_unique_projects
+
+- name: p_ci_templates_auto_devops_build
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
+ feature_flag: usage_data_track_ci_templates_unique_projects
+
+- name: p_ci_templates_auto_devops_deploy
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
+ feature_flag: usage_data_track_ci_templates_unique_projects
+
+- name: p_ci_templates_auto_devops_deploy_latest
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
+ feature_flag: usage_data_track_ci_templates_unique_projects
+
+- name: p_ci_templates_security_sast
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
+ feature_flag: usage_data_track_ci_templates_unique_projects
+
+- name: p_ci_templates_security_secret_detection
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
+ feature_flag: usage_data_track_ci_templates_unique_projects
+
+- name: p_ci_templates_terraform_base_latest
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
+ feature_flag: usage_data_track_ci_templates_unique_projects
diff --git a/lib/gitlab/usage_data_counters/known_events/common.yml b/lib/gitlab/usage_data_counters/known_events/common.yml
index a877e0dc042..4fe4aedc8c9 100644
--- a/lib/gitlab/usage_data_counters/known_events/common.yml
+++ b/lib/gitlab/usage_data_counters/known_events/common.yml
@@ -466,57 +466,6 @@
redis_slot: terraform
aggregation: weekly
feature_flag: usage_data_p_terraform_state_api_unique_users
-# CI templates
-- name: p_ci_templates_5_min_production_app
- category: ci_templates
- redis_slot: ci_templates
- aggregation: weekly
- feature_flag: usage_data_track_ci_templates_unique_projects
-- name: p_ci_templates_auto_devops
- category: ci_templates
- redis_slot: ci_templates
- aggregation: weekly
- feature_flag: usage_data_track_ci_templates_unique_projects
-- name: p_ci_templates_aws_cf_deploy_ec2
- category: ci_templates
- redis_slot: ci_templates
- aggregation: weekly
- feature_flag: usage_data_track_ci_templates_unique_projects
-- name: p_ci_templates_aws_deploy_ecs
- category: ci_templates
- redis_slot: ci_templates
- aggregation: weekly
- feature_flag: usage_data_track_ci_templates_unique_projects
-- name: p_ci_templates_auto_devops_build
- category: ci_templates
- redis_slot: ci_templates
- aggregation: weekly
- feature_flag: usage_data_track_ci_templates_unique_projects
-- name: p_ci_templates_auto_devops_deploy
- category: ci_templates
- redis_slot: ci_templates
- aggregation: weekly
- feature_flag: usage_data_track_ci_templates_unique_projects
-- name: p_ci_templates_auto_devops_deploy_latest
- category: ci_templates
- redis_slot: ci_templates
- aggregation: weekly
- feature_flag: usage_data_track_ci_templates_unique_projects
-- name: p_ci_templates_security_sast
- category: ci_templates
- redis_slot: ci_templates
- aggregation: weekly
- feature_flag: usage_data_track_ci_templates_unique_projects
-- name: p_ci_templates_security_secret_detection
- category: ci_templates
- redis_slot: ci_templates
- aggregation: weekly
- feature_flag: usage_data_track_ci_templates_unique_projects
-- name: p_ci_templates_terraform_base_latest
- category: ci_templates
- redis_slot: ci_templates
- aggregation: weekly
- feature_flag: usage_data_track_ci_templates_unique_projects
# Pipeline Authoring
- name: o_pipeline_authoring_unique_users_committing_ciconfigfile
category: pipeline_authoring
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index aaea0271112..39cfe546066 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -28728,6 +28728,9 @@ msgstr ""
msgid "Terraform"
msgstr ""
+msgid "Terraform|%{name} successfully removed"
+msgstr ""
+
msgid "Terraform|%{number} Terraform report failed to generate"
msgid_plural "Terraform|%{number} Terraform reports failed to generate"
msgstr[0] ""
@@ -28804,6 +28807,9 @@ msgstr ""
msgid "Terraform|Remove state file and versions"
msgstr ""
+msgid "Terraform|Removing"
+msgstr ""
+
msgid "Terraform|Reported Resource Changes: %{addNum} to add, %{changeNum} to change, %{deleteNum} to delete"
msgstr ""
@@ -30857,12 +30863,6 @@ msgstr ""
msgid "Toggle thread"
msgstr ""
-msgid "ToggleButton|Toggle Status: OFF"
-msgstr ""
-
-msgid "ToggleButton|Toggle Status: ON"
-msgstr ""
-
msgid "Toggled :%{name}: emoji award."
msgstr ""
diff --git a/spec/features/projects/terraform_spec.rb b/spec/features/projects/terraform_spec.rb
index dfa4dad8490..55b906c2bc5 100644
--- a/spec/features/projects/terraform_spec.rb
+++ b/spec/features/projects/terraform_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe 'Terraform', :js do
fill_in "terraform-state-remove-input-#{additional_state.name}", with: additional_state.name
click_button 'Remove'
- expect(page).not_to have_content(additional_state.name)
+ expect(page).to have_content("#{additional_state.name} successfully removed")
expect { additional_state.reload }.to raise_error ActiveRecord::RecordNotFound
end
end
diff --git a/spec/frontend/issuable_list/components/issuable_item_spec.js b/spec/frontend/issuable_list/components/issuable_item_spec.js
index d6e6816086e..987acf559e3 100644
--- a/spec/frontend/issuable_list/components/issuable_item_spec.js
+++ b/spec/frontend/issuable_list/components/issuable_item_spec.js
@@ -18,6 +18,8 @@ const createComponent = ({ issuableSymbol = '#', issuable = mockIssuable, slots
slots,
});
+const MOCK_GITLAB_URL = 'http://0.0.0.0:3000';
+
describe('IssuableItem', () => {
// The mock data is dependent that this is after our default date
useFakeDate(2020, 11, 11);
@@ -28,7 +30,7 @@ describe('IssuableItem', () => {
let wrapper;
beforeEach(() => {
- gon.gitlab_url = 'http://0.0.0.0:3000';
+ gon.gitlab_url = MOCK_GITLAB_URL;
wrapper = createComponent();
});
@@ -73,11 +75,11 @@ describe('IssuableItem', () => {
describe('isIssuableUrlExternal', () => {
it.each`
- issuableWebUrl | urlType | returnValue
- ${'/gitlab-org/gitlab-test/-/issues/2'} | ${'relative'} | ${false}
- ${'http://0.0.0.0:3000/gitlab-org/gitlab-test/-/issues/1'} | ${'absolute and internal'} | ${false}
- ${'http://jira.atlassian.net/browse/IG-1'} | ${'external'} | ${true}
- ${'https://github.com/gitlabhq/gitlabhq/issues/1'} | ${'external'} | ${true}
+ issuableWebUrl | urlType | returnValue
+ ${'/gitlab-org/gitlab-test/-/issues/2'} | ${'relative'} | ${false}
+ ${`${MOCK_GITLAB_URL}/gitlab-org/gitlab-test/-/issues/1`} | ${'absolute and internal'} | ${false}
+ ${'http://jira.atlassian.net/browse/IG-1'} | ${'external'} | ${true}
+ ${'https://github.com/gitlabhq/gitlabhq/issues/1'} | ${'external'} | ${true}
`(
'returns $returnValue when `issuable.webUrl` is $urlType',
async ({ issuableWebUrl, returnValue }) => {
@@ -217,14 +219,32 @@ describe('IssuableItem', () => {
});
describe('template', () => {
- it('renders issuable title', () => {
- const titleEl = wrapper.find('[data-testid="issuable-title"]');
+ it.each`
+ gitlabWebUrl | webUrl | expectedHref | expectedTarget
+ ${undefined} | ${`${MOCK_GITLAB_URL}/issue`} | ${`${MOCK_GITLAB_URL}/issue`} | ${undefined}
+ ${undefined} | ${'https://jira.com/issue'} | ${'https://jira.com/issue'} | ${'_blank'}
+ ${'/gitlab-org/issue'} | ${'https://jira.com/issue'} | ${'/gitlab-org/issue'} | ${undefined}
+ `(
+ 'renders issuable title correctly when `gitlabWebUrl` is `$gitlabWebUrl` and webUrl is `$webUrl`',
+ async ({ webUrl, gitlabWebUrl, expectedHref, expectedTarget }) => {
+ wrapper.setProps({
+ issuable: {
+ ...mockIssuable,
+ webUrl,
+ gitlabWebUrl,
+ },
+ });
- expect(titleEl.exists()).toBe(true);
- expect(titleEl.find(GlLink).attributes('href')).toBe(mockIssuable.webUrl);
- expect(titleEl.find(GlLink).attributes('target')).not.toBeDefined();
- expect(titleEl.find(GlLink).text()).toBe(mockIssuable.title);
- });
+ await wrapper.vm.$nextTick();
+
+ const titleEl = wrapper.find('[data-testid="issuable-title"]');
+
+ expect(titleEl.exists()).toBe(true);
+ expect(titleEl.find(GlLink).attributes('href')).toBe(expectedHref);
+ expect(titleEl.find(GlLink).attributes('target')).toBe(expectedTarget);
+ expect(titleEl.find(GlLink).text()).toBe(mockIssuable.title);
+ },
+ );
it('renders checkbox when `showCheckbox` prop is true', async () => {
wrapper.setProps({
diff --git a/spec/frontend/terraform/components/states_table_actions_spec.js b/spec/frontend/terraform/components/states_table_actions_spec.js
index 7449ddd3d2a..61f6e9f0f7b 100644
--- a/spec/frontend/terraform/components/states_table_actions_spec.js
+++ b/spec/frontend/terraform/components/states_table_actions_spec.js
@@ -14,6 +14,7 @@ localVue.use(VueApollo);
describe('StatesTableActions', () => {
let lockResponse;
let removeResponse;
+ let toast;
let unlockResponse;
let updateStateResponse;
let wrapper;
@@ -59,10 +60,13 @@ describe('StatesTableActions', () => {
const createComponent = (propsData = defaultProps) => {
const apolloProvider = createMockApolloProvider();
+ toast = jest.fn();
+
wrapper = shallowMount(StateActions, {
apolloProvider,
localVue,
propsData,
+ mocks: { $toast: { show: toast } },
stubs: { GlDropdown, GlModal, GlSprintf },
});
@@ -83,6 +87,7 @@ describe('StatesTableActions', () => {
afterEach(() => {
lockResponse = null;
removeResponse = null;
+ toast = null;
unlockResponse = null;
updateStateResponse = null;
wrapper.destroy();
@@ -243,7 +248,6 @@ describe('StatesTableActions', () => {
describe('when clicking the remove button', () => {
beforeEach(() => {
findRemoveBtn().vm.$emit('click');
-
return waitForPromises();
});
@@ -254,21 +258,70 @@ describe('StatesTableActions', () => {
});
describe('when submitting the remove modal', () => {
- it('does not call the remove mutation when state name is missing', async () => {
- findRemoveModal().vm.$emit('ok');
- await wrapper.vm.$nextTick();
+ describe('when state name is missing', () => {
+ beforeEach(() => {
+ findRemoveModal().vm.$emit('ok');
+ return waitForPromises();
+ });
- expect(removeResponse).not.toHaveBeenCalledWith();
+ it('does not call the remove mutation', () => {
+ expect(removeResponse).not.toHaveBeenCalledWith();
+ });
});
- it('calls the remove mutation when state name is present', async () => {
- await wrapper.setData({ removeConfirmText: defaultProps.state.name });
+ describe('when state name is present', () => {
+ beforeEach(async () => {
+ await wrapper.setData({ removeConfirmText: defaultProps.state.name });
+
+ findRemoveModal().vm.$emit('ok');
- findRemoveModal().vm.$emit('ok');
- await wrapper.vm.$nextTick();
+ await waitForPromises();
+ });
+
+ it('calls the remove mutation', () => {
+ expect(removeResponse).toHaveBeenCalledWith({ stateID: defaultProps.state.id });
+ });
+
+ it('calls the toast action', () => {
+ expect(toast).toHaveBeenCalledWith(`${defaultProps.state.name} successfully removed`);
+ });
- expect(removeResponse).toHaveBeenCalledWith({
- stateID: defaultProps.state.id,
+ it('calls mutations to set loading and errors', () => {
+ // loading update
+ expect(updateStateResponse).toHaveBeenNthCalledWith(
+ 1,
+ {},
+ {
+ terraformState: {
+ ...defaultProps.state,
+ _showDetails: false,
+ errorMessages: [],
+ loadingLock: false,
+ loadingRemove: true,
+ },
+ },
+ // Apollo fields
+ expect.any(Object),
+ expect.any(Object),
+ );
+
+ // final update
+ expect(updateStateResponse).toHaveBeenNthCalledWith(
+ 2,
+ {},
+ {
+ terraformState: {
+ ...defaultProps.state,
+ _showDetails: false,
+ errorMessages: [],
+ loadingLock: false,
+ loadingRemove: false,
+ },
+ },
+ // Apollo fields
+ expect.any(Object),
+ expect.any(Object),
+ );
});
});
});
diff --git a/spec/frontend/terraform/components/states_table_spec.js b/spec/frontend/terraform/components/states_table_spec.js
index a9aeba202e0..100e577f514 100644
--- a/spec/frontend/terraform/components/states_table_spec.js
+++ b/spec/frontend/terraform/components/states_table_spec.js
@@ -1,4 +1,4 @@
-import { GlIcon, GlTooltip } from '@gitlab/ui';
+import { GlIcon, GlLoadingIcon, GlTooltip } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { useFakeDate } from 'helpers/fake_date';
import StatesTable from '~/terraform/components/states_table.vue';
@@ -92,6 +92,17 @@ describe('StatesTable', () => {
},
},
},
+ {
+ _showDetails: false,
+ errorMessages: [],
+ name: 'state-5',
+ loadingLock: false,
+ loadingRemove: true,
+ lockedAt: null,
+ lockedByUser: null,
+ updatedAt: '2020-10-10T00:00:00Z',
+ latestVersion: null,
+ },
],
};
@@ -112,14 +123,15 @@ describe('StatesTable', () => {
});
it.each`
- name | toolTipText | locked | lineNumber
- ${'state-1'} | ${'Locked by user-1 2 days ago'} | ${true} | ${0}
- ${'state-2'} | ${'Locking state'} | ${false} | ${1}
- ${'state-3'} | ${'Unlocking state'} | ${false} | ${2}
- ${'state-4'} | ${'Locked by Unknown User 5 days ago'} | ${true} | ${3}
+ name | toolTipText | locked | loading | lineNumber
+ ${'state-1'} | ${'Locked by user-1 2 days ago'} | ${true} | ${false} | ${0}
+ ${'state-2'} | ${'Locking state'} | ${false} | ${true} | ${1}
+ ${'state-3'} | ${'Unlocking state'} | ${false} | ${true} | ${2}
+ ${'state-4'} | ${'Locked by Unknown User 5 days ago'} | ${true} | ${false} | ${3}
+ ${'state-5'} | ${'Removing'} | ${false} | ${true} | ${4}
`(
'displays the name and locked information "$name" for line "$lineNumber"',
- ({ name, toolTipText, locked, lineNumber }) => {
+ ({ name, toolTipText, locked, loading, lineNumber }) => {
const states = wrapper.findAll('[data-testid="terraform-states-table-name"]');
const state = states.at(lineNumber);
@@ -127,6 +139,7 @@ describe('StatesTable', () => {
expect(state.text()).toContain(name);
expect(state.find(GlIcon).exists()).toBe(locked);
+ expect(state.find(GlLoadingIcon).exists()).toBe(loading);
expect(toolTip.exists()).toBe(locked);
if (locked) {
diff --git a/spec/frontend/vue_shared/components/toggle_button_spec.js b/spec/frontend/vue_shared/components/toggle_button_spec.js
deleted file mode 100644
index 632e648aadc..00000000000
--- a/spec/frontend/vue_shared/components/toggle_button_spec.js
+++ /dev/null
@@ -1,96 +0,0 @@
-import { GlIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import ToggleButton from '~/vue_shared/components/toggle_button.vue';
-
-describe('Toggle Button component', () => {
- let wrapper;
-
- function createComponent(propsData = {}) {
- wrapper = shallowMount(ToggleButton, {
- propsData,
- });
- }
-
- const findInput = () => wrapper.find('input');
- const findButton = () => wrapper.find('button');
- const findToggleIcon = () => wrapper.find(GlIcon);
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- it('renders input with provided name', () => {
- createComponent({
- name: 'foo',
- });
-
- expect(findInput().attributes('name')).toBe('foo');
- });
-
- describe.each`
- value | iconName
- ${true} | ${'status_success_borderless'}
- ${false} | ${'status_failed_borderless'}
- `('when `value` prop is `$value`', ({ value, iconName }) => {
- beforeEach(() => {
- createComponent({
- value,
- name: 'foo',
- });
- });
-
- it('renders input with correct value attribute', () => {
- expect(findInput().attributes('value')).toBe(`${value}`);
- });
-
- it('renders correct icon', () => {
- const icon = findToggleIcon();
- expect(icon.isVisible()).toBe(true);
- expect(icon.props('name')).toBe(iconName);
- expect(findButton().classes('is-checked')).toBe(value);
- });
-
- describe('when clicked', () => {
- it('emits `change` event with correct event', async () => {
- findButton().trigger('click');
- await wrapper.vm.$nextTick();
-
- expect(wrapper.emitted('change')).toStrictEqual([[!value]]);
- });
- });
- });
-
- describe('when `disabledInput` prop is `true`', () => {
- beforeEach(() => {
- createComponent({
- value: true,
- disabledInput: true,
- });
- });
-
- it('renders disabled button', () => {
- expect(findButton().classes()).toContain('is-disabled');
- });
-
- it('does not emit change event when clicked', async () => {
- findButton().trigger('click');
- await wrapper.vm.$nextTick();
-
- expect(wrapper.emitted('change')).toBeFalsy();
- });
- });
-
- describe('when `isLoading` prop is `true`', () => {
- beforeEach(() => {
- createComponent({
- value: true,
- isLoading: true,
- });
- });
-
- it('renders loading class', () => {
- expect(findButton().classes()).toContain('is-loading');
- });
- });
-});
diff --git a/spec/graphql/resolvers/group_labels_resolver_spec.rb b/spec/graphql/resolvers/group_labels_resolver_spec.rb
new file mode 100644
index 00000000000..ed94f12502a
--- /dev/null
+++ b/spec/graphql/resolvers/group_labels_resolver_spec.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::GroupLabelsResolver do
+ include GraphqlHelpers
+
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:group, reload: true) { create(:group, :private) }
+ let_it_be(:subgroup, reload: true) { create(:group, :private, parent: group) }
+ let_it_be(:sub_subgroup, reload: true) { create(:group, :private, parent: subgroup) }
+ let_it_be(:project, reload: true) { create(:project, :private, group: sub_subgroup) }
+ let_it_be(:label1) { create(:label, project: project, name: 'project feature') }
+ let_it_be(:label2) { create(:label, project: project, name: 'new project feature') }
+ let_it_be(:group_label1) { create(:group_label, group: group, name: 'group feature') }
+ let_it_be(:group_label2) { create(:group_label, group: group, name: 'new group feature') }
+ let_it_be(:subgroup_label1) { create(:group_label, group: subgroup, name: 'subgroup feature') }
+ let_it_be(:subgroup_label2) { create(:group_label, group: subgroup, name: 'new subgroup feature') }
+ let_it_be(:sub_subgroup_label1) { create(:group_label, group: sub_subgroup, name: 'sub_subgroup feature') }
+ let_it_be(:sub_subgroup_label2) { create(:group_label, group: sub_subgroup, name: 'new sub_subgroup feature') }
+
+ specify do
+ expect(described_class).to have_nullable_graphql_type(Types::LabelType.connection_type)
+ end
+
+ describe '#resolve' do
+ context 'with unauthorized user' do
+ it 'raises error' do
+ expect { resolve_labels(subgroup) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'with authorized user' do
+ it 'does not raise error' do
+ group.add_guest(current_user)
+
+ expect { resolve_labels(subgroup) }.not_to raise_error
+ end
+ end
+
+ context 'without parent' do
+ it 'returns no labels' do
+ expect(resolve_labels(nil)).to eq(Label.none)
+ end
+ end
+
+ context 'at group level' do
+ before_all do
+ group.add_developer(current_user)
+ end
+
+ # because :include_ancestor_groups, :include_descendant_groups, :only_group_labels default to false
+ # the `nil` value would be equivalent to passing in `false` so just check for `nil` option
+ where(:include_ancestor_groups, :include_descendant_groups, :only_group_labels, :search_term, :test) do
+ nil | nil | nil | nil | -> { expect(subject).to contain_exactly(subgroup_label1, subgroup_label2) }
+ nil | nil | true | nil | -> { expect(subject).to contain_exactly(subgroup_label1, subgroup_label2) }
+ nil | true | nil | nil | -> { expect(subject).to contain_exactly(subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2, label1, label2) }
+ nil | true | true | nil | -> { expect(subject).to contain_exactly(subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
+ true | nil | nil | nil | -> { expect(subject).to contain_exactly(group_label1, group_label2, subgroup_label1, subgroup_label2) }
+ true | nil | true | nil | -> { expect(subject).to contain_exactly(group_label1, group_label2, subgroup_label1, subgroup_label2) }
+ true | true | nil | nil | -> { expect(subject).to contain_exactly(group_label1, group_label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2, label1, label2) }
+ true | true | true | nil | -> { expect(subject).to contain_exactly(group_label1, group_label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
+
+ nil | nil | nil | 'new' | -> { expect(subject).to contain_exactly(subgroup_label2) }
+ nil | nil | true | 'new' | -> { expect(subject).to contain_exactly(subgroup_label2) }
+ nil | true | nil | 'new' | -> { expect(subject).to contain_exactly(subgroup_label2, sub_subgroup_label2, label2) }
+ nil | true | true | 'new' | -> { expect(subject).to contain_exactly(subgroup_label2, sub_subgroup_label2) }
+ true | nil | nil | 'new' | -> { expect(subject).to contain_exactly(group_label2, subgroup_label2) }
+ true | nil | true | 'new' | -> { expect(subject).to contain_exactly(group_label2, subgroup_label2) }
+ true | true | nil | 'new' | -> { expect(subject).to contain_exactly(group_label2, subgroup_label2, sub_subgroup_label2, label2) }
+ true | true | true | 'new' | -> { expect(subject).to contain_exactly(group_label2, subgroup_label2, sub_subgroup_label2) }
+ end
+
+ with_them do
+ let(:params) do
+ {
+ include_ancestor_groups: include_ancestor_groups,
+ include_descendant_groups: include_descendant_groups,
+ only_group_labels: only_group_labels,
+ search_term: search_term
+ }
+ end
+
+ subject { resolve_labels(subgroup, params) }
+
+ it { self.instance_exec(&test) }
+ end
+ end
+ end
+
+ def resolve_labels(parent, args = {}, context = { current_user: current_user })
+ resolve(described_class, obj: parent, args: args, ctx: context)
+ end
+end
diff --git a/spec/graphql/resolvers/labels_resolver_spec.rb b/spec/graphql/resolvers/labels_resolver_spec.rb
new file mode 100644
index 00000000000..3d027a6c8d5
--- /dev/null
+++ b/spec/graphql/resolvers/labels_resolver_spec.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::LabelsResolver do
+ include GraphqlHelpers
+
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:group, reload: true) { create(:group, :private) }
+ let_it_be(:subgroup, reload: true) { create(:group, :private, parent: group) }
+ let_it_be(:sub_subgroup, reload: true) { create(:group, :private, parent: subgroup) }
+ let_it_be(:project, reload: true) { create(:project, :private, group: subgroup) }
+ let_it_be(:label1) { create(:label, project: project, name: 'project feature') }
+ let_it_be(:label2) { create(:label, project: project, name: 'new project feature') }
+ let_it_be(:group_label1) { create(:group_label, group: group, name: 'group feature') }
+ let_it_be(:group_label2) { create(:group_label, group: group, name: 'new group feature') }
+ let_it_be(:subgroup_label1) { create(:group_label, group: subgroup, name: 'subgroup feature') }
+ let_it_be(:subgroup_label2) { create(:group_label, group: subgroup, name: 'new subgroup feature') }
+ let_it_be(:sub_subgroup_label1) { create(:group_label, group: sub_subgroup, name: 'sub_subgroup feature') }
+ let_it_be(:sub_subgroup_label2) { create(:group_label, group: sub_subgroup, name: 'new sub_subgroup feature') }
+
+ specify do
+ expect(described_class).to have_nullable_graphql_type(Types::LabelType.connection_type)
+ end
+
+ describe '#resolve' do
+ context 'with unauthorized user' do
+ it 'returns no labels' do
+ expect { resolve_labels(project) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'with authorized user' do
+ it 'returns no labels' do
+ group.add_guest(current_user)
+
+ expect { resolve_labels(project) }.not_to raise_error
+ end
+ end
+
+ context 'without parent' do
+ it 'returns no labels' do
+ expect(resolve_labels(nil)).to eq(Label.none)
+ end
+ end
+
+ context 'at project level' do
+ before_all do
+ group.add_developer(current_user)
+ end
+
+ # because :include_ancestor_groups, :include_descendant_groups, :only_group_labels default to false
+ # the `nil` value would be equivalent to passing in `false` so just check for `nil` option
+ where(:include_ancestor_groups, :include_descendant_groups, :only_group_labels, :search_term, :test) do
+ nil | nil | nil | nil | -> { expect(subject).to contain_exactly(label1, label2, subgroup_label1, subgroup_label2) }
+ nil | nil | true | nil | -> { expect(subject).to contain_exactly(label1, label2, subgroup_label1, subgroup_label2) }
+ nil | true | nil | nil | -> { expect(subject).to contain_exactly(label1, label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
+ nil | true | true | nil | -> { expect(subject).to contain_exactly(label1, label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
+ true | nil | nil | nil | -> { expect(subject).to contain_exactly(label1, label2, group_label1, group_label2, subgroup_label1, subgroup_label2) }
+ true | nil | true | nil | -> { expect(subject).to contain_exactly(label1, label2, group_label1, group_label2, subgroup_label1, subgroup_label2) }
+ true | true | nil | nil | -> { expect(subject).to contain_exactly(label1, label2, group_label1, group_label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
+ true | true | true | nil | -> { expect(subject).to contain_exactly(label1, label2, group_label1, group_label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
+
+ nil | nil | nil | 'new' | -> { expect(subject).to contain_exactly(label2, subgroup_label2) }
+ nil | nil | true | 'new' | -> { expect(subject).to contain_exactly(label2, subgroup_label2) }
+ nil | true | nil | 'new' | -> { expect(subject).to contain_exactly(label2, subgroup_label2, sub_subgroup_label2) }
+ nil | true | true | 'new' | -> { expect(subject).to contain_exactly(label2, subgroup_label2, sub_subgroup_label2) }
+ true | nil | nil | 'new' | -> { expect(subject).to contain_exactly(label2, group_label2, subgroup_label2) }
+ true | nil | true | 'new' | -> { expect(subject).to contain_exactly(label2, group_label2, subgroup_label2) }
+ true | true | nil | 'new' | -> { expect(subject).to contain_exactly(label2, group_label2, subgroup_label2, sub_subgroup_label2) }
+ true | true | true | 'new' | -> { expect(subject).to contain_exactly(label2, group_label2, subgroup_label2, sub_subgroup_label2) }
+ end
+
+ with_them do
+ let(:params) do
+ {
+ include_ancestor_groups: include_ancestor_groups,
+ include_descendant_groups: include_descendant_groups,
+ only_group_labels: only_group_labels,
+ search_term: search_term
+ }
+ end
+
+ subject { resolve_labels(project, params) }
+
+ it { self.instance_exec(&test) }
+ end
+ end
+ end
+
+ def resolve_labels(parent, args = {}, context = { current_user: current_user })
+ resolve(described_class, obj: parent, args: args, ctx: context)
+ end
+end
diff --git a/spec/graphql/types/group_type_spec.rb b/spec/graphql/types/group_type_spec.rb
index de19e8b602a..bba702ba3e9 100644
--- a/spec/graphql/types/group_type_spec.rb
+++ b/spec/graphql/types/group_type_spec.rb
@@ -38,5 +38,7 @@ RSpec.describe GitlabSchema.types['Group'] do
it { is_expected.to have_graphql_resolver(Resolvers::GroupMembersResolver) }
end
- it_behaves_like 'a GraphQL type with labels'
+ it_behaves_like 'a GraphQL type with labels' do
+ let(:labels_resolver_arguments) { [:search_term, :includeAncestorGroups, :includeDescendantGroups, :onlyGroupLabels] }
+ end
end
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index 95c835773e1..9579ef8b99b 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -332,7 +332,9 @@ RSpec.describe GitlabSchema.types['Project'] do
it { is_expected.to have_graphql_resolver(Resolvers::Terraform::StatesResolver) }
end
- it_behaves_like 'a GraphQL type with labels'
+ it_behaves_like 'a GraphQL type with labels' do
+ let(:labels_resolver_arguments) { [:search_term, :includeAncestorGroups] }
+ end
describe 'jira_imports' do
subject { resolve_field(:jira_imports, project) }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb
index 3616461d94f..cd868a57bbc 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/template_usage_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::TemplateUsage do
%w(Template-1 Template-2).each do |expected_template|
expect(Gitlab::UsageDataCounters::CiTemplateUniqueCounter).to(
receive(:track_unique_project_event)
- .with(project_id: project.id, template: expected_template)
+ .with(project_id: project.id, template: expected_template, config_source: pipeline.config_source)
)
end
diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb
index fa01d4e48df..20a8f2f6a41 100644
--- a/spec/lib/gitlab/url_blocker_spec.rb
+++ b/spec/lib/gitlab/url_blocker_spec.rb
@@ -160,6 +160,47 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
end
end
end
+
+ context 'when resolving runs into a timeout' do
+ let(:import_url) { 'http://example.com' }
+
+ subject { described_class.validate!(import_url, dns_rebind_protection: dns_rebind_protection) }
+
+ before do
+ skip 'timeout is not available' unless timeout_available?
+
+ stub_env('RSPEC_ALLOW_INVALID_URLS', 'false')
+ stub_const("#{described_class}::GETADDRINFO_TIMEOUT_SECONDS", 0)
+ end
+
+ context 'with dns rebinding enabled' do
+ let(:dns_rebind_protection) { true }
+
+ it 'raises an error due to DNS timeout' do
+ expect { subject }.to raise_error(described_class::BlockedUrlError)
+ end
+ end
+
+ context 'with dns rebinding disabled' do
+ let(:dns_rebind_protection) { false }
+
+ it_behaves_like 'validates URI and hostname' do
+ let(:expected_uri) { import_url }
+ let(:expected_hostname) { nil }
+ end
+ end
+
+ # Detect whether the timeout option is available.
+ #
+ # See https://bugs.ruby-lang.org/issues/15553
+ def timeout_available?
+ Addrinfo.getaddrinfo('localhost', nil, timeout: 0)
+
+ false
+ rescue SocketError
+ true
+ end
+ end
end
describe '#blocked_url?' do
diff --git a/spec/lib/gitlab/usage_data_counters/ci_template_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/ci_template_unique_counter_spec.rb
index 4b07f9143b5..b1d5d106082 100644
--- a/spec/lib/gitlab/usage_data_counters/ci_template_unique_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/ci_template_unique_counter_spec.rb
@@ -3,28 +3,88 @@
require 'spec_helper'
RSpec.describe Gitlab::UsageDataCounters::CiTemplateUniqueCounter do
- let(:project_id) { 1 }
-
describe '.track_unique_project_event' do
- described_class::TEMPLATE_TO_EVENT.keys.each do |template|
- context "when given template #{template}" do
- it_behaves_like 'tracking unique hll events' do
- subject(:request) { described_class.track_unique_project_event(project_id: project_id, template: template) }
+ using RSpec::Parameterized::TableSyntax
+
+ where(:template, :config_source, :expected_event) do
+ # Implicit Auto DevOps usage
+ 'Auto-DevOps.gitlab-ci.yml' | :auto_devops_source | 'p_ci_templates_implicit_auto_devops'
+ 'Jobs/Build.gitlab-ci.yml' | :auto_devops_source | 'p_ci_templates_implicit_auto_devops_build'
+ 'Jobs/Deploy.gitlab-ci.yml' | :auto_devops_source | 'p_ci_templates_implicit_auto_devops_deploy'
+ 'Security/SAST.gitlab-ci.yml' | :auto_devops_source | 'p_ci_templates_implicit_security_sast'
+ 'Security/Secret-Detection.gitlab-ci.yml' | :auto_devops_source | 'p_ci_templates_implicit_security_secret_detection'
+ # Explicit include:template usage
+ '5-Minute-Production-App.gitlab-ci.yml' | :repository_source | 'p_ci_templates_5_min_production_app'
+ 'Auto-DevOps.gitlab-ci.yml' | :repository_source | 'p_ci_templates_auto_devops'
+ 'AWS/CF-Provision-and-Deploy-EC2.gitlab-ci.yml' | :repository_source | 'p_ci_templates_aws_cf_deploy_ec2'
+ 'AWS/Deploy-ECS.gitlab-ci.yml' | :repository_source | 'p_ci_templates_aws_deploy_ecs'
+ 'Jobs/Build.gitlab-ci.yml' | :repository_source | 'p_ci_templates_auto_devops_build'
+ 'Jobs/Deploy.gitlab-ci.yml' | :repository_source | 'p_ci_templates_auto_devops_deploy'
+ 'Jobs/Deploy.latest.gitlab-ci.yml' | :repository_source | 'p_ci_templates_auto_devops_deploy_latest'
+ 'Security/SAST.gitlab-ci.yml' | :repository_source | 'p_ci_templates_security_sast'
+ 'Security/Secret-Detection.gitlab-ci.yml' | :repository_source | 'p_ci_templates_security_secret_detection'
+ 'Terraform/Base.latest.gitlab-ci.yml' | :repository_source | 'p_ci_templates_terraform_base_latest'
+ end
+
+ with_them do
+ it_behaves_like 'tracking unique hll events' do
+ subject(:request) { described_class.track_unique_project_event(project_id: project_id, template: template, config_source: config_source) }
+
+ let(:project_id) { 1 }
+ let(:target_id) { expected_event }
+ let(:expected_type) { instance_of(Integer) }
+ end
+ end
+
+ context 'known_events coverage tests' do
+ let(:project_id) { 1 }
+ let(:config_source) { :repository_source }
- let(:target_id) { "p_ci_templates_#{described_class::TEMPLATE_TO_EVENT[template]}" }
- let(:expected_type) { instance_of(Integer) }
+ # These tests help guard against missing "explicit" events in known_events/ci_templates.yml
+ context 'explicit include:template events' do
+ described_class::TEMPLATE_TO_EVENT.keys.each do |template|
+ it "does not raise error for #{template}" do
+ expect do
+ described_class.track_unique_project_event(project_id: project_id, template: template, config_source: config_source)
+ end.not_to raise_error
+ end
+ end
+ end
+
+ # This test is to help guard against missing "implicit" events in known_events/ci_templates.yml
+ it 'does not raise error for any template in an implicit Auto DevOps pipeline' do
+ project = create(:project, :auto_devops)
+ pipeline = double(project: project)
+ command = double
+ result = Gitlab::Ci::YamlProcessor.new(
+ Gitlab::Ci::Pipeline::Chain::Config::Content::AutoDevops.new(pipeline, command).content,
+ project: project,
+ user: double,
+ sha: double
+ ).execute
+
+ config_source = :auto_devops_source
+
+ result.included_templates.each do |template|
+ expect do
+ described_class.track_unique_project_event(project_id: project.id, template: template, config_source: config_source)
+ end.not_to raise_error
end
end
end
- it 'does not track templates outside of TEMPLATE_TO_EVENT' do
- expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to(
- receive(:track_event)
- )
+ context 'templates outside of TEMPLATE_TO_EVENT' do
+ let(:project_id) { 1 }
+ let(:config_source) { :repository_source }
+
Dir.glob(File.join('lib', 'gitlab', 'ci', 'templates', '**'), base: Rails.root) do |template|
next if described_class::TEMPLATE_TO_EVENT.key?(template)
- described_class.track_unique_project_event(project_id: 1, template: template)
+ it "does not track #{template}" do
+ expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to(receive(:track_event))
+
+ described_class.track_unique_project_event(project_id: project_id, template: template, config_source: config_source)
+ end
end
end
end
diff --git a/spec/support/helpers/dns_helpers.rb b/spec/support/helpers/dns_helpers.rb
index 1795b0a9ac3..52c708e77a5 100644
--- a/spec/support/helpers/dns_helpers.rb
+++ b/spec/support/helpers/dns_helpers.rb
@@ -12,19 +12,38 @@ module DnsHelpers
end
def stub_all_dns!
- allow(Addrinfo).to receive(:getaddrinfo).with(anything, anything, nil, :STREAM).and_return([])
- allow(Addrinfo).to receive(:getaddrinfo).with(anything, anything, nil, :STREAM, anything, anything).and_return([])
+ allow(Addrinfo).to receive(:getaddrinfo).and_return([])
end
def stub_invalid_dns!
- allow(Addrinfo).to receive(:getaddrinfo).with(/\Afoobar\.\w|(\d{1,3}\.){4,}\d{1,3}\z/i, anything, nil, :STREAM) do
- raise SocketError.new("getaddrinfo: Name or service not known")
- end
+ invalid_addresses = %r{
+ \A
+ (?:
+ foobar\.\w |
+ (?:\d{1,3}\.){4,}\d{1,3}
+ )
+ \z
+ }ix
+
+ allow(Addrinfo).to receive(:getaddrinfo)
+ .with(invalid_addresses, any_args)
+ .and_raise(SocketError, 'getaddrinfo: Name or service not known')
end
def permit_local_dns!
- local_addresses = /\A(127|10)\.0\.0\.\d{1,3}|(192\.168|172\.16)\.\d{1,3}\.\d{1,3}|0\.0\.0\.0|localhost\z/i
- allow(Addrinfo).to receive(:getaddrinfo).with(local_addresses, anything, nil, :STREAM).and_call_original
- allow(Addrinfo).to receive(:getaddrinfo).with(local_addresses, anything, nil, :STREAM, anything, anything, any_args).and_call_original
+ local_addresses = %r{
+ \A
+ (?:
+ (?:127|10)\.0\.0\.\d{1,3} |
+ (?:192\.168|172\.16)\.\d{1,3}\.\d{1,3} |
+ 0\.0\.0\.0 |
+ localhost
+ )
+ \z
+ }ix
+
+ allow(Addrinfo).to receive(:getaddrinfo)
+ .with(local_addresses, any_args)
+ .and_call_original
end
end
diff --git a/spec/support/helpers/stub_requests.rb b/spec/support/helpers/stub_requests.rb
index 473f07dd413..81872b1c908 100644
--- a/spec/support/helpers/stub_requests.rb
+++ b/spec/support/helpers/stub_requests.rb
@@ -18,14 +18,13 @@ module StubRequests
end
def stub_dns(url, ip_address:, port: 80)
- url = parse_url(url)
+ url = URI(url)
socket = Socket.sockaddr_in(port, ip_address)
addr = Addrinfo.new(socket)
- # See Gitlab::UrlBlocker
allow(Addrinfo).to receive(:getaddrinfo)
- .with(url.hostname, url.port, nil, :STREAM)
- .and_return([addr])
+ .with(url.hostname, url.port, any_args)
+ .and_return([addr])
end
def stub_all_dns(url, ip_address:)
@@ -34,22 +33,14 @@ module StubRequests
socket = Socket.sockaddr_in(port, ip_address)
addr = Addrinfo.new(socket)
- # See Gitlab::UrlBlocker
- allow(Addrinfo).to receive(:getaddrinfo).and_call_original
allow(Addrinfo).to receive(:getaddrinfo)
- .with(url.hostname, anything, nil, :STREAM)
+ .with(url.hostname, any_args)
.and_return([addr])
end
def stubbed_hostname(url, hostname: IP_ADDRESS_STUB)
- url = parse_url(url)
+ url = URI(url)
url.hostname = hostname
url.to_s
end
-
- private
-
- def parse_url(url)
- url.is_a?(URI) ? url : URI(url)
- end
end
diff --git a/spec/support/shared_examples/graphql/label_fields.rb b/spec/support/shared_examples/graphql/label_fields.rb
index 0d09ab391f1..4159e4e03ab 100644
--- a/spec/support/shared_examples/graphql/label_fields.rb
+++ b/spec/support/shared_examples/graphql/label_fields.rb
@@ -18,7 +18,7 @@ RSpec.shared_examples 'a GraphQL type with labels' do
subject { described_class.fields['labels'] }
it { is_expected.to have_graphql_type(Types::LabelType.connection_type) }
- it { is_expected.to have_graphql_arguments(:search_term) }
+ it { is_expected.to have_graphql_arguments(labels_resolver_arguments) }
end
end