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>2019-10-09 18:05:58 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2019-10-09 18:05:58 +0300
commitcc6b394a063eb77c90505e4adca2e2da2e29c3a2 (patch)
tree94ec203489b9c800b832c7bd4f1b7aadc5a96958
parent0a850868dfb85086cba8320cee9dac4657dcae6c (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/components/cluster_form_dropdown.vue13
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue119
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/components/role_name_dropdown.vue53
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/services/aws_services_facade.js35
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/store/actions.js8
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/store/index.js8
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/store/mutation_types.js2
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js6
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/store/state.js1
-rw-r--r--app/assets/javascripts/jobs/components/job_log.vue52
-rw-r--r--app/assets/stylesheets/pages/builds.scss20
-rw-r--r--app/finders/snippets_finder.rb20
-rw-r--r--app/models/concerns/notification_branch_selection.rb2
-rw-r--r--changelogs/unreleased/12739-incomplete-group-audit-logs-in-group-view.yml5
-rw-r--r--changelogs/unreleased/32146-remove-fe-code.yml5
-rw-r--r--changelogs/unreleased/33582-fix-protected-branch-wildcard.yml5
-rw-r--r--changelogs/unreleased/fj-26123-narrow-snippet-search-scope-in-com.yml5
-rw-r--r--changelogs/unreleased/gitaly-version-v1.67.0.yml5
-rw-r--r--doc/development/internal_api.md10
-rw-r--r--lib/api/internal/base.rb4
-rw-r--r--lib/gitlab/snippet_search_results.rb8
-rw-r--r--locale/gitlab.pot42
-rw-r--r--qa/qa.rb1
-rw-r--r--qa/qa/page/project/milestone/show.rb14
-rw-r--r--spec/features/projects/jobs/user_browses_job_spec.rb60
-rw-r--r--spec/finders/snippets_finder_spec.rb20
-rw-r--r--spec/frontend/create_cluster/eks_cluster/components/cluster_form_dropdown_spec.js25
-rw-r--r--spec/frontend/create_cluster/eks_cluster/components/eks_cluster_configuration_form_spec.js126
-rw-r--r--spec/frontend/create_cluster/eks_cluster/components/role_name_dropdown_spec.js43
-rw-r--r--spec/frontend/create_cluster/eks_cluster/store/actions_spec.js22
-rw-r--r--spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js22
-rw-r--r--spec/javascripts/jobs/components/job_log_spec.js57
-rw-r--r--spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js5
-rw-r--r--spec/requests/api/internal/base_spec.rb54
-rw-r--r--spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb72
36 files changed, 539 insertions, 412 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index b6148bc0a75..65ee0959841 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-1.66.0
+1.67.0
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/components/cluster_form_dropdown.vue b/app/assets/javascripts/create_cluster/eks_cluster/components/cluster_form_dropdown.vue
index f9465da6fda..3c6da43c4c4 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/components/cluster_form_dropdown.vue
+++ b/app/assets/javascripts/create_cluster/eks_cluster/components/cluster_form_dropdown.vue
@@ -3,6 +3,8 @@ import DropdownSearchInput from '~/vue_shared/components/dropdown/dropdown_searc
import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue';
import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue';
+const findItem = (items, valueProp, value) => items.find(item => item[valueProp] === value);
+
export default {
components: {
DropdownButton,
@@ -26,7 +28,7 @@ export default {
default: '',
},
value: {
- type: Object,
+ type: [Object, String],
required: false,
default: () => null,
},
@@ -93,8 +95,8 @@ export default {
},
data() {
return {
+ selectedItem: findItem(this.items, this.value),
searchQuery: '',
- selectedItem: null,
};
},
computed: {
@@ -127,10 +129,15 @@ export default {
return (this.selectedItem && this.selectedItem[this.valueProperty]) || '';
},
},
+ watch: {
+ value(value) {
+ this.selectedItem = findItem(this.items, this.valueProperty, value);
+ },
+ },
methods: {
select(item) {
this.selectedItem = item;
- this.$emit('input', item);
+ this.$emit('input', item[this.valueProperty]);
},
},
};
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 6f6b9ad025a..94a446f1721 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
@@ -3,12 +3,15 @@ import { createNamespacedHelpers, mapState, mapActions } from 'vuex';
import { sprintf, s__ } from '~/locale';
import ClusterFormDropdown from './cluster_form_dropdown.vue';
import RegionDropdown from './region_dropdown.vue';
-import RoleNameDropdown from './role_name_dropdown.vue';
import SecurityGroupDropdown from './security_group_dropdown.vue';
+const { mapState: mapRolesState, mapActions: mapRolesActions } = createNamespacedHelpers('roles');
const { mapState: mapRegionsState, mapActions: mapRegionsActions } = createNamespacedHelpers(
'regions',
);
+const { mapState: mapKeyPairsState, mapActions: mapKeyPairsActions } = createNamespacedHelpers(
+ 'keyPairs',
+);
const { mapState: mapVpcsState, mapActions: mapVpcActions } = createNamespacedHelpers('vpcs');
const { mapState: mapSubnetsState, mapActions: mapSubnetActions } = createNamespacedHelpers(
'subnets',
@@ -18,16 +21,31 @@ export default {
components: {
ClusterFormDropdown,
RegionDropdown,
- RoleNameDropdown,
SecurityGroupDropdown,
},
computed: {
- ...mapState(['selectedRegion', 'selectedVpc', 'selectedSubnet']),
+ ...mapState([
+ 'selectedRegion',
+ 'selectedKeyPair',
+ 'selectedVpc',
+ 'selectedSubnet',
+ 'selectedRole',
+ ]),
+ ...mapRolesState({
+ roles: 'items',
+ isLoadingRoles: 'isLoadingItems',
+ loadingRolesError: 'loadingItemsError',
+ }),
...mapRegionsState({
regions: 'items',
isLoadingRegions: 'isLoadingItems',
loadingRegionsError: 'loadingItemsError',
}),
+ ...mapKeyPairsState({
+ keyPairs: 'items',
+ isLoadingKeyPairs: 'isLoadingItems',
+ loadingKeyPairsError: 'loadingItemsError',
+ }),
...mapVpcsState({
vpcs: 'items',
isLoadingVpcs: 'isLoadingItems',
@@ -41,9 +59,38 @@ export default {
vpcDropdownDisabled() {
return !this.selectedRegion;
},
+ keyPairDropdownDisabled() {
+ return !this.selectedRegion;
+ },
subnetDropdownDisabled() {
return !this.selectedVpc;
},
+ roleDropdownHelpText() {
+ return sprintf(
+ s__(
+ 'ClusterIntegration|Select the IAM Role to allow Amazon EKS and the Kubernetes control plane to manage AWS resources on your behalf. To use a new role name, first create one on %{startLink}Amazon Web Services%{endLink}.',
+ ),
+ {
+ startLink:
+ '<a href="https://console.aws.amazon.com/iam/home?#roles" target="_blank" rel="noopener noreferrer">',
+ endLink: '</a>',
+ },
+ false,
+ );
+ },
+ keyPairDropdownHelpText() {
+ return sprintf(
+ s__(
+ 'ClusterIntegration|Select the key pair name that will be used to create EC2 nodes. To use a new key pair name, first create one on %{startLink}Amazon Web Services%{endLink}.',
+ ),
+ {
+ startLink:
+ '<a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html#having-ec2-create-your-key-pair" target="_blank" rel="noopener noreferrer">',
+ endLink: '</a>',
+ },
+ false,
+ );
+ },
vpcDropdownHelpText() {
return sprintf(
s__(
@@ -73,15 +120,19 @@ export default {
},
mounted() {
this.fetchRegions();
+ this.fetchRoles();
},
methods: {
- ...mapActions(['setRegion', 'setVpc', 'setSubnet']),
+ ...mapActions(['setRegion', 'setVpc', 'setSubnet', 'setRole', 'setKeyPair']),
...mapRegionsActions({ fetchRegions: 'fetchItems' }),
...mapVpcActions({ fetchVpcs: 'fetchItems' }),
...mapSubnetActions({ fetchSubnets: 'fetchItems' }),
- setRegionAndFetchVpcs(region) {
+ ...mapRolesActions({ fetchRoles: 'fetchItems' }),
+ ...mapKeyPairsActions({ fetchKeyPairs: 'fetchItems' }),
+ setRegionAndFetchVpcsAndKeyPairs(region) {
this.setRegion({ region });
this.fetchVpcs({ region });
+ this.fetchKeyPairs({ region });
},
setVpcAndFetchSubnets(vpc) {
this.setVpc({ vpc });
@@ -93,28 +144,58 @@ export default {
<template>
<form name="eks-cluster-configuration-form">
<div class="form-group">
- <label class="label-bold" name="role" for="eks-role">{{
- s__('ClusterIntegration|Role name')
- }}</label>
- <role-name-dropdown />
+ <label class="label-bold" for="eks-role">{{ s__('ClusterIntegration|Role name') }}</label>
+ <cluster-form-dropdown
+ field-id="eks-role"
+ field-name="eks-role"
+ :input="selectedRole"
+ :items="roles"
+ :loading="isLoadingRoles"
+ :loading-text="s__('ClusterIntegration|Loading IAM Roles')"
+ :placeholder="s__('ClusterIntergation|Select role name')"
+ :search-field-placeholder="s__('ClusterIntegration|Search IAM Roles')"
+ :empty-text="s__('ClusterIntegration|No IAM Roles found')"
+ :has-errors="Boolean(loadingRolesError)"
+ :error-message="s__('ClusterIntegration|Could not load IAM roles')"
+ @input="setRole({ role: $event })"
+ />
+ <p class="form-text text-muted" v-html="roleDropdownHelpText"></p>
</div>
<div class="form-group">
- <label class="label-bold" name="role" for="eks-role">{{
- s__('ClusterIntegration|Region')
- }}</label>
+ <label class="label-bold" for="eks-role">{{ s__('ClusterIntegration|Region') }}</label>
<region-dropdown
:value="selectedRegion"
:regions="regions"
:error="loadingRegionsError"
:loading="isLoadingRegions"
- @input="setRegionAndFetchVpcs($event)"
+ @input="setRegionAndFetchVpcsAndKeyPairs($event)"
/>
</div>
<div class="form-group">
- <label class="label-bold" name="eks-vpc" for="eks-vpc">{{
- s__('ClusterIntegration|VPC')
+ <label class="label-bold" for="eks-key-pair">{{
+ s__('ClusterIntegration|Key pair name')
}}</label>
<cluster-form-dropdown
+ field-id="eks-key-pair"
+ field-name="eks-key-pair"
+ :input="selectedKeyPair"
+ :items="keyPairs"
+ :disabled="keyPairDropdownDisabled"
+ :disabled-text="s__('ClusterIntegration|Select a region to choose a Key Pair')"
+ :loading="isLoadingKeyPairs"
+ :loading-text="s__('ClusterIntegration|Loading Key Pairs')"
+ :placeholder="s__('ClusterIntergation|Select key pair')"
+ :search-field-placeholder="s__('ClusterIntegration|Search Key Pairs')"
+ :empty-text="s__('ClusterIntegration|No Key Pairs found')"
+ :has-errors="Boolean(loadingKeyPairsError)"
+ :error-message="s__('ClusterIntegration|Could not load Key Pairs')"
+ @input="setKeyPair({ keyPair: $event })"
+ />
+ <p class="form-text text-muted" v-html="keyPairDropdownHelpText"></p>
+ </div>
+ <div class="form-group">
+ <label class="label-bold" for="eks-vpc">{{ s__('ClusterIntegration|VPC') }}</label>
+ <cluster-form-dropdown
field-id="eks-vpc"
field-name="eks-vpc"
:input="selectedVpc"
@@ -126,16 +207,14 @@ export default {
:placeholder="s__('ClusterIntergation|Select a VPC')"
:search-field-placeholder="s__('ClusterIntegration|Search VPCs')"
:empty-text="s__('ClusterIntegration|No VPCs found')"
- :has-errors="loadingVpcsError"
+ :has-errors="Boolean(loadingVpcsError)"
:error-message="s__('ClusterIntegration|Could not load VPCs for the selected region')"
@input="setVpcAndFetchSubnets($event)"
/>
<p class="form-text text-muted" v-html="vpcDropdownHelpText"></p>
</div>
<div class="form-group">
- <label class="label-bold" name="eks-subnet" for="eks-subnet">{{
- s__('ClusterIntegration|Subnet')
- }}</label>
+ <label class="label-bold" for="eks-role">{{ s__('ClusterIntegration|Subnet') }}</label>
<cluster-form-dropdown
field-id="eks-subnet"
field-name="eks-subnet"
@@ -148,7 +227,7 @@ export default {
:placeholder="s__('ClusterIntergation|Select a subnet')"
:search-field-placeholder="s__('ClusterIntegration|Search subnets')"
:empty-text="s__('ClusterIntegration|No subnet found')"
- :has-errors="loadingSubnetsError"
+ :has-errors="Boolean(loadingSubnetsError)"
:error-message="s__('ClusterIntegration|Could not load subnets for the selected VPC')"
@input="setSubnet({ subnet: $event })"
/>
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/components/role_name_dropdown.vue b/app/assets/javascripts/create_cluster/eks_cluster/components/role_name_dropdown.vue
deleted file mode 100644
index 70230b294ac..00000000000
--- a/app/assets/javascripts/create_cluster/eks_cluster/components/role_name_dropdown.vue
+++ /dev/null
@@ -1,53 +0,0 @@
-<script>
-import { sprintf, s__ } from '~/locale';
-
-import ClusterFormDropdown from './cluster_form_dropdown.vue';
-
-export default {
- components: {
- ClusterFormDropdown,
- },
- props: {
- roles: {
- type: Array,
- required: false,
- default: () => [],
- },
- loading: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- computed: {
- helpText() {
- return sprintf(
- s__(
- 'ClusterIntegration|Select the IAM Role to allow Amazon EKS and the Kubernetes control plane to manage AWS resources on your behalf. To use a new role name, first create one on %{startLink}Amazon Web Services%{endLink}.',
- ),
- {
- startLink:
- '<a href="https://console.aws.amazon.com/iam/home?#roles" target="_blank" rel="noopener noreferrer">',
- endLink: '</a>',
- },
- false,
- );
- },
- },
-};
-</script>
-<template>
- <div>
- <cluster-form-dropdown
- field-id="eks-role-name"
- field-name="eks-role-name"
- :items="roles"
- :loading="loading"
- :loading-text="s__('ClusterIntegration|Loading IAM Roles')"
- :placeholder="s__('ClusterIntergation|Select role name')"
- :search-field-placeholder="s__('ClusterIntegration|Search IAM Roles')"
- :empty-text="s__('ClusterIntegration|No IAM Roles found')"
- />
- <p class="form-text text-muted" v-html="helpText"></p>
- </div>
-</template>
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/services/aws_services_facade.js b/app/assets/javascripts/create_cluster/eks_cluster/services/aws_services_facade.js
index 030b6b384b1..98ad33d6651 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/services/aws_services_facade.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/services/aws_services_facade.js
@@ -1,4 +1,39 @@
import EC2 from 'aws-sdk/clients/ec2';
+import IAM from 'aws-sdk/clients/iam';
+
+export const fetchRoles = () =>
+ new Promise((resolve, reject) => {
+ const iam = new IAM();
+
+ iam
+ .listRoles()
+ .on('success', ({ data: { Roles: roles } }) => {
+ const transformedRoles = roles.map(({ RoleName: name }) => ({ name }));
+
+ resolve(transformedRoles);
+ })
+ .on('error', error => {
+ reject(error);
+ })
+ .send();
+ });
+
+export const fetchKeyPairs = () =>
+ new Promise((resolve, reject) => {
+ const ec2 = new EC2();
+
+ ec2
+ .describeKeyPairs()
+ .on('success', ({ data: { KeyPairs: keyPairs } }) => {
+ const transformedKeyPairs = keyPairs.map(({ RegionName: name }) => ({ name }));
+
+ resolve(transformedKeyPairs);
+ })
+ .on('error', error => {
+ reject(error);
+ })
+ .send();
+ });
export const fetchRegions = () =>
new Promise((resolve, reject) => {
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 0809fc2dfc4..f2abc121f57 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js
@@ -4,6 +4,10 @@ export const setRegion = ({ commit }, payload) => {
commit(types.SET_REGION, payload);
};
+export const setKeyPair = ({ commit }, payload) => {
+ commit(types.SET_KEY_PAIR, payload);
+};
+
export const setVpc = ({ commit }, payload) => {
commit(types.SET_VPC, payload);
};
@@ -12,4 +16,8 @@ export const setSubnet = ({ commit }, payload) => {
commit(types.SET_SUBNET, payload);
};
+export const setRole = ({ commit }, payload) => {
+ commit(types.SET_ROLE, payload);
+};
+
export default () => {};
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/index.js b/app/assets/javascripts/create_cluster/eks_cluster/store/index.js
index 622095f2cc8..584cd267d8c 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/store/index.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/store/index.js
@@ -15,10 +15,18 @@ const createStore = () =>
mutations,
state: state(),
modules: {
+ roles: {
+ namespaced: true,
+ ...clusterDropdownStore(awsServices.fetchRoles),
+ },
regions: {
namespaced: true,
...clusterDropdownStore(awsServices.fetchRegions),
},
+ keyPairs: {
+ namespaced: true,
+ ...clusterDropdownStore(awsServices.fetchKeyPairs),
+ },
vpcs: {
namespaced: true,
...clusterDropdownStore(awsServices.fetchVpcs),
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 ada76b21f18..b9af9c1d5a4 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
@@ -1,3 +1,5 @@
export const SET_REGION = 'SET_REGION';
export const SET_VPC = 'SET_VPC';
+export const SET_KEY_PAIR = 'SET_KEY_PAIR';
export const SET_SUBNET = 'SET_SUBNET';
+export const SET_ROLE = 'SET_ROLE';
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 346716bb0df..748a78e0b1e 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/store/mutations.js
@@ -4,10 +4,16 @@ export default {
[types.SET_REGION](state, { region }) {
state.selectedRegion = region;
},
+ [types.SET_KEY_PAIR](state, { keyPair }) {
+ state.selectedKeyPair = keyPair;
+ },
[types.SET_VPC](state, { vpc }) {
state.selectedVpc = vpc;
},
[types.SET_SUBNET](state, { subnet }) {
state.selectedSubnet = subnet;
},
+ [types.SET_ROLE](state, { role }) {
+ state.selectedRole = role;
+ },
};
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 84880e15d9c..6ed174d247b 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/store/state.js
+++ b/app/assets/javascripts/create_cluster/eks_cluster/store/state.js
@@ -4,6 +4,7 @@ export default () => ({
selectedRegion: '',
selectedRole: '',
+ selectedKeyPair: '',
selectedVpc: '',
selectedSubnet: '',
selectedSecurityGroup: '',
diff --git a/app/assets/javascripts/jobs/components/job_log.vue b/app/assets/javascripts/jobs/components/job_log.vue
index a3fbe9338ee..20888c0af42 100644
--- a/app/assets/javascripts/jobs/components/job_log.vue
+++ b/app/assets/javascripts/jobs/components/job_log.vue
@@ -19,18 +19,13 @@ export default {
updated() {
this.$nextTick(() => {
this.handleScrollDown();
- this.handleCollapsibleRows();
});
},
mounted() {
this.$nextTick(() => {
this.handleScrollDown();
- this.handleCollapsibleRows();
});
},
- destroyed() {
- this.removeEventListener();
- },
methods: {
...mapActions(['scrollBottom']),
/**
@@ -47,53 +42,6 @@ export default {
}, 0);
}
},
- removeEventListener() {
- this.$el.querySelectorAll('.js-section-start').forEach(el => {
- const titleSection = el.nextSibling;
- titleSection.removeEventListener(
- 'click',
- this.handleHeaderClick.bind(this, el, el.dataset.section),
- );
- el.removeEventListener('click', this.handleSectionClick);
- });
- },
- /**
- * The collapsible rows are sent in HTML from the backend
- * We need tos add a onclick handler for the divs that match `.js-section-start`
- *
- */
- handleCollapsibleRows() {
- this.$el.querySelectorAll('.js-section-start').forEach(el => {
- const titleSection = el.nextSibling;
- titleSection.addEventListener(
- 'click',
- this.handleHeaderClick.bind(this, el, el.dataset.section),
- );
- el.addEventListener('click', this.handleSectionClick);
- });
- },
-
- handleHeaderClick(arrowElement, section) {
- this.updateToggleSection(arrowElement, section);
- },
-
- updateToggleSection(arrow, section) {
- // toggle the arrow class
- arrow.classList.toggle('fa-caret-right');
- arrow.classList.toggle('fa-caret-down');
-
- // hide the sections
- const sibilings = this.$el.querySelectorAll(`.js-s-${section}:not(.js-section-header)`);
- sibilings.forEach(row => row.classList.toggle('hidden'));
- },
- /**
- * On click, we toggle the hidden class of
- * all the rows that match the `data-section` selector
- */
- handleSectionClick(evt) {
- const clickedArrow = evt.currentTarget;
- this.updateToggleSection(clickedArrow, clickedArrow.dataset.section);
- },
},
};
</script>
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index 89fd160b575..c7d51a2093a 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -124,26 +124,6 @@
float: left;
padding-left: $gl-padding-8;
}
-
- .section-start {
- display: inline;
- }
-
- .section-start,
- .section-header {
- &:hover {
- cursor: pointer;
-
- &::after {
- content: '';
- background-color: rgba($white-light, 0.2);
- left: 0;
- right: 0;
- position: absolute;
- height: $job-log-highlight-height;
- }
- }
- }
}
.build-header {
diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb
index bf29f15642d..6e2bb1ded43 100644
--- a/app/finders/snippets_finder.rb
+++ b/app/finders/snippets_finder.rb
@@ -59,18 +59,20 @@ class SnippetsFinder < UnionFinder
end
def execute
- base =
- if project
- snippets_for_a_single_project
- else
- snippets_for_multiple_projects
- end
-
+ base = init_collection
base.with_optional_visibility(visibility_from_scope).fresh
end
private
+ def init_collection
+ if project
+ snippets_for_a_single_project
+ else
+ snippets_for_multiple_projects
+ end
+ end
+
# Produces a query that retrieves snippets from multiple projects.
#
# The resulting query will, depending on the user's permissions, include the
@@ -115,7 +117,7 @@ class SnippetsFinder < UnionFinder
# This method requires that `current_user` returns a `User` instead of `nil`,
# and is optimised for this specific scenario.
def snippets_of_authorized_projects
- base = author ? snippets_for_author : Snippet.all
+ base = author ? author.snippets : Snippet.all
base
.only_include_projects_with_snippets_enabled(include_private: true)
@@ -157,3 +159,5 @@ class SnippetsFinder < UnionFinder
end
end
end
+
+SnippetsFinder.prepend_if_ee('EE::SnippetsFinder')
diff --git a/app/models/concerns/notification_branch_selection.rb b/app/models/concerns/notification_branch_selection.rb
index d8e18de7551..7f00b652530 100644
--- a/app/models/concerns/notification_branch_selection.rb
+++ b/app/models/concerns/notification_branch_selection.rb
@@ -21,7 +21,7 @@ module NotificationBranchSelection
end
is_default_branch = ref == project.default_branch
- is_protected_branch = project.protected_branches.exists?(name: ref)
+ is_protected_branch = ProtectedBranch.protected?(project, ref)
case branches_to_be_notified
when "all"
diff --git a/changelogs/unreleased/12739-incomplete-group-audit-logs-in-group-view.yml b/changelogs/unreleased/12739-incomplete-group-audit-logs-in-group-view.yml
new file mode 100644
index 00000000000..206badd5ab2
--- /dev/null
+++ b/changelogs/unreleased/12739-incomplete-group-audit-logs-in-group-view.yml
@@ -0,0 +1,5 @@
+---
+title: Specify sort order explicitly for Group and Project audit events
+merge_request: 17739
+author:
+type: fixed
diff --git a/changelogs/unreleased/32146-remove-fe-code.yml b/changelogs/unreleased/32146-remove-fe-code.yml
new file mode 100644
index 00000000000..f7dd251de44
--- /dev/null
+++ b/changelogs/unreleased/32146-remove-fe-code.yml
@@ -0,0 +1,5 @@
+---
+title: Removes Collapsible Sections from Job Log
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/33582-fix-protected-branch-wildcard.yml b/changelogs/unreleased/33582-fix-protected-branch-wildcard.yml
new file mode 100644
index 00000000000..091a88f80a5
--- /dev/null
+++ b/changelogs/unreleased/33582-fix-protected-branch-wildcard.yml
@@ -0,0 +1,5 @@
+---
+title: Fix protected branch detection used by notification service
+merge_request: 18221
+author:
+type: fixed
diff --git a/changelogs/unreleased/fj-26123-narrow-snippet-search-scope-in-com.yml b/changelogs/unreleased/fj-26123-narrow-snippet-search-scope-in-com.yml
new file mode 100644
index 00000000000..a5f670257e1
--- /dev/null
+++ b/changelogs/unreleased/fj-26123-narrow-snippet-search-scope-in-com.yml
@@ -0,0 +1,5 @@
+---
+title: Narrow snippet search scope in GitLab.com
+merge_request: 17625
+author:
+type: performance
diff --git a/changelogs/unreleased/gitaly-version-v1.67.0.yml b/changelogs/unreleased/gitaly-version-v1.67.0.yml
new file mode 100644
index 00000000000..03846e4d4d8
--- /dev/null
+++ b/changelogs/unreleased/gitaly-version-v1.67.0.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade to Gitaly v1.67.0
+merge_request: 18326
+author:
+type: changed
diff --git a/doc/development/internal_api.md b/doc/development/internal_api.md
index a2665fac77a..63c441f2505 100644
--- a/doc/development/internal_api.md
+++ b/doc/development/internal_api.md
@@ -115,16 +115,6 @@ curl --request POST --header "Gitlab-Shared-Secret: <Base64 encoded token>" --da
- GitLab-shell
-## Get merge requests for a ref [NOT USED]
-
-```
-GET /internal/merge_request_urls
-```
-
-**Deprecated**: This used to be called from GitLab shell to fetch the
-merge requests for a change to output them after a push, but this is
-now handled in the `/internal/post_receive` call.
-
## Authorized Keys Check
This endpoint is called by the GitLab-shell authorized keys
diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb
index a5ec3e12b99..7963adfd7f4 100644
--- a/lib/api/internal/base.rb
+++ b/lib/api/internal/base.rb
@@ -112,10 +112,6 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
- get "/merge_request_urls" do
- merge_request_urls
- end
-
#
# Get a ssh key using the fingerprint
#
diff --git a/lib/gitlab/snippet_search_results.rb b/lib/gitlab/snippet_search_results.rb
index 6763914287a..e955ccd35da 100644
--- a/lib/gitlab/snippet_search_results.rb
+++ b/lib/gitlab/snippet_search_results.rb
@@ -45,7 +45,7 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord
def snippets
- SnippetsFinder.new(current_user)
+ SnippetsFinder.new(current_user, finder_params)
.execute
.includes(:author)
.reorder(updated_at: :desc)
@@ -67,5 +67,11 @@ module Gitlab
def paginated_objects(relation, page)
relation.page(page).per(per_page)
end
+
+ def finder_params
+ {}
+ end
end
end
+
+Gitlab::SnippetSearchResults.prepend_if_ee('::EE::Gitlab::SnippetSearchResults')
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 6fce25f2cb6..83fd5e28805 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -3417,6 +3417,12 @@ msgstr ""
msgid "ClusterIntegration|Copy Service Token"
msgstr ""
+msgid "ClusterIntegration|Could not load IAM roles"
+msgstr ""
+
+msgid "ClusterIntegration|Could not load Key Pairs"
+msgstr ""
+
msgid "ClusterIntegration|Could not load VPCs for the selected region"
msgstr ""
@@ -3549,6 +3555,9 @@ msgstr ""
msgid "ClusterIntegration|JupyterHub, a multi-user Hub, spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. JupyterHub can be used to serve notebooks to a class of students, a corporate data science group, or a scientific research group."
msgstr ""
+msgid "ClusterIntegration|Key pair name"
+msgstr ""
+
msgid "ClusterIntegration|Knative"
msgstr ""
@@ -3609,6 +3618,9 @@ msgstr ""
msgid "ClusterIntegration|Loading IAM Roles"
msgstr ""
+msgid "ClusterIntegration|Loading Key Pairs"
+msgstr ""
+
msgid "ClusterIntegration|Loading Regions"
msgstr ""
@@ -3630,6 +3642,9 @@ msgstr ""
msgid "ClusterIntegration|No IAM Roles found"
msgstr ""
+msgid "ClusterIntegration|No Key Pairs found"
+msgstr ""
+
msgid "ClusterIntegration|No VPCs found"
msgstr ""
@@ -3717,6 +3732,9 @@ msgstr ""
msgid "ClusterIntegration|Search IAM Roles"
msgstr ""
+msgid "ClusterIntegration|Search Key Pairs"
+msgstr ""
+
msgid "ClusterIntegration|Search VPCs"
msgstr ""
@@ -3744,6 +3762,9 @@ msgstr ""
msgid "ClusterIntegration|Select a VPC to use for your EKS Cluster resources. To use a new VPC, first create one on %{startLink}Amazon Web Services%{endLink}."
msgstr ""
+msgid "ClusterIntegration|Select a region to choose a Key Pair"
+msgstr ""
+
msgid "ClusterIntegration|Select a region to choose a VPC"
msgstr ""
@@ -3762,6 +3783,9 @@ msgstr ""
msgid "ClusterIntegration|Select the IAM Role to allow Amazon EKS and the Kubernetes control plane to manage AWS resources on your behalf. To use a new role name, first create one on %{startLink}Amazon Web Services%{endLink}."
msgstr ""
+msgid "ClusterIntegration|Select the key pair name that will be used to create EC2 nodes. To use a new key pair name, first create one on %{startLink}Amazon Web Services%{endLink}."
+msgstr ""
+
msgid "ClusterIntegration|Select zone"
msgstr ""
@@ -3906,6 +3930,9 @@ msgstr ""
msgid "ClusterIntergation|Select a subnet"
msgstr ""
+msgid "ClusterIntergation|Select key pair"
+msgstr ""
+
msgid "ClusterIntergation|Select role name"
msgstr ""
@@ -14014,9 +14041,15 @@ msgstr ""
msgid "SearchResults|Showing %{count} %{scope} for \"%{term}\""
msgstr ""
+msgid "SearchResults|Showing %{count} %{scope} for \"%{term}\" in your personal and project snippets"
+msgstr ""
+
msgid "SearchResults|Showing %{from} - %{to} of %{count} %{scope} for \"%{term}\""
msgstr ""
+msgid "SearchResults|Showing %{from} - %{to} of %{count} %{scope} for \"%{term}\" in your personal and project snippets"
+msgstr ""
+
msgid "SearchResults|We couldn't find any %{scope} matching %{term}"
msgstr ""
@@ -14207,6 +14240,12 @@ msgstr ""
msgid "SecurityDashboard|Project"
msgstr ""
+msgid "SecurityDashboard|Projects added"
+msgstr ""
+
+msgid "SecurityDashboard|Remove project from dashboard"
+msgstr ""
+
msgid "SecurityDashboard|Report type"
msgstr ""
@@ -14216,6 +14255,9 @@ msgstr ""
msgid "SecurityDashboard|Security Dashboard"
msgstr ""
+msgid "SecurityDashboard|Select a project to add by using the project search field above."
+msgstr ""
+
msgid "SecurityDashboard|Severity"
msgstr ""
diff --git a/qa/qa.rb b/qa/qa.rb
index 205e6b82aec..44a46e2ea38 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -259,7 +259,6 @@ module QA
module Milestone
autoload :New, 'qa/page/project/milestone/new'
autoload :Index, 'qa/page/project/milestone/index'
- autoload :Show, 'qa/page/project/milestone/show'
end
module Operations
diff --git a/qa/qa/page/project/milestone/show.rb b/qa/qa/page/project/milestone/show.rb
deleted file mode 100644
index a6ad76cb33b..00000000000
--- a/qa/qa/page/project/milestone/show.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Page
- module Project
- module Milestone
- class Show < Page::Base
- end
- end
- end
- end
-end
-
-QA::Page::Project::Milestone::Show.prepend_if_ee('QA::EE::Page::Project::Milestone::Show')
diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb
index 82ad08d0ff2..856c39df8b3 100644
--- a/spec/features/projects/jobs/user_browses_job_spec.rb
+++ b/spec/features/projects/jobs/user_browses_job_spec.rb
@@ -38,66 +38,6 @@ describe 'User browses a job', :js do
expect(page).to have_content('Job has been erased')
end
- shared_examples 'has collapsible sections' do
- it 'collapses the section clicked' do
- wait_for_requests
- text_to_hide = "Cloning into '/nolith/ci-tests'"
- text_to_show = 'Waiting for pod'
-
- expect(page).to have_content(text_to_hide)
- expect(page).to have_content(text_to_show)
-
- first('.js-section-start[data-section="get-sources"]').click
-
- expect(page).not_to have_content(text_to_hide)
- expect(page).to have_content(text_to_show)
- end
-
- it 'collapses the section header clicked' do
- wait_for_requests
- text_to_hide = "Cloning into '/nolith/ci-tests'"
- text_to_show = 'Waiting for pod'
-
- expect(page).to have_content(text_to_hide)
- expect(page).to have_content(text_to_show)
-
- first('.js-section-header.js-s-get-sources').click
-
- expect(page).not_to have_content(text_to_hide)
- expect(page).to have_content(text_to_show)
- end
- end
-
- context 'when job trace contains sections' do
- let!(:build) { create(:ci_build, :success, :trace_with_sections, :coverage, pipeline: pipeline) }
-
- it_behaves_like 'has collapsible sections'
- end
-
- context 'when job trace contains duplicate sections' do
- let!(:build) { create(:ci_build, :success, :trace_with_duplicate_sections, :coverage, pipeline: pipeline) }
-
- it_behaves_like 'has collapsible sections'
- end
-
- context 'when job trace contains sections' do
- let!(:build) { create(:ci_build, :success, :trace_with_duplicate_sections, :coverage, pipeline: pipeline) }
-
- it 'collapses a section' do
- wait_for_requests
- text_to_hide = "Cloning into '/nolith/ci-tests'"
- text_to_show = 'Waiting for pod'
-
- expect(page).to have_content(text_to_hide)
- expect(page).to have_content(text_to_show)
-
- first('.js-section-start[data-section="get-sources"]').click
-
- expect(page).not_to have_content(text_to_hide)
- expect(page).to have_content(text_to_show)
- end
- end
-
context 'with a failed job' do
let!(:build) { create(:ci_build, :failed, :trace_artifact, pipeline: pipeline) }
diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb
index d367f9015c7..72de05b5131 100644
--- a/spec/finders/snippets_finder_spec.rb
+++ b/spec/finders/snippets_finder_spec.rb
@@ -150,6 +150,26 @@ describe SnippetsFinder do
expect(snippets).to contain_exactly(private_project_snippet, internal_project_snippet, public_project_snippet)
end
+
+ context 'filter by author' do
+ let!(:other_user) { create(:user) }
+ let!(:other_private_project_snippet) { create(:project_snippet, :private, project: project, author: other_user) }
+ let!(:other_internal_project_snippet) { create(:project_snippet, :internal, project: project, author: other_user) }
+ let!(:other_public_project_snippet) { create(:project_snippet, :public, project: project, author: other_user) }
+
+ it 'returns all snippets for project members' do
+ project.add_developer(user)
+
+ snippets = described_class.new(user, author: other_user).execute
+
+ expect(snippets)
+ .to contain_exactly(
+ other_private_project_snippet,
+ other_internal_project_snippet,
+ other_public_project_snippet
+ )
+ end
+ end
end
context 'when the user cannot read cross project' do
diff --git a/spec/frontend/create_cluster/eks_cluster/components/cluster_form_dropdown_spec.js b/spec/frontend/create_cluster/eks_cluster/components/cluster_form_dropdown_spec.js
index e873ef0b2fa..366c2fc7b26 100644
--- a/spec/frontend/create_cluster/eks_cluster/components/cluster_form_dropdown_spec.js
+++ b/spec/frontend/create_cluster/eks_cluster/components/cluster_form_dropdown_spec.js
@@ -7,12 +7,22 @@ import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidde
describe('ClusterFormDropdown', () => {
let vm;
+ const firstItem = { name: 'item 1', value: 1 };
+ const secondItem = { name: 'item 2', value: 2 };
+ const items = [firstItem, secondItem, { name: 'item 3', value: 3 }];
beforeEach(() => {
vm = shallowMount(ClusterFormDropdown);
});
afterEach(() => vm.destroy());
+ describe('when initial value is provided', () => {
+ it('sets selectedItem to initial value', () => {
+ vm.setProps({ items, value: secondItem.value });
+ expect(vm.find(DropdownButton).props('toggleText')).toEqual(secondItem.name);
+ });
+ });
+
describe('when no item is selected', () => {
it('displays placeholder text', () => {
const placeholder = 'placeholder';
@@ -24,18 +34,19 @@ describe('ClusterFormDropdown', () => {
});
describe('when an item is selected', () => {
- const selectedItem = { name: 'Name', value: 'value' };
-
beforeEach(() => {
- vm.setData({ selectedItem });
+ vm.setProps({ items });
+ vm.findAll('.js-dropdown-item')
+ .at(1)
+ .trigger('click');
});
it('displays selected item label', () => {
- expect(vm.find(DropdownButton).props('toggleText')).toEqual(selectedItem.name);
+ expect(vm.find(DropdownButton).props('toggleText')).toEqual(secondItem.name);
});
it('sets selected value to dropdown hidden input', () => {
- expect(vm.find(DropdownHiddenInput).props('value')).toEqual(selectedItem.value);
+ expect(vm.find(DropdownHiddenInput).props('value')).toEqual(secondItem.value);
});
});
@@ -124,9 +135,7 @@ describe('ClusterFormDropdown', () => {
});
it('it filters results by search query', () => {
- const secondItem = { name: 'second item' };
- const items = [{ name: 'first item' }, secondItem];
- const searchQuery = 'second';
+ const searchQuery = secondItem.name;
vm.setProps({ items });
vm.setData({ searchQuery });
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 cc7c6735a80..df214442369 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
@@ -14,12 +14,16 @@ describe('EksClusterConfigurationForm', () => {
let store;
let actions;
let state;
+ let rolesState;
let regionsState;
let vpcsState;
let subnetsState;
+ let keyPairsState;
let vpcsActions;
+ let rolesActions;
let regionsActions;
let subnetsActions;
+ let keyPairsActions;
let vm;
beforeEach(() => {
@@ -28,16 +32,27 @@ describe('EksClusterConfigurationForm', () => {
setRegion: jest.fn(),
setVpc: jest.fn(),
setSubnet: jest.fn(),
+ setRole: jest.fn(),
+ setKeyPair: jest.fn(),
};
regionsActions = {
fetchItems: jest.fn(),
};
+ keyPairsActions = {
+ fetchItems: jest.fn(),
+ };
vpcsActions = {
fetchItems: jest.fn(),
};
subnetsActions = {
fetchItems: jest.fn(),
};
+ rolesActions = {
+ fetchItems: jest.fn(),
+ };
+ rolesState = {
+ ...clusterDropdownStoreState(),
+ };
regionsState = {
...clusterDropdownStoreState(),
};
@@ -47,6 +62,9 @@ describe('EksClusterConfigurationForm', () => {
subnetsState = {
...clusterDropdownStoreState(),
};
+ keyPairsState = {
+ ...clusterDropdownStoreState(),
+ };
store = new Vuex.Store({
state,
actions,
@@ -66,6 +84,16 @@ describe('EksClusterConfigurationForm', () => {
state: subnetsState,
actions: subnetsActions,
},
+ roles: {
+ namespaced: true,
+ state: rolesState,
+ actions: rolesActions,
+ },
+ keyPairs: {
+ namespaced: true,
+ state: keyPairsState,
+ actions: keyPairsActions,
+ },
},
});
});
@@ -82,13 +110,37 @@ describe('EksClusterConfigurationForm', () => {
});
const findRegionDropdown = () => vm.find(RegionDropdown);
+ const findKeyPairDropdown = () => vm.find('[field-id="eks-key-pair"]');
const findVpcDropdown = () => vm.find('[field-id="eks-vpc"]');
const findSubnetDropdown = () => vm.find('[field-id="eks-subnet"]');
+ const findRoleDropdown = () => vm.find('[field-id="eks-role"]');
describe('when mounted', () => {
it('fetches available regions', () => {
expect(regionsActions.fetchItems).toHaveBeenCalled();
});
+
+ it('fetches available roles', () => {
+ expect(rolesActions.fetchItems).toHaveBeenCalled();
+ });
+ });
+
+ it('sets isLoadingRoles to RoleDropdown loading property', () => {
+ rolesState.isLoadingItems = true;
+
+ return Vue.nextTick().then(() => {
+ expect(findRoleDropdown().props('loading')).toBe(rolesState.isLoadingItems);
+ });
+ });
+
+ it('sets roles to RoleDropdown items property', () => {
+ expect(findRoleDropdown().props('items')).toBe(rolesState.items);
+ });
+
+ it('sets RoleDropdown hasErrors to true when loading roles failed', () => {
+ rolesState.loadingItemsError = new Error();
+
+ expect(findRoleDropdown().props('hasErrors')).toEqual(true);
});
it('sets isLoadingRegions to RegionDropdown loading property', () => {
@@ -107,6 +159,36 @@ describe('EksClusterConfigurationForm', () => {
expect(findRegionDropdown().props('error')).toBe(regionsState.loadingItemsError);
});
+ it('disables KeyPairDropdown when no region is selected', () => {
+ expect(findKeyPairDropdown().props('disabled')).toBe(true);
+ });
+
+ it('enables KeyPairDropdown when no region is selected', () => {
+ state.selectedRegion = { name: 'west-1 ' };
+
+ return Vue.nextTick().then(() => {
+ expect(findKeyPairDropdown().props('disabled')).toBe(false);
+ });
+ });
+
+ it('sets isLoadingKeyPairs to KeyPairDropdown loading property', () => {
+ keyPairsState.isLoadingItems = true;
+
+ return Vue.nextTick().then(() => {
+ expect(findKeyPairDropdown().props('loading')).toBe(keyPairsState.isLoadingItems);
+ });
+ });
+
+ it('sets keyPairs to KeyPairDropdown items property', () => {
+ expect(findKeyPairDropdown().props('items')).toBe(keyPairsState.items);
+ });
+
+ it('sets KeyPairDropdown hasErrors to true when loading key pairs fails', () => {
+ keyPairsState.loadingItemsError = new Error();
+
+ expect(findKeyPairDropdown().props('hasErrors')).toEqual(true);
+ });
+
it('disables VpcDropdown when no region is selected', () => {
expect(findVpcDropdown().props('disabled')).toBe(true);
});
@@ -131,8 +213,10 @@ describe('EksClusterConfigurationForm', () => {
expect(findVpcDropdown().props('items')).toBe(vpcsState.items);
});
- it('sets loadingVpcsError to VpcDropdown hasErrors property', () => {
- expect(findVpcDropdown().props('hasErrors')).toBe(vpcsState.loadingItemsError);
+ it('sets VpcDropdown hasErrors to true when loading vpcs fails', () => {
+ vpcsState.loadingItemsError = new Error();
+
+ expect(findVpcDropdown().props('hasErrors')).toEqual(true);
});
it('disables SubnetDropdown when no vpc is selected', () => {
@@ -159,8 +243,10 @@ describe('EksClusterConfigurationForm', () => {
expect(findSubnetDropdown().props('items')).toBe(subnetsState.items);
});
- it('sets loadingSubnetsError to SubnetDropdown hasErrors property', () => {
- expect(findSubnetDropdown().props('hasErrors')).toBe(subnetsState.loadingItemsError);
+ it('sets SubnetDropdown hasErrors to true when loading subnets fails', () => {
+ subnetsState.loadingItemsError = new Error();
+
+ expect(findSubnetDropdown().props('hasErrors')).toEqual(true);
});
describe('when region is selected', () => {
@@ -177,6 +263,14 @@ describe('EksClusterConfigurationForm', () => {
it('fetches available vpcs', () => {
expect(vpcsActions.fetchItems).toHaveBeenCalledWith(expect.anything(), { region }, undefined);
});
+
+ it('fetches available key pairs', () => {
+ expect(keyPairsActions.fetchItems).toHaveBeenCalledWith(
+ expect.anything(),
+ { region },
+ undefined,
+ );
+ });
});
describe('when vpc is selected', () => {
@@ -206,4 +300,28 @@ describe('EksClusterConfigurationForm', () => {
expect(actions.setSubnet).toHaveBeenCalledWith(expect.anything(), { subnet }, undefined);
});
});
+
+ describe('when role is selected', () => {
+ const role = { name: 'admin' };
+
+ beforeEach(() => {
+ findRoleDropdown().vm.$emit('input', role);
+ });
+
+ it('dispatches setRole action', () => {
+ expect(actions.setRole).toHaveBeenCalledWith(expect.anything(), { role }, undefined);
+ });
+ });
+
+ describe('when key pair is selected', () => {
+ const keyPair = { name: 'key pair' };
+
+ beforeEach(() => {
+ findKeyPairDropdown().vm.$emit('input', keyPair);
+ });
+
+ it('dispatches setKeyPair action', () => {
+ expect(actions.setKeyPair).toHaveBeenCalledWith(expect.anything(), { keyPair }, undefined);
+ });
+ });
});
diff --git a/spec/frontend/create_cluster/eks_cluster/components/role_name_dropdown_spec.js b/spec/frontend/create_cluster/eks_cluster/components/role_name_dropdown_spec.js
deleted file mode 100644
index 657637c1b56..00000000000
--- a/spec/frontend/create_cluster/eks_cluster/components/role_name_dropdown_spec.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-
-import ClusterFormDropdown from '~/create_cluster/eks_cluster/components/cluster_form_dropdown.vue';
-import RoleNameDropdown from '~/create_cluster/eks_cluster/components/role_name_dropdown.vue';
-
-describe('RoleNameDropdown', () => {
- let vm;
-
- beforeEach(() => {
- vm = shallowMount(RoleNameDropdown);
- });
- afterEach(() => vm.destroy());
-
- it('renders a cluster-form-dropdown', () => {
- expect(vm.find(ClusterFormDropdown).exists()).toBe(true);
- });
-
- it('sets roles to cluster-form-dropdown items property', () => {
- const roles = [{ name: 'basic' }];
-
- vm.setProps({ roles });
-
- expect(vm.find(ClusterFormDropdown).props('items')).toEqual(roles);
- });
-
- it('sets a loading text', () => {
- expect(vm.find(ClusterFormDropdown).props('loadingText')).toEqual('Loading IAM Roles');
- });
-
- it('sets a placeholder', () => {
- expect(vm.find(ClusterFormDropdown).props('placeholder')).toEqual('Select role name');
- });
-
- it('sets an empty results text', () => {
- expect(vm.find(ClusterFormDropdown).props('emptyText')).toEqual('No IAM Roles found');
- });
-
- it('sets a search field placeholder', () => {
- expect(vm.find(ClusterFormDropdown).props('searchFieldPlaceholder')).toEqual(
- 'Search IAM Roles',
- );
- });
-});
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 893c657e699..aa7ced81e0d 100644
--- a/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js
+++ b/spec/frontend/create_cluster/eks_cluster/store/actions_spec.js
@@ -2,24 +2,36 @@ import testAction from 'helpers/vuex_action_helper';
import createState from '~/create_cluster/eks_cluster/store/state';
import * as actions from '~/create_cluster/eks_cluster/store/actions';
-import { SET_REGION, SET_VPC, SET_SUBNET } from '~/create_cluster/eks_cluster/store/mutation_types';
+import {
+ SET_REGION,
+ SET_VPC,
+ SET_KEY_PAIR,
+ SET_SUBNET,
+ SET_ROLE,
+} from '~/create_cluster/eks_cluster/store/mutation_types';
describe('EKS Cluster Store Actions', () => {
let region;
let vpc;
let subnet;
+ let role;
+ let keyPair;
beforeEach(() => {
region = { name: 'regions-1' };
vpc = { name: 'vpc-1' };
subnet = { name: 'subnet-1' };
+ role = { name: 'role-1' };
+ keyPair = { name: 'key-pair-1' };
});
it.each`
- action | mutation | payload | payloadDescription
- ${'setRegion'} | ${SET_REGION} | ${{ region }} | ${'region'}
- ${'setVpc'} | ${SET_VPC} | ${{ vpc }} | ${'vpc'}
- ${'setSubnet'} | ${SET_SUBNET} | ${{ subnet }} | ${'subnet'}
+ action | mutation | payload | payloadDescription
+ ${'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'}
`(`$action commits $mutation with $payloadDescription payload`, data => {
const { action, mutation, payload } = data;
diff --git a/spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js b/spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js
index 38199471f79..966406386ac 100644
--- a/spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js
+++ b/spec/frontend/create_cluster/eks_cluster/store/mutations_spec.js
@@ -1,4 +1,10 @@
-import { SET_REGION, SET_VPC, SET_SUBNET } from '~/create_cluster/eks_cluster/store/mutation_types';
+import {
+ SET_REGION,
+ SET_VPC,
+ SET_KEY_PAIR,
+ SET_SUBNET,
+ SET_ROLE,
+} from '~/create_cluster/eks_cluster/store/mutation_types';
import createState from '~/create_cluster/eks_cluster/store/state';
import mutations from '~/create_cluster/eks_cluster/store/mutations';
@@ -7,20 +13,26 @@ describe('Create EKS cluster store mutations', () => {
let region;
let vpc;
let subnet;
+ let role;
+ let keyPair;
beforeEach(() => {
region = { name: 'regions-1' };
vpc = { name: 'vpc-1' };
subnet = { name: 'subnet-1' };
+ role = { name: 'role-1' };
+ keyPair = { name: 'key pair' };
state = createState();
});
it.each`
- mutation | mutatedProperty | payload | expectedValue | expectedValueDescription
- ${SET_REGION} | ${'selectedRegion'} | ${{ region }} | ${region} | ${'selected region payload'}
- ${SET_VPC} | ${'selectedVpc'} | ${{ vpc }} | ${vpc} | ${'selected vpc payload'}
- ${SET_SUBNET} | ${'selectedSubnet'} | ${{ subnet }} | ${subnet} | ${'selected sybnet payload'}
+ mutation | mutatedProperty | payload | expectedValue | expectedValueDescription
+ ${SET_ROLE} | ${'selectedRole'} | ${{ role }} | ${role} | ${'selected role payload'}
+ ${SET_REGION} | ${'selectedRegion'} | ${{ region }} | ${region} | ${'selected region payload'}
+ ${SET_KEY_PAIR} | ${'selectedKeyPair'} | ${{ keyPair }} | ${keyPair} | ${'selected key pair payload'}
+ ${SET_VPC} | ${'selectedVpc'} | ${{ vpc }} | ${vpc} | ${'selected vpc payload'}
+ ${SET_SUBNET} | ${'selectedSubnet'} | ${{ subnet }} | ${subnet} | ${'selected sybnet payload'}
`(`$mutation sets $mutatedProperty to $expectedValueDescription`, data => {
const { mutation, mutatedProperty, payload, expectedValue } = data;
diff --git a/spec/javascripts/jobs/components/job_log_spec.js b/spec/javascripts/jobs/components/job_log_spec.js
index 24bb6b9a48b..dd58f234394 100644
--- a/spec/javascripts/jobs/components/job_log_spec.js
+++ b/spec/javascripts/jobs/components/job_log_spec.js
@@ -3,7 +3,6 @@ import component from '~/jobs/components/job_log.vue';
import createStore from '~/jobs/store';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { resetStore } from '../store/helpers';
-import { logWithCollapsibleSections } from '../mock_data';
describe('Job Log', () => {
const Component = Vue.extend(component);
@@ -63,60 +62,4 @@ describe('Job Log', () => {
expect(vm.$el.querySelector('.js-log-animation')).toBeNull();
});
});
-
- describe('Collapsible sections', () => {
- beforeEach(() => {
- vm = mountComponentWithStore(Component, {
- props: {
- trace: logWithCollapsibleSections.html,
- isComplete: true,
- },
- store,
- });
- });
-
- it('renders open arrow', () => {
- expect(vm.$el.querySelector('.fa-caret-down')).not.toBeNull();
- });
-
- it('toggles hidden class to the sibilings rows when arrow is clicked', done => {
- vm.$nextTick()
- .then(() => {
- const { section } = vm.$el.querySelector('.js-section-start').dataset;
- vm.$el.querySelector('.js-section-start').click();
-
- vm.$el.querySelectorAll(`.js-s-${section}:not(.js-section-header)`).forEach(el => {
- expect(el.classList.contains('hidden')).toEqual(true);
- });
-
- vm.$el.querySelector('.js-section-start').click();
-
- vm.$el.querySelectorAll(`.js-s-${section}:not(.js-section-header)`).forEach(el => {
- expect(el.classList.contains('hidden')).toEqual(false);
- });
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('toggles hidden class to the sibilings rows when header section is clicked', done => {
- vm.$nextTick()
- .then(() => {
- const { section } = vm.$el.querySelector('.js-section-header').dataset;
- vm.$el.querySelector('.js-section-header').click();
-
- vm.$el.querySelectorAll(`.js-s-${section}:not(.js-section-header)`).forEach(el => {
- expect(el.classList.contains('hidden')).toEqual(true);
- });
-
- vm.$el.querySelector('.js-section-header').click();
-
- vm.$el.querySelectorAll(`.js-s-${section}:not(.js-section-header)`).forEach(el => {
- expect(el.classList.contains('hidden')).toEqual(false);
- });
- })
- .then(done)
- .catch(done.fail);
- });
- });
});
diff --git a/spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js b/spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js
index f8271866ca1..9c2deca585b 100644
--- a/spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js
+++ b/spec/javascripts/vue_shared/components/project_selector/project_selector_spec.js
@@ -4,9 +4,11 @@ import ProjectSelector from '~/vue_shared/components/project_selector/project_se
import ProjectListItem from '~/vue_shared/components/project_selector/project_list_item.vue';
import { GlSearchBoxByType } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
+import { mount, createLocalVue } from '@vue/test-utils';
import { trimText } from 'spec/helpers/text_helper';
+const localVue = createLocalVue();
+
describe('ProjectSelector component', () => {
let wrapper;
let vm;
@@ -22,6 +24,7 @@ describe('ProjectSelector component', () => {
jasmine.clock().install();
wrapper = mount(Vue.extend(ProjectSelector), {
+ localVue,
propsData: {
projectSearchResults: searchResults,
selectedProjects: selected,
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index 4d13b32d4a1..2280d8ca9d4 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -770,47 +770,6 @@ describe API::Internal::Base do
end
end
- describe 'GET /internal/merge_request_urls' do
- let(:repo_name) { "#{project.full_path}" }
- let(:changes) { URI.escape("#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/new_branch") }
-
- before do
- project.add_developer(user)
- end
-
- it 'returns link to create new merge request' do
- get api("/internal/merge_request_urls?project=#{repo_name}&changes=#{changes}"), params: { secret_token: secret_token }
-
- expect(json_response).to match [{
- "branch_name" => "new_branch",
- "url" => "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch",
- "new_merge_request" => true
- }]
- end
-
- it 'returns empty array if printing_merge_request_link_enabled is false' do
- project.update!(printing_merge_request_link_enabled: false)
-
- get api("/internal/merge_request_urls?project=#{repo_name}&changes=#{changes}"), params: { secret_token: secret_token }
-
- expect(json_response).to eq([])
- end
-
- context 'with a gl_repository parameter' do
- let(:gl_repository) { "project-#{project.id}" }
-
- it 'returns link to create new merge request' do
- get api("/internal/merge_request_urls?gl_repository=#{gl_repository}&changes=#{changes}"), params: { secret_token: secret_token }
-
- expect(json_response).to match [{
- "branch_name" => "new_branch",
- "url" => "http://#{Gitlab.config.gitlab.host}/#{project.full_path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch",
- "new_merge_request" => true
- }]
- end
- end
- end
-
# TODO: Uncomment when the end-point is reenabled
# describe 'POST /notify_post_receive' do
# let(:valid_params) do
@@ -951,6 +910,19 @@ describe API::Internal::Base do
expect(json_response['messages']).to include(build_basic_message(message))
end
+ it 'returns the link to an existing merge request when it exists' do
+ merge_request = create(:merge_request, source_project: project, source_branch: branch_name, target_branch: 'master')
+
+ post api('/internal/post_receive'), params: valid_params
+
+ message = <<~MESSAGE.strip
+ View merge request for feature:
+ #{project_merge_request_url(project, merge_request)}
+ MESSAGE
+
+ expect(json_response['messages']).to include(build_basic_message(message))
+ end
+
it 'returns no merge request messages if printing_merge_request_link_enabled is false' do
project.update!(printing_merge_request_link_enabled: false)
diff --git a/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb b/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
index 1ead9ec2738..f15128d3e13 100644
--- a/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
@@ -369,6 +369,48 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
end
end
+ context 'on a protected branch with protected branches defined using wildcards' do
+ before do
+ create(:protected_branch, project: project, name: '*-stable')
+ end
+
+ let(:data) do
+ Gitlab::DataBuilder::Push.build(
+ project: project,
+ user: user,
+ ref: '1-stable'
+ )
+ end
+
+ context 'pushing tags' do
+ let(:data) do
+ Gitlab::DataBuilder::Push.build(
+ project: project,
+ user: user,
+ ref: "#{Gitlab::Git::TAG_REF_PREFIX}test"
+ )
+ end
+
+ it_behaves_like "triggered #{service_name} service", event_type: "push"
+ end
+
+ context 'notification enabled only for default branch' do
+ it_behaves_like "untriggered #{service_name} service", event_type: "push", branches_to_be_notified: "default"
+ end
+
+ context 'notification enabled only for protected branches' do
+ it_behaves_like "triggered #{service_name} service", event_type: "push", branches_to_be_notified: "protected"
+ end
+
+ context 'notification enabled only for default and protected branches' do
+ it_behaves_like "triggered #{service_name} service", event_type: "push", branches_to_be_notified: "default_and_protected"
+ end
+
+ context 'notification enabled for all branches' do
+ it_behaves_like "triggered #{service_name} service", event_type: "push", branches_to_be_notified: "all"
+ end
+ end
+
context 'on a neither protected nor default branch' do
let(:data) do
Gitlab::DataBuilder::Push.build(
@@ -570,6 +612,36 @@ RSpec.shared_examples 'slack or mattermost notifications' do |service_name|
end
end
+ context 'on a protected branch with protected branches defined usin wildcards' do
+ before do
+ create(:protected_branch, project: project, name: '*-stable')
+ end
+
+ let(:pipeline) do
+ create(:ci_pipeline,
+ project: project, status: :failed,
+ sha: project.commit.sha, ref: '1-stable')
+ end
+
+ let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
+
+ context 'notification enabled only for default branch' do
+ it_behaves_like "untriggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "default"
+ end
+
+ context 'notification enabled only for protected branches' do
+ it_behaves_like "triggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "protected"
+ end
+
+ context 'notification enabled only for default and protected branches' do
+ it_behaves_like "triggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "default_and_protected"
+ end
+
+ context 'notification enabled for all branches' do
+ it_behaves_like "triggered #{service_name} service", event_type: "pipeline", branches_to_be_notified: "all"
+ end
+ end
+
context 'on a neither protected nor default branch' do
let(:pipeline) do
create(:ci_pipeline,