summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/groups/components/item_actions.vue6
-rw-r--r--app/assets/javascripts/groups/components/item_stats.vue14
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/index.vue8
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/modal.vue8
-rw-r--r--app/assets/javascripts/ide/components/repo_commit_section.vue2
-rw-r--r--app/assets/javascripts/ide/components/repo_edit_button.vue2
-rw-r--r--app/assets/javascripts/profile/account/components/delete_account_modal.vue111
-rw-r--r--app/assets/javascripts/profile/account/index.js8
-rw-r--r--app/assets/javascripts/projects/project_new.js7
-rw-r--r--app/assets/javascripts/vue_shared/components/modal.vue30
-rw-r--r--app/assets/javascripts/vue_shared/components/recaptcha_modal.vue2
-rw-r--r--app/models/concerns/deployment_platform.rb7
-rw-r--r--app/views/profiles/accounts/show.html.haml6
-rw-r--r--app/views/projects/clusters/_advanced_settings.html.haml4
-rw-r--r--app/views/projects/clusters/_banner.html.haml13
-rw-r--r--app/views/projects/clusters/_cluster.html.haml2
-rw-r--r--app/views/projects/clusters/_enabled.html.haml17
-rw-r--r--app/views/projects/clusters/_integration_form.html.haml33
-rw-r--r--app/views/projects/clusters/gcp/_show.html.haml4
-rw-r--r--app/views/projects/clusters/index.html.haml2
-rw-r--r--app/views/projects/clusters/show.html.haml6
-rw-r--r--app/views/projects/clusters/user/_show.html.haml4
-rw-r--r--changelogs/unreleased/40228-verify-integrity-of-repositories.yml5
-rw-r--r--changelogs/unreleased/feature-api_runners_online.yml5
-rw-r--r--changelogs/unreleased/jivl-fix-import-project-url-bug.yml5
-rw-r--r--changelogs/unreleased/ldap_username_attributes.yml5
-rw-r--r--changelogs/unreleased/winh-modal-target-id.yml5
-rw-r--r--db/post_migrate/20171221140220_schedule_issues_closed_at_type_change.rb2
-rw-r--r--doc/administration/raketasks/check.md13
-rw-r--r--doc/api/runners.md40
-rw-r--r--doc/development/gitaly.md23
-rw-r--r--lib/api/entities.rb2
-rw-r--r--lib/gitlab/encoding_helper.rb26
-rw-r--r--lib/gitlab/git.rb2
-rw-r--r--lib/gitlab/git/blob.rb31
-rw-r--r--lib/gitlab/git/operation_service.rb5
-rw-r--r--lib/gitlab/git/repository.rb9
-rw-r--r--lib/gitlab/ldap/adapter.rb2
-rw-r--r--lib/gitlab/ldap/config.rb2
-rw-r--r--lib/gitlab/ldap/person.rb36
-rw-r--r--lib/gitlab/setup_helper.rb61
-rw-r--r--lib/gitlab/shell.rb13
-rw-r--r--lib/tasks/gitlab/check.rake41
-rw-r--r--lib/tasks/gitlab/git.rake35
-rw-r--r--lib/tasks/gitlab/gitaly.rake57
-rw-r--r--lib/tasks/gitlab/task_helpers.rb2
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb6
-rw-r--r--spec/features/projects/clusters/user_spec.rb8
-rw-r--r--spec/javascripts/groups/components/item_actions_spec.js12
-rw-r--r--spec/javascripts/profile/account/components/delete_account_modal_spec.js8
-rw-r--r--spec/javascripts/projects/project_new_spec.js32
-rw-r--r--spec/javascripts/repo/components/new_dropdown/index_spec.js13
-rw-r--r--spec/javascripts/vue_shared/components/modal_spec.js62
-rw-r--r--spec/lib/gitlab/encoding_helper_spec.rb18
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb15
-rw-r--r--spec/lib/gitlab/ldap/adapter_spec.rb10
-rw-r--r--spec/lib/gitlab/ldap/person_spec.rb73
-rw-r--r--spec/lib/gitlab/o_auth/user_spec.rb20
-rw-r--r--spec/support/test_env.rb7
-rw-r--r--spec/tasks/gitlab/git_rake_spec.rb38
60 files changed, 696 insertions, 349 deletions
diff --git a/app/assets/javascripts/groups/components/item_actions.vue b/app/assets/javascripts/groups/components/item_actions.vue
index a685960d862..0dd0783ce06 100644
--- a/app/assets/javascripts/groups/components/item_actions.vue
+++ b/app/assets/javascripts/groups/components/item_actions.vue
@@ -45,11 +45,9 @@ export default {
onLeaveGroup() {
this.modalStatus = true;
},
- leaveGroup(leaveConfirmed) {
+ leaveGroup() {
this.modalStatus = false;
- if (leaveConfirmed) {
- eventHub.$emit('leaveGroup', this.group, this.parentGroup);
- }
+ eventHub.$emit('leaveGroup', this.group, this.parentGroup);
},
},
};
diff --git a/app/assets/javascripts/groups/components/item_stats.vue b/app/assets/javascripts/groups/components/item_stats.vue
index 803dc63d39c..2e42fb6c9a6 100644
--- a/app/assets/javascripts/groups/components/item_stats.vue
+++ b/app/assets/javascripts/groups/components/item_stats.vue
@@ -42,28 +42,28 @@ export default {
v-if="isGroup"
css-class="number-subgroups"
icon-name="folder"
- :title="s__('Subgroups')"
- :value=item.subgroupCount
+ :title="__('Subgroups')"
+ :value="item.subgroupCount"
/>
<item-stats-value
v-if="isGroup"
css-class="number-projects"
icon-name="bookmark"
- :title="s__('Projects')"
- :value=item.projectCount
+ :title="__('Projects')"
+ :value="item.projectCount"
/>
<item-stats-value
v-if="isGroup"
css-class="number-users"
icon-name="users"
- :title="s__('Members')"
- :value=item.memberCount
+ :title="__('Members')"
+ :value="item.memberCount"
/>
<item-stats-value
v-if="isProject"
css-class="project-stars"
icon-name="star"
- :value=item.starCount
+ :value="item.starCount"
/>
<item-stats-value
css-class="item-visibility"
diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue
index 6e67e99a70f..d475813c4f7 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/index.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue
@@ -32,10 +32,10 @@
methods: {
createNewItem(type) {
this.modalType = type;
- this.toggleModalOpen();
+ this.openModal = true;
},
- toggleModalOpen() {
- this.openModal = !this.openModal;
+ hideModal() {
+ this.openModal = false;
},
},
};
@@ -95,7 +95,7 @@
:branch-id="branch"
:path="path"
:parent="parent"
- @toggle="toggleModalOpen"
+ @hide="hideModal"
/>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
index a0650d37690..0312f56efbd 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
@@ -43,10 +43,10 @@
type: this.type,
});
- this.toggleModalOpen();
+ this.hideModal();
},
- toggleModalOpen() {
- this.$emit('toggle');
+ hideModal() {
+ this.$emit('hide');
},
},
computed: {
@@ -86,7 +86,7 @@
:title="modalTitle"
:primary-button-label="buttonLabel"
kind="success"
- @toggle="toggleModalOpen"
+ @cancel="hideModal"
@submit="createEntryInStore"
>
<form
diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue
index 470db2c9650..979721dcb5a 100644
--- a/app/assets/javascripts/ide/components/repo_commit_section.vue
+++ b/app/assets/javascripts/ide/components/repo_commit_section.vue
@@ -110,7 +110,7 @@ export default {
kind="primary"
:title="__('Branch has changed')"
:text="__('This branch has changed since you started editing. Would you like to create a new branch?')"
- @toggle="showNewBranchModal = false"
+ @cancel="showNewBranchModal = false"
@submit="makeCommit(true)"
/>
<commit-files-list
diff --git a/app/assets/javascripts/ide/components/repo_edit_button.vue b/app/assets/javascripts/ide/components/repo_edit_button.vue
index 37bd9003e96..42d5d709209 100644
--- a/app/assets/javascripts/ide/components/repo_edit_button.vue
+++ b/app/assets/javascripts/ide/components/repo_edit_button.vue
@@ -50,7 +50,7 @@ export default {
kind="warning"
:title="__('Are you sure?')"
:text="__('Are you sure you want to discard your changes?')"
- @toggle="closeDiscardPopup"
+ @cancel="closeDiscardPopup"
@submit="toggleEditMode(true)"
/>
</div>
diff --git a/app/assets/javascripts/profile/account/components/delete_account_modal.vue b/app/assets/javascripts/profile/account/components/delete_account_modal.vue
index 78be6b6e884..36ad618aa46 100644
--- a/app/assets/javascripts/profile/account/components/delete_account_modal.vue
+++ b/app/assets/javascripts/profile/account/components/delete_account_modal.vue
@@ -1,7 +1,7 @@
<script>
- import modal from '../../../vue_shared/components/modal.vue';
- import { __, s__, sprintf } from '../../../locale';
- import csrf from '../../../lib/utils/csrf';
+ import modal from '~/vue_shared/components/modal.vue';
+ import { __, s__, sprintf } from '~/locale';
+ import csrf from '~/lib/utils/csrf';
export default {
props: {
@@ -22,7 +22,6 @@
return {
enteredPassword: '',
enteredUsername: '',
- isOpen: false,
};
},
components: {
@@ -69,78 +68,58 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
return this.enteredUsername === this.username;
},
- onSubmit(status) {
- if (status) {
- if (!this.canSubmit()) {
- return;
- }
-
- this.$refs.form.submit();
- }
-
- this.toggleOpen(false);
- },
- toggleOpen(isOpen) {
- this.isOpen = isOpen;
+ onSubmit() {
+ this.$refs.form.submit();
},
},
};
</script>
<template>
- <div>
- <modal
- v-if="isOpen"
- :title="s__('Profiles|Delete your account?')"
- :text="text"
- :kind="`danger ${!canSubmit() && 'disabled'}`"
- :primary-button-label="s__('Profiles|Delete account')"
- @toggle="toggleOpen"
- @submit="onSubmit">
-
- <template slot="body" slot-scope="props">
- <p v-html="props.text"></p>
+ <modal
+ id="delete-account-modal"
+ :title="s__('Profiles|Delete your account?')"
+ :text="text"
+ kind="danger"
+ :primary-button-label="s__('Profiles|Delete account')"
+ @submit="onSubmit"
+ :submit-disabled="!canSubmit()">
- <form
- ref="form"
- :action="actionUrl"
- method="post">
+ <template slot="body" slot-scope="props">
+ <p v-html="props.text"></p>
- <input
- type="hidden"
- name="_method"
- value="delete" />
- <input
- type="hidden"
- name="authenticity_token"
- :value="csrfToken" />
+ <form
+ ref="form"
+ :action="actionUrl"
+ method="post">
- <p id="input-label" v-html="inputLabel"></p>
+ <input
+ type="hidden"
+ name="_method"
+ value="delete" />
+ <input
+ type="hidden"
+ name="authenticity_token"
+ :value="csrfToken" />
- <input
- v-if="confirmWithPassword"
- name="password"
- class="form-control"
- type="password"
- v-model="enteredPassword"
- aria-labelledby="input-label" />
- <input
- v-else
- name="username"
- class="form-control"
- type="text"
- v-model="enteredUsername"
- aria-labelledby="input-label" />
- </form>
- </template>
+ <p id="input-label" v-html="inputLabel"></p>
- </modal>
+ <input
+ v-if="confirmWithPassword"
+ name="password"
+ class="form-control"
+ type="password"
+ v-model="enteredPassword"
+ aria-labelledby="input-label" />
+ <input
+ v-else
+ name="username"
+ class="form-control"
+ type="text"
+ v-model="enteredUsername"
+ aria-labelledby="input-label" />
+ </form>
+ </template>
- <button
- type="button"
- class="btn btn-danger"
- @click="toggleOpen(true)">
- {{ s__('Profiles|Delete account') }}
- </button>
- </div>
+ </modal>
</template>
diff --git a/app/assets/javascripts/profile/account/index.js b/app/assets/javascripts/profile/account/index.js
index 635056e0eeb..a93bc935dd0 100644
--- a/app/assets/javascripts/profile/account/index.js
+++ b/app/assets/javascripts/profile/account/index.js
@@ -1,7 +1,12 @@
import Vue from 'vue';
+import Translate from '~/vue_shared/translate';
+
import deleteAccountModal from './components/delete_account_modal.vue';
+Vue.use(Translate);
+
+const deleteAccountButton = document.getElementById('delete-account-button');
const deleteAccountModalEl = document.getElementById('delete-account-modal');
// eslint-disable-next-line no-new
new Vue({
@@ -9,6 +14,9 @@ new Vue({
components: {
deleteAccountModal,
},
+ mounted() {
+ deleteAccountButton.classList.remove('disabled');
+ },
render(createElement) {
return createElement('delete-account-modal', {
props: {
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index 3ecc0c2a6e5..4710e70d619 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -1,6 +1,7 @@
let hasUserDefinedProjectPath = false;
-const deriveProjectPathFromUrl = ($projectImportUrl, $projectPath) => {
+const deriveProjectPathFromUrl = ($projectImportUrl) => {
+ const $currentProjectPath = $projectImportUrl.parents('.toggle-import-form').find('#project_path');
if (hasUserDefinedProjectPath) {
return;
}
@@ -21,7 +22,7 @@ const deriveProjectPathFromUrl = ($projectImportUrl, $projectPath) => {
// extract everything after the last slash
const pathMatch = /\/([^/]+)$/.exec(importUrl);
if (pathMatch) {
- $projectPath.val(pathMatch[1]);
+ $currentProjectPath.val(pathMatch[1]);
}
};
@@ -96,7 +97,7 @@ const bindEvents = () => {
hasUserDefinedProjectPath = $projectPath.val().trim().length > 0;
});
- $projectImportUrl.keyup(() => deriveProjectPathFromUrl($projectImportUrl, $projectPath));
+ $projectImportUrl.keyup(() => deriveProjectPathFromUrl($projectImportUrl));
};
document.addEventListener('DOMContentLoaded', bindEvents);
diff --git a/app/assets/javascripts/vue_shared/components/modal.vue b/app/assets/javascripts/vue_shared/components/modal.vue
index 55f466b7b41..00089dfef38 100644
--- a/app/assets/javascripts/vue_shared/components/modal.vue
+++ b/app/assets/javascripts/vue_shared/components/modal.vue
@@ -3,6 +3,10 @@ export default {
name: 'modal',
props: {
+ id: {
+ type: String,
+ required: false,
+ },
title: {
type: String,
required: false,
@@ -62,11 +66,11 @@ export default {
},
methods: {
- close() {
- this.$emit('toggle', false);
+ emitCancel(event) {
+ this.$emit('cancel', event);
},
- emitSubmit(status) {
- this.$emit('submit', status);
+ emitSubmit(event) {
+ this.$emit('submit', event);
},
},
};
@@ -75,7 +79,9 @@ export default {
<template>
<div class="modal-open">
<div
- class="modal show"
+ :id="id"
+ class="modal"
+ :class="id ? '' : 'show'"
role="dialog"
tabindex="-1"
>
@@ -93,7 +99,8 @@ export default {
<button
type="button"
class="close pull-right"
- @click="close"
+ @click="emitCancel($event)"
+ data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
@@ -110,7 +117,8 @@ export default {
type="button"
class="btn pull-left"
:class="btnCancelKindClass"
- @click="close">
+ @click="emitCancel($event)"
+ data-dismiss="modal">
{{ closeButtonLabel }}
</button>
<button
@@ -119,13 +127,17 @@ export default {
class="btn pull-right js-primary-button"
:disabled="submitDisabled"
:class="btnKindClass"
- @click="emitSubmit(true)">
+ @click="emitSubmit($event)"
+ data-dismiss="modal">
{{ primaryButtonLabel }}
</button>
</div>
</div>
</div>
</div>
- <div class="modal-backdrop fade in" />
+ <div
+ v-if="!id"
+ class="modal-backdrop fade in">
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue b/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue
index 8053c65d498..16d60bb2876 100644
--- a/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue
@@ -70,7 +70,7 @@ export default {
class="recaptcha-modal js-recaptcha-modal"
:hide-footer="true"
:title="__('Please solve the reCAPTCHA')"
- @toggle="close"
+ @cancel="close"
>
<div slot="body">
<p>
diff --git a/app/models/concerns/deployment_platform.rb b/app/models/concerns/deployment_platform.rb
index e1373455e98..89d0474a596 100644
--- a/app/models/concerns/deployment_platform.rb
+++ b/app/models/concerns/deployment_platform.rb
@@ -1,8 +1,9 @@
module DeploymentPlatform
def deployment_platform
- @deployment_platform ||= find_cluster_platform_kubernetes
- @deployment_platform ||= find_kubernetes_service_integration
- @deployment_platform ||= build_cluster_and_deployment_platform
+ @deployment_platform ||=
+ find_cluster_platform_kubernetes ||
+ find_kubernetes_service_integration ||
+ build_cluster_and_deployment_platform
end
private
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index f1313b79589..79e197ad08b 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -84,11 +84,13 @@
= s_('Profiles|Deleting an account has the following effects:')
= render 'users/deletion_guidance', user: current_user
+ %button#delete-account-button.btn.btn-danger.disabled{ data: { toggle: 'modal',
+ target: '#delete-account-modal' } }
+ = s_('Profiles|Delete account')
+
#delete-account-modal{ data: { action_url: user_registration_path,
confirm_with_password: ('true' if current_user.confirm_deletion_with_password?),
username: current_user.username } }
- %button.btn.btn-danger.disabled
- = s_('Profiles|Delete account')
- else
- if @user.solo_owned_groups.present?
%p
diff --git a/app/views/projects/clusters/_advanced_settings.html.haml b/app/views/projects/clusters/_advanced_settings.html.haml
index 7032b892029..8a13713ae02 100644
--- a/app/views/projects/clusters/_advanced_settings.html.haml
+++ b/app/views/projects/clusters/_advanced_settings.html.haml
@@ -11,5 +11,5 @@
%label.text-danger
= s_('ClusterIntegration|Remove cluster integration')
%p
- = s_('ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine.')
- = link_to(s_('ClusterIntegration|Remove integration'), namespace_project_cluster_path(@project.namespace, @project, @cluster.id), method: :delete, class: 'btn btn-danger', data: { confirm: "Are you sure you want to remove cluster integration from this project? This will not delete your cluster on Google Kubernetes Engine"})
+ = s_("ClusterIntegration|Remove this cluster's configuration from this project. This will not delete your actual cluster.")
+ = link_to(s_('ClusterIntegration|Remove integration'), namespace_project_cluster_path(@project.namespace, @project, @cluster.id), method: :delete, class: 'btn btn-danger', data: { confirm: s_("ClusterIntegration|Are you sure you want to remove this cluster's integration? This will not delete your actual cluster.")})
diff --git a/app/views/projects/clusters/_banner.html.haml b/app/views/projects/clusters/_banner.html.haml
index 76a66fb92a2..26ca3307a4a 100644
--- a/app/views/projects/clusters/_banner.html.haml
+++ b/app/views/projects/clusters/_banner.html.haml
@@ -1,6 +1,6 @@
-%h4= s_('ClusterIntegration|Enable cluster integration')
-.settings-content
+%h4= s_('ClusterIntegration|Cluster integration')
+.settings-content
.hidden.js-cluster-error.alert.alert-danger.alert-block.append-bottom-10{ role: 'alert' }
= s_('ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine')
%p.js-error-reason
@@ -11,11 +11,4 @@
.hidden.js-cluster-success.alert.alert-success.alert-block.append-bottom-10{ role: 'alert' }
= s_('ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster\'s details')
- %p
- - if @cluster.enabled?
- - if can?(current_user, :update_cluster, @cluster)
- = s_('ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab\'s connection to it.')
- - else
- = s_('ClusterIntegration|Cluster integration is enabled for this project.')
- - else
- = s_('ClusterIntegration|Cluster integration is disabled for this project.')
+ %p= s_('ClusterIntegration|Control how your cluster integrates with GitLab')
diff --git a/app/views/projects/clusters/_cluster.html.haml b/app/views/projects/clusters/_cluster.html.haml
index ad696daa259..3943dfc0856 100644
--- a/app/views/projects/clusters/_cluster.html.haml
+++ b/app/views/projects/clusters/_cluster.html.haml
@@ -4,7 +4,7 @@
.table-mobile-content
= link_to cluster.name, namespace_project_cluster_path(@project.namespace, @project, cluster)
.table-section.section-30
- .table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Environment pattern")
+ .table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Environment scope")
.table-mobile-content= cluster.environment_scope
.table-section.section-30
.table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Project namespace")
diff --git a/app/views/projects/clusters/_enabled.html.haml b/app/views/projects/clusters/_enabled.html.haml
deleted file mode 100644
index 547b3c8446f..00000000000
--- a/app/views/projects/clusters/_enabled.html.haml
+++ /dev/null
@@ -1,17 +0,0 @@
-= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field|
- = form_errors(@cluster)
- .form-group.append-bottom-20
- %label.append-bottom-10
- = field.hidden_field :enabled, { class: 'js-toggle-input'}
-
- %button{ type: 'button',
- class: "js-toggle-cluster project-feature-toggle #{'is-checked' unless !@cluster.enabled?} #{'is-disabled' unless can?(current_user, :update_cluster, @cluster)}",
- "aria-label": s_("ClusterIntegration|Toggle Cluster"),
- disabled: !can?(current_user, :update_cluster, @cluster) }
- %span.toggle-icon
- = sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked')
- = sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked')
-
- - if can?(current_user, :update_cluster, @cluster)
- .form-group
- = field.submit _('Save'), class: 'btn btn-success'
diff --git a/app/views/projects/clusters/_integration_form.html.haml b/app/views/projects/clusters/_integration_form.html.haml
new file mode 100644
index 00000000000..9d593ffc021
--- /dev/null
+++ b/app/views/projects/clusters/_integration_form.html.haml
@@ -0,0 +1,33 @@
+= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field|
+ = form_errors(@cluster)
+ .form-group.append-bottom-20
+ %h5= s_('ClusterIntegration|Integration status')
+ %p
+ - if @cluster.enabled?
+ - if can?(current_user, :update_cluster, @cluster)
+ = s_('ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab\'s connection to it.')
+ - else
+ = s_('ClusterIntegration|Cluster integration is enabled for this project.')
+ - else
+ = s_('ClusterIntegration|Cluster integration is disabled for this project.')
+ %label.append-bottom-10
+ = field.hidden_field :enabled, { class: 'js-toggle-input'}
+
+ %button{ type: 'button',
+ class: "js-toggle-cluster project-feature-toggle #{'is-checked' unless !@cluster.enabled?} #{'is-disabled' unless can?(current_user, :update_cluster, @cluster)}",
+ "aria-label": s_("ClusterIntegration|Toggle Cluster"),
+ disabled: !can?(current_user, :update_cluster, @cluster) }
+ %span.toggle-icon
+ = sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked')
+ = sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked')
+
+ .form-group
+ %h5= s_('ClusterIntegration|Environment scope')
+ %p
+ = s_("ClusterIntegration|Choose which of your project's environments will use this cluster.")
+ = link_to s_("ClusterIntegration|Learn more about environments"), help_page_path('ci/environments')
+ = field.text_field :environment_scope, class: 'form-control js-select-on-focus', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope')
+
+ - if can?(current_user, :update_cluster, @cluster)
+ .form-group
+ = field.submit _('Save changes'), class: 'btn btn-success'
diff --git a/app/views/projects/clusters/gcp/_show.html.haml b/app/views/projects/clusters/gcp/_show.html.haml
index bde85aed341..f3122a1bf47 100644
--- a/app/views/projects/clusters/gcp/_show.html.haml
+++ b/app/views/projects/clusters/gcp/_show.html.haml
@@ -9,10 +9,6 @@
= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field|
= form_errors(@cluster)
- .form-group
- = field.label :environment_scope, s_('ClusterIntegration|Environment scope')
- = field.text_field :environment_scope, class: 'form-control js-select-on-focus', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope')
-
= field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field|
.form-group
= platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL')
diff --git a/app/views/projects/clusters/index.html.haml b/app/views/projects/clusters/index.html.haml
index bec512be91c..74dbe859eea 100644
--- a/app/views/projects/clusters/index.html.haml
+++ b/app/views/projects/clusters/index.html.haml
@@ -13,7 +13,7 @@
.table-section.section-30{ role: "rowheader" }
= s_("ClusterIntegration|Cluster")
.table-section.section-30{ role: "rowheader" }
- = s_("ClusterIntegration|Environment pattern")
+ = s_("ClusterIntegration|Environment scope")
.table-section.section-30{ role: "rowheader" }
= s_("ClusterIntegration|Project namespace")
.table-section.section-10{ role: "rowheader" }
diff --git a/app/views/projects/clusters/show.html.haml b/app/views/projects/clusters/show.html.haml
index 0115c64c076..c7c84b5a42c 100644
--- a/app/views/projects/clusters/show.html.haml
+++ b/app/views/projects/clusters/show.html.haml
@@ -18,9 +18,9 @@
.js-cluster-application-notice
.flash-container
- %section.settings.no-animate.expanded
+ %section.settings.no-animate.expanded#cluster-integration
= render 'banner'
- = render 'enabled'
+ = render 'integration_form'
.cluster-applications-table#js-cluster-applications
@@ -41,6 +41,6 @@
%h4= _('Advanced settings')
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
- %p= s_('ClusterIntegration|Manage cluster integration on your GitLab project')
+ %p= s_("ClusterIntegration|Advanced options on this cluster's integration")
.settings-content
= render 'advanced_settings'
diff --git a/app/views/projects/clusters/user/_show.html.haml b/app/views/projects/clusters/user/_show.html.haml
index 89595bca007..5931e0b7f17 100644
--- a/app/views/projects/clusters/user/_show.html.haml
+++ b/app/views/projects/clusters/user/_show.html.haml
@@ -4,10 +4,6 @@
= field.label :name, s_('ClusterIntegration|Cluster name')
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Cluster name')
- .form-group
- = field.label :environment_scope, s_('ClusterIntegration|Environment scope')
- = field.text_field :environment_scope, class: 'form-control js-select-on-focus', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope')
-
= field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field|
.form-group
= platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL')
diff --git a/changelogs/unreleased/40228-verify-integrity-of-repositories.yml b/changelogs/unreleased/40228-verify-integrity-of-repositories.yml
new file mode 100644
index 00000000000..261d48652db
--- /dev/null
+++ b/changelogs/unreleased/40228-verify-integrity-of-repositories.yml
@@ -0,0 +1,5 @@
+---
+title: Fix gitlab-rake gitlab:import:repos import schedule
+merge_request: 15931
+author:
+type: fixed
diff --git a/changelogs/unreleased/feature-api_runners_online.yml b/changelogs/unreleased/feature-api_runners_online.yml
new file mode 100644
index 00000000000..08f4dd16f28
--- /dev/null
+++ b/changelogs/unreleased/feature-api_runners_online.yml
@@ -0,0 +1,5 @@
+---
+title: Add online and status attribute to runner api entity
+merge_request: 11750
+author:
+type: added
diff --git a/changelogs/unreleased/jivl-fix-import-project-url-bug.yml b/changelogs/unreleased/jivl-fix-import-project-url-bug.yml
new file mode 100644
index 00000000000..0d97b9c9a53
--- /dev/null
+++ b/changelogs/unreleased/jivl-fix-import-project-url-bug.yml
@@ -0,0 +1,5 @@
+---
+title: Fix import project url not updating project name
+merge_request: 16120
+author:
+type: fixed
diff --git a/changelogs/unreleased/ldap_username_attributes.yml b/changelogs/unreleased/ldap_username_attributes.yml
new file mode 100644
index 00000000000..89bbca58fc9
--- /dev/null
+++ b/changelogs/unreleased/ldap_username_attributes.yml
@@ -0,0 +1,5 @@
+---
+title: Modify `LDAP::Person` to return username value based on attributes
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/winh-modal-target-id.yml b/changelogs/unreleased/winh-modal-target-id.yml
new file mode 100644
index 00000000000..f8d5b72be50
--- /dev/null
+++ b/changelogs/unreleased/winh-modal-target-id.yml
@@ -0,0 +1,5 @@
+---
+title: Add id to modal.vue to support data-toggle="modal"
+merge_request: 16189
+author:
+type: other
diff --git a/db/post_migrate/20171221140220_schedule_issues_closed_at_type_change.rb b/db/post_migrate/20171221140220_schedule_issues_closed_at_type_change.rb
index be18c5866ae..eeecc7b1de0 100644
--- a/db/post_migrate/20171221140220_schedule_issues_closed_at_type_change.rb
+++ b/db/post_migrate/20171221140220_schedule_issues_closed_at_type_change.rb
@@ -1,6 +1,6 @@
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
-
+# rubocop:disable Migration/Datetime
class ScheduleIssuesClosedAtTypeChange < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
diff --git a/doc/administration/raketasks/check.md b/doc/administration/raketasks/check.md
index c8b5434c068..c39cb49b1c6 100644
--- a/doc/administration/raketasks/check.md
+++ b/doc/administration/raketasks/check.md
@@ -28,19 +28,25 @@ exactly which repositories are causing the trouble.
### Check all GitLab repositories
+>**Note:**
+>
+> - `gitlab:repo:check` has been deprecated in favor of `gitlab:git:fsck`
+> - [Deprecated][ce-15931] in GitLab 10.4.
+> - `gitlab:repo:check` will be removed in the future. [Removal issue][ce-41699]
+
This task loops through all repositories on the GitLab server and runs the
3 integrity checks described previously.
**Omnibus Installation**
```
-sudo gitlab-rake gitlab:repo:check
+sudo gitlab-rake gitlab:git:fsck
```
**Source Installation**
```bash
-sudo -u git -H bundle exec rake gitlab:repo:check RAILS_ENV=production
+sudo -u git -H bundle exec rake gitlab:git:fsck RAILS_ENV=production
```
### Check repositories for a specific user
@@ -76,3 +82,6 @@ The LDAP check Rake task will test the bind_dn and password credentials
(if configured) and will list a sample of LDAP users. This task is also
executed as part of the `gitlab:check` task, but can run independently.
See [LDAP Rake Tasks - LDAP Check](ldap.md#check) for details.
+
+[ce-15931]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15931
+[ce-41699]: https://gitlab.com/gitlab-org/gitlab-ce/issues/41699
diff --git a/doc/api/runners.md b/doc/api/runners.md
index 015b09a745e..7495c6cdedb 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -30,14 +30,18 @@ Example response:
"description": "test-1-20150125",
"id": 6,
"is_shared": false,
- "name": null
+ "name": null,
+ "online": true,
+ "status": "online"
},
{
"active": true,
"description": "test-2-20150125",
"id": 8,
"is_shared": false,
- "name": null
+ "name": null,
+ "online": false,
+ "status": "offline"
}
]
```
@@ -69,28 +73,36 @@ Example response:
"description": "shared-runner-1",
"id": 1,
"is_shared": true,
- "name": null
+ "name": null,
+ "online": true,
+ "status": "online"
},
{
"active": true,
"description": "shared-runner-2",
"id": 3,
"is_shared": true,
- "name": null
+ "name": null,
+ "online": false
+ "status": "offline"
},
{
"active": true,
"description": "test-1-20150125",
"id": 6,
"is_shared": false,
- "name": null
+ "name": null,
+ "online": true
+ "status": "paused"
},
{
"active": true,
"description": "test-2-20150125",
"id": 8,
"is_shared": false,
- "name": null
+ "name": null,
+ "online": false,
+ "status": "offline"
}
]
```
@@ -122,6 +134,8 @@ Example response:
"is_shared": false,
"contacted_at": "2016-01-25T16:39:48.066Z",
"name": null,
+ "online": true,
+ "status": "online",
"platform": null,
"projects": [
{
@@ -176,6 +190,8 @@ Example response:
"is_shared": false,
"contacted_at": "2016-01-25T16:39:48.066Z",
"name": null,
+ "online": true,
+ "status": "online",
"platform": null,
"projects": [
{
@@ -327,14 +343,18 @@ Example response:
"description": "test-2-20150125",
"id": 8,
"is_shared": false,
- "name": null
+ "name": null,
+ "online": false,
+ "status": "offline"
},
{
"active": true,
"description": "development_runner",
"id": 5,
"is_shared": true,
- "name": null
+ "name": null,
+ "online": true
+ "status": "paused"
}
]
```
@@ -364,7 +384,9 @@ Example response:
"description": "test-2016-02-01",
"id": 9,
"is_shared": false,
- "name": null
+ "name": null,
+ "online": true,
+ "status": "online"
}
```
diff --git a/doc/development/gitaly.md b/doc/development/gitaly.md
index ca2048c7019..26abf967dcf 100644
--- a/doc/development/gitaly.md
+++ b/doc/development/gitaly.md
@@ -97,6 +97,29 @@ describe 'Gitaly Request count tests' do
end
```
+## Running tests with a locally modified version of Gitaly
+
+Normally, gitlab-ce/ee tests use a local clone of Gitaly in `tmp/tests/gitaly`
+pinned at the version specified in GITALY_SERVER_VERSION. If you want
+to run tests locally against a modified version of Gitaly you can
+replace `tmp/tests/gitaly` with a symlink.
+
+```shell
+rm -rf tmp/tests/gitaly
+ln -s /path/to/gitaly tmp/tests/gitaly
+```
+
+Make sure you run `make` in your local Gitaly directory before running
+tests. Otherwise, Gitaly will fail to boot.
+
+If you make changes to your local Gitaly in between test runs you need
+to manually run `make` again.
+
+Note that CI tests will not use your locally modified version of
+Gitaly. To use a custom Gitaly version in CI you need to update
+GITALY_SERVER_VERSION. You can use the format `=revision` to use a
+non-tagged commit from https://gitlab.com/gitlab-org/gitaly in CI.
+
---
[Return to Development documentation](README.md)
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 4ad4a1f7867..f5fa5fef389 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -862,6 +862,8 @@ module API
expose :active
expose :is_shared
expose :name
+ expose :online?, as: :online
+ expose :status
end
class RunnerDetails < Runner
diff --git a/lib/gitlab/encoding_helper.rb b/lib/gitlab/encoding_helper.rb
index 6b53eb4533d..c0edcabc6fd 100644
--- a/lib/gitlab/encoding_helper.rb
+++ b/lib/gitlab/encoding_helper.rb
@@ -14,14 +14,7 @@ module Gitlab
ENCODING_CONFIDENCE_THRESHOLD = 50
def encode!(message)
- return nil unless message.respond_to?(:force_encoding)
- return message if message.encoding == Encoding::UTF_8 && message.valid_encoding?
-
- if message.respond_to?(:frozen?) && message.frozen?
- message = message.dup
- end
-
- message.force_encoding("UTF-8")
+ message = force_encode_utf8(message)
return message if message.valid_encoding?
# return message if message type is binary
@@ -35,6 +28,8 @@ module Gitlab
# encode and clean the bad chars
message.replace clean(message)
+ rescue ArgumentError
+ return nil
rescue
encoding = detect ? detect[:encoding] : "unknown"
"--broken encoding: #{encoding}"
@@ -54,8 +49,8 @@ module Gitlab
end
def encode_utf8(message)
- return nil unless message.is_a?(String)
- return message if message.encoding == Encoding::UTF_8 && message.valid_encoding?
+ message = force_encode_utf8(message)
+ return message if message.valid_encoding?
detect = CharlockHolmes::EncodingDetector.detect(message)
if detect && detect[:encoding]
@@ -69,6 +64,8 @@ module Gitlab
else
clean(message)
end
+ rescue ArgumentError
+ return nil
end
def encode_binary(s)
@@ -83,6 +80,15 @@ module Gitlab
private
+ def force_encode_utf8(message)
+ raise ArgumentError unless message.respond_to?(:force_encoding)
+ return message if message.encoding == Encoding::UTF_8 && message.valid_encoding?
+
+ message = message.dup if message.respond_to?(:frozen?) && message.frozen?
+
+ message.force_encoding("UTF-8")
+ end
+
def clean(message)
message.encode("UTF-16BE", undef: :replace, invalid: :replace, replace: "")
.encode("UTF-8")
diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb
index 1f7c35cafaa..71647099f83 100644
--- a/lib/gitlab/git.rb
+++ b/lib/gitlab/git.rb
@@ -11,7 +11,7 @@ module Gitlab
include Gitlab::EncodingHelper
def ref_name(ref)
- encode_utf8(ref).sub(/\Arefs\/(tags|heads|remotes)\//, '')
+ encode!(ref).sub(/\Arefs\/(tags|heads|remotes)\//, '')
end
def branch_name(ref)
diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb
index 228d97a87ab..a1755143abe 100644
--- a/lib/gitlab/git/blob.rb
+++ b/lib/gitlab/git/blob.rb
@@ -50,10 +50,19 @@ module Gitlab
# to the caller to limit the number of blobs and blob_size_limit.
#
# Gitaly migration issue: https://gitlab.com/gitlab-org/gitaly/issues/798
- def batch(repository, blob_references, blob_size_limit: nil)
- blob_size_limit ||= MAX_DATA_DISPLAY_SIZE
- blob_references.map do |sha, path|
- find_by_rugged(repository, sha, path, limit: blob_size_limit)
+ def batch(repository, blob_references, blob_size_limit: MAX_DATA_DISPLAY_SIZE)
+ Gitlab::GitalyClient.migrate(:list_blobs_by_sha_path) do |is_enabled|
+ if is_enabled
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ blob_references.map do |sha, path|
+ find_by_gitaly(repository, sha, path, limit: blob_size_limit)
+ end
+ end
+ else
+ blob_references.map do |sha, path|
+ find_by_rugged(repository, sha, path, limit: blob_size_limit)
+ end
+ end
end
end
@@ -122,13 +131,23 @@ module Gitlab
)
end
- def find_by_gitaly(repository, sha, path)
+ def find_by_gitaly(repository, sha, path, limit: MAX_DATA_DISPLAY_SIZE)
path = path.sub(/\A\/*/, '')
path = '/' if path.empty?
name = File.basename(path)
- entry = Gitlab::GitalyClient::CommitService.new(repository).tree_entry(sha, path, MAX_DATA_DISPLAY_SIZE)
+
+ # Gitaly will think that setting the limit to 0 means unlimited, while
+ # the client might only need the metadata and thus set the limit to 0.
+ # In this method we'll then set the limit to 1, but clear the byte of data
+ # that we got back so for the outside world it looks like the limit was
+ # actually 0.
+ req_limit = limit == 0 ? 1 : limit
+
+ entry = Gitlab::GitalyClient::CommitService.new(repository).tree_entry(sha, path, req_limit)
return unless entry
+ entry.data = "" if limit == 0
+
case entry.type
when :COMMIT
new(
diff --git a/lib/gitlab/git/operation_service.rb b/lib/gitlab/git/operation_service.rb
index ef5bdbaf819..3fb0e2eed93 100644
--- a/lib/gitlab/git/operation_service.rb
+++ b/lib/gitlab/git/operation_service.rb
@@ -97,6 +97,11 @@ module Gitlab
end
end
+ def update_branch(branch_name, newrev, oldrev)
+ ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
+ update_ref_in_hooks(ref, newrev, oldrev)
+ end
+
private
# Returns [newrev, should_run_after_create, should_run_after_create_branch]
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index aec85f971ca..17c05c44d7e 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -1219,9 +1219,16 @@ module Gitlab
rebase_path = worktree_path(REBASE_WORKTREE_PREFIX, rebase_id)
env = git_env_for_user(user)
+ if remote_repository.is_a?(RemoteRepository)
+ env.merge!(remote_repository.fetch_env)
+ remote_repo_path = GITALY_INTERNAL_URL
+ else
+ remote_repo_path = remote_repository.path
+ end
+
with_worktree(rebase_path, branch, env: env) do
run_git!(
- %W(pull --rebase #{remote_repository.path} #{remote_branch}),
+ %W(pull --rebase #{remote_repo_path} #{remote_branch}),
chdir: rebase_path, env: env
)
diff --git a/lib/gitlab/ldap/adapter.rb b/lib/gitlab/ldap/adapter.rb
index 0afaa2306b5..76863e77dc3 100644
--- a/lib/gitlab/ldap/adapter.rb
+++ b/lib/gitlab/ldap/adapter.rb
@@ -74,7 +74,7 @@ module Gitlab
def user_options(fields, value, limit)
options = {
- attributes: Gitlab::LDAP::Person.ldap_attributes(config).compact.uniq,
+ attributes: Gitlab::LDAP::Person.ldap_attributes(config),
base: config.base
}
diff --git a/lib/gitlab/ldap/config.rb b/lib/gitlab/ldap/config.rb
index c8f19cd52d5..0d9a554fc18 100644
--- a/lib/gitlab/ldap/config.rb
+++ b/lib/gitlab/ldap/config.rb
@@ -148,7 +148,7 @@ module Gitlab
def default_attributes
{
- 'username' => %w(uid userid sAMAccountName),
+ 'username' => %w(uid sAMAccountName userid),
'email' => %w(mail email userPrincipalName),
'name' => 'cn',
'first_name' => 'givenName',
diff --git a/lib/gitlab/ldap/person.rb b/lib/gitlab/ldap/person.rb
index 38d7a9ba2f5..e81cec6ba1a 100644
--- a/lib/gitlab/ldap/person.rb
+++ b/lib/gitlab/ldap/person.rb
@@ -6,6 +6,8 @@ module Gitlab
# Source: http://ctogonewild.com/2009/09/03/bitmask-searches-in-ldap/
AD_USER_DISABLED = Net::LDAP::Filter.ex("userAccountControl:1.2.840.113556.1.4.803", "2")
+ InvalidEntryError = Class.new(StandardError)
+
attr_accessor :entry, :provider
def self.find_by_uid(uid, adapter)
@@ -29,11 +31,12 @@ module Gitlab
def self.ldap_attributes(config)
[
- 'dn', # Used in `dn`
- config.uid, # Used in `uid`
- *config.attributes['name'], # Used in `name`
- *config.attributes['email'] # Used in `email`
- ]
+ 'dn',
+ config.uid,
+ *config.attributes['name'],
+ *config.attributes['email'],
+ *config.attributes['username']
+ ].compact.uniq
end
def self.normalize_dn(dn)
@@ -60,6 +63,8 @@ module Gitlab
Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" }
@entry = entry
@provider = provider
+
+ validate_entry
end
def name
@@ -71,7 +76,13 @@ module Gitlab
end
def username
- uid
+ username = attribute_value(:username)
+
+ # Depending on the attribute, multiple values may
+ # be returned. We need only one for username.
+ # Ex. `uid` returns only one value but `mail` may
+ # return an array of multiple email addresses.
+ [username].flatten.first
end
def email
@@ -104,6 +115,19 @@ module Gitlab
entry.public_send(selected_attr) # rubocop:disable GitlabSecurity/PublicSend
end
+
+ def validate_entry
+ allowed_attrs = self.class.ldap_attributes(config).map(&:downcase)
+
+ # Net::LDAP::Entry transforms keys to symbols. Change to strings to compare.
+ entry_attrs = entry.attribute_names.map { |n| n.to_s.downcase }
+ invalid_attrs = entry_attrs - allowed_attrs
+
+ if invalid_attrs.any?
+ raise InvalidEntryError,
+ "#{self.class.name} initialized with Net::LDAP::Entry containing invalid attributes(s): #{invalid_attrs}"
+ end
+ end
end
end
end
diff --git a/lib/gitlab/setup_helper.rb b/lib/gitlab/setup_helper.rb
new file mode 100644
index 00000000000..d01213bb6e0
--- /dev/null
+++ b/lib/gitlab/setup_helper.rb
@@ -0,0 +1,61 @@
+module Gitlab
+ module SetupHelper
+ class << self
+ # We cannot create config.toml files for all possible Gitaly configuations.
+ # For instance, if Gitaly is running on another machine then it makes no
+ # sense to write a config.toml file on the current machine. This method will
+ # only generate a configuration for the most common and simplest case: when
+ # we have exactly one Gitaly process and we are sure it is running locally
+ # because it uses a Unix socket.
+ # For development and testing purposes, an extra storage is added to gitaly,
+ # which is not known to Rails, but must be explicitly stubbed.
+ def gitaly_configuration_toml(gitaly_dir, gitaly_ruby: true)
+ storages = []
+ address = nil
+
+ Gitlab.config.repositories.storages.each do |key, val|
+ if address
+ if address != val['gitaly_address']
+ raise ArgumentError, "Your gitlab.yml contains more than one gitaly_address."
+ end
+ elsif URI(val['gitaly_address']).scheme != 'unix'
+ raise ArgumentError, "Automatic config.toml generation only supports 'unix:' addresses."
+ else
+ address = val['gitaly_address']
+ end
+
+ storages << { name: key, path: val['path'] }
+ end
+
+ if Rails.env.test?
+ storages << { name: 'test_second_storage', path: Rails.root.join('tmp', 'tests', 'second_storage').to_s }
+ end
+
+ config = { socket_path: address.sub(%r{\Aunix:}, ''), storage: storages }
+ config[:auth] = { token: 'secret' } if Rails.env.test?
+ config[:'gitaly-ruby'] = { dir: File.join(gitaly_dir, 'ruby') } if gitaly_ruby
+ config[:'gitlab-shell'] = { dir: Gitlab.config.gitlab_shell.path }
+ config[:bin_dir] = Gitlab.config.gitaly.client_path
+
+ TOML.dump(config)
+ end
+
+ # rubocop:disable Rails/Output
+ def create_gitaly_configuration(dir, force: false)
+ config_path = File.join(dir, 'config.toml')
+ FileUtils.rm_f(config_path) if force
+
+ File.open(config_path, File::WRONLY | File::CREAT | File::EXCL) do |f|
+ f.puts gitaly_configuration_toml(dir)
+ end
+ rescue Errno::EEXIST
+ puts "Skipping config.toml generation:"
+ puts "A configuration file already exists."
+ rescue ArgumentError => e
+ puts "Skipping config.toml generation:"
+ puts e.message
+ end
+ # rubocop:enable Rails/Output
+ end
+ end
+end
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index 40650fc5ee7..b9cc97c9244 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -71,7 +71,6 @@ module Gitlab
# Ex.
# add_repository("/path/to/storage", "gitlab/gitlab-ci")
#
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/387
def add_repository(storage, name)
relative_path = name.dup
relative_path << '.git' unless relative_path.end_with?('.git')
@@ -100,7 +99,7 @@ module Gitlab
# Ex.
# import_repository("/path/to/storage", "gitlab/gitlab-ci", "https://gitlab.com/gitlab-org/gitlab-test.git")
#
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/387
+ # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/874
def import_repository(storage, name, url)
# The timeout ensures the subprocess won't hang forever
cmd = gitlab_projects(storage, "#{name}.git")
@@ -122,7 +121,6 @@ module Gitlab
# Ex.
# fetch_remote(my_repo, "upstream")
#
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/387
def fetch_remote(repository, remote, ssh_auth: nil, forced: false, no_tags: false)
gitaly_migrate(:fetch_remote) do |is_enabled|
if is_enabled
@@ -142,7 +140,7 @@ module Gitlab
# Ex.
# mv_repository("/path/to/storage", "gitlab/gitlab-ci", "randx/gitlab-ci-new")
#
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/387
+ # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/873
def mv_repository(storage, path, new_path)
gitlab_projects(storage, "#{path}.git").mv_project("#{new_path}.git")
end
@@ -156,7 +154,7 @@ module Gitlab
# Ex.
# fork_repository("/path/to/forked_from/storage", "gitlab/gitlab-ci", "/path/to/forked_to/storage", "new-namespace/gitlab-ci")
#
- # Gitaly note: JV: not easy to migrate because this involves two Gitaly servers, not one.
+ # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/817
def fork_repository(forked_from_storage, forked_from_disk_path, forked_to_storage, forked_to_disk_path)
gitlab_projects(forked_from_storage, "#{forked_from_disk_path}.git")
.fork_repository(forked_to_storage, "#{forked_to_disk_path}.git")
@@ -170,7 +168,7 @@ module Gitlab
# Ex.
# remove_repository("/path/to/storage", "gitlab/gitlab-ci")
#
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/387
+ # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/873
def remove_repository(storage, name)
gitlab_projects(storage, "#{name}.git").rm_project
end
@@ -221,7 +219,6 @@ module Gitlab
# Ex.
# add_namespace("/path/to/storage", "gitlab")
#
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/385
def add_namespace(storage, name)
Gitlab::GitalyClient.migrate(:add_namespace) do |enabled|
if enabled
@@ -243,7 +240,6 @@ module Gitlab
# Ex.
# rm_namespace("/path/to/storage", "gitlab")
#
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/385
def rm_namespace(storage, name)
Gitlab::GitalyClient.migrate(:remove_namespace) do |enabled|
if enabled
@@ -261,7 +257,6 @@ module Gitlab
# Ex.
# mv_namespace("/path/to/storage", "gitlab", "gitlabhq")
#
- # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/385
def mv_namespace(storage, old_name, new_name)
Gitlab::GitalyClient.migrate(:rename_namespace) do |enabled|
if enabled
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index dfade1f3885..903e84359cd 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -387,14 +387,8 @@ namespace :gitlab do
namespace :repo do
desc "GitLab | Check the integrity of the repositories managed by GitLab"
task check: :environment do
- Gitlab.config.repositories.storages.each do |name, repository_storage|
- namespace_dirs = Dir.glob(File.join(repository_storage['path'], '*'))
-
- namespace_dirs.each do |namespace_dir|
- repo_dirs = Dir.glob(File.join(namespace_dir, '*'))
- repo_dirs.each { |repo_dir| check_repo_integrity(repo_dir) }
- end
- end
+ puts "This task is deprecated. Please use gitlab:git:fsck instead".color(:red)
+ Rake::Task["gitlab:git:fsck"].execute
end
end
@@ -461,35 +455,4 @@ namespace :gitlab do
puts "FAIL. Please update gitlab-shell to #{required_version} from #{current_version}".color(:red)
end
end
-
- def check_repo_integrity(repo_dir)
- puts "\nChecking repo at #{repo_dir.color(:yellow)}"
-
- git_fsck(repo_dir)
- check_config_lock(repo_dir)
- check_ref_locks(repo_dir)
- end
-
- def git_fsck(repo_dir)
- puts "Running `git fsck`".color(:yellow)
- system(*%W(#{Gitlab.config.git.bin_path} fsck), chdir: repo_dir)
- end
-
- def check_config_lock(repo_dir)
- config_exists = File.exist?(File.join(repo_dir, 'config.lock'))
- config_output = config_exists ? 'yes'.color(:red) : 'no'.color(:green)
- puts "'config.lock' file exists?".color(:yellow) + " ... #{config_output}"
- end
-
- def check_ref_locks(repo_dir)
- lock_files = Dir.glob(File.join(repo_dir, 'refs/heads/*.lock'))
- if lock_files.present?
- puts "Ref lock files exist:".color(:red)
- lock_files.each do |lock_file|
- puts " #{lock_file}"
- end
- else
- puts "No ref lock files exist".color(:green)
- end
- end
end
diff --git a/lib/tasks/gitlab/git.rake b/lib/tasks/gitlab/git.rake
index cf82134d97e..3f5dd2ae3b3 100644
--- a/lib/tasks/gitlab/git.rake
+++ b/lib/tasks/gitlab/git.rake
@@ -30,6 +30,20 @@ namespace :gitlab do
end
end
+ desc 'GitLab | Git | Check all repos integrity'
+ task fsck: :environment do
+ failures = perform_git_cmd(%W(#{Gitlab.config.git.bin_path} fsck --name-objects --no-progress), "Checking integrity") do |repo|
+ check_config_lock(repo)
+ check_ref_locks(repo)
+ end
+
+ if failures.empty?
+ puts "Done".color(:green)
+ else
+ output_failures(failures)
+ end
+ end
+
def perform_git_cmd(cmd, message)
puts "Starting #{message} on all repositories"
@@ -40,6 +54,8 @@ namespace :gitlab do
else
failures << repo
end
+
+ yield(repo) if block_given?
end
failures
@@ -49,5 +65,24 @@ namespace :gitlab do
puts "The following repositories reported errors:".color(:red)
failures.each { |f| puts "- #{f}" }
end
+
+ def check_config_lock(repo_dir)
+ config_exists = File.exist?(File.join(repo_dir, 'config.lock'))
+ config_output = config_exists ? 'yes'.color(:red) : 'no'.color(:green)
+
+ puts "'config.lock' file exists?".color(:yellow) + " ... #{config_output}"
+ end
+
+ def check_ref_locks(repo_dir)
+ lock_files = Dir.glob(File.join(repo_dir, 'refs/heads/*.lock'))
+
+ if lock_files.present?
+ puts "Ref lock files exist:".color(:red)
+
+ lock_files.each { |lock_file| puts " #{lock_file}" }
+ else
+ puts "No ref lock files exist".color(:green)
+ end
+ end
end
end
diff --git a/lib/tasks/gitlab/gitaly.rake b/lib/tasks/gitlab/gitaly.rake
index 4d880c05f99..4507b841964 100644
--- a/lib/tasks/gitlab/gitaly.rake
+++ b/lib/tasks/gitlab/gitaly.rake
@@ -21,8 +21,8 @@ namespace :gitlab do
command << 'BUNDLE_FLAGS=--no-deployment' if Rails.env.test?
+ Gitlab::SetupHelper.create_gitaly_configuration(args.dir)
Dir.chdir(args.dir) do
- create_gitaly_configuration
# In CI we run scripts/gitaly-test-build instead of this command
unless ENV['CI'].present?
Bundler.with_original_env { run_command!(command) }
@@ -39,60 +39,7 @@ namespace :gitlab do
# Exclude gitaly-ruby configuration because that depends on the gitaly
# installation directory.
- puts gitaly_configuration_toml(gitaly_ruby: false)
- end
-
- private
-
- # We cannot create config.toml files for all possible Gitaly configuations.
- # For instance, if Gitaly is running on another machine then it makes no
- # sense to write a config.toml file on the current machine. This method will
- # only generate a configuration for the most common and simplest case: when
- # we have exactly one Gitaly process and we are sure it is running locally
- # because it uses a Unix socket.
- # For development and testing purposes, an extra storage is added to gitaly,
- # which is not known to Rails, but must be explicitly stubbed.
- def gitaly_configuration_toml(gitaly_ruby: true)
- storages = []
- address = nil
-
- Gitlab.config.repositories.storages.each do |key, val|
- if address
- if address != val['gitaly_address']
- raise ArgumentError, "Your gitlab.yml contains more than one gitaly_address."
- end
- elsif URI(val['gitaly_address']).scheme != 'unix'
- raise ArgumentError, "Automatic config.toml generation only supports 'unix:' addresses."
- else
- address = val['gitaly_address']
- end
-
- storages << { name: key, path: val['path'] }
- end
-
- if Rails.env.test?
- storages << { name: 'test_second_storage', path: Rails.root.join('tmp', 'tests', 'second_storage').to_s }
- end
-
- config = { socket_path: address.sub(%r{\Aunix:}, ''), storage: storages }
- config[:auth] = { token: 'secret' } if Rails.env.test?
- config[:'gitaly-ruby'] = { dir: File.join(Dir.pwd, 'ruby') } if gitaly_ruby
- config[:'gitlab-shell'] = { dir: Gitlab.config.gitlab_shell.path }
- config[:bin_dir] = Gitlab.config.gitaly.client_path
-
- TOML.dump(config)
- end
-
- def create_gitaly_configuration
- File.open("config.toml", File::WRONLY | File::CREAT | File::EXCL) do |f|
- f.puts gitaly_configuration_toml
- end
- rescue Errno::EEXIST
- puts "Skipping config.toml generation:"
- puts "A configuration file already exists."
- rescue ArgumentError => e
- puts "Skipping config.toml generation:"
- puts e.message
+ puts Gitlab::SetupHelper.gitaly_configuration_toml('', gitaly_ruby: false)
end
end
end
diff --git a/lib/tasks/gitlab/task_helpers.rb b/lib/tasks/gitlab/task_helpers.rb
index 6723662703c..c1182af1014 100644
--- a/lib/tasks/gitlab/task_helpers.rb
+++ b/lib/tasks/gitlab/task_helpers.rb
@@ -130,7 +130,7 @@ module Gitlab
def all_repos
Gitlab.config.repositories.storages.each_value do |repository_storage|
- IO.popen(%W(find #{repository_storage['path']} -mindepth 2 -maxdepth 2 -type d -name *.git)) do |find|
+ IO.popen(%W(find #{repository_storage['path']} -mindepth 2 -type d -name *.git)) do |find|
find.each_line do |path|
yield path.chomp
end
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index 67b8901f8fb..882a2756b72 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -81,14 +81,14 @@ feature 'Gcp Cluster', :js do
end
it 'user sees a cluster details page' do
- expect(page).to have_button('Save')
+ expect(page).to have_button('Save changes')
expect(page.find(:css, '.cluster-name').value).to eq(cluster.name)
end
context 'when user disables the cluster' do
before do
page.find(:css, '.js-toggle-cluster').click
- click_button 'Save'
+ page.within('#cluster-integration') { click_button 'Save changes' }
end
it 'user sees the successful message' do
@@ -99,7 +99,7 @@ feature 'Gcp Cluster', :js do
context 'when user changes cluster parameters' do
before do
fill_in 'cluster_platform_kubernetes_attributes_namespace', with: 'my-namespace'
- click_button 'Save changes'
+ page.within('#js-cluster-details') { click_button 'Save changes' }
end
it 'user sees the successful message' do
diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb
index 414f4acba86..a519b9f9c7e 100644
--- a/spec/features/projects/clusters/user_spec.rb
+++ b/spec/features/projects/clusters/user_spec.rb
@@ -29,7 +29,7 @@ feature 'User Cluster', :js do
end
it 'user sees a cluster details page' do
- expect(page).to have_content('Enable cluster integration')
+ expect(page).to have_content('Cluster integration')
expect(page.find_field('cluster[name]').value).to eq('dev-cluster')
expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value)
.to have_content('http://example.com')
@@ -57,14 +57,14 @@ feature 'User Cluster', :js do
end
it 'user sees a cluster details page' do
- expect(page).to have_button('Save')
+ expect(page).to have_button('Save changes')
end
context 'when user disables the cluster' do
before do
page.find(:css, '.js-toggle-cluster').click
fill_in 'cluster_name', with: 'dev-cluster'
- click_button 'Save'
+ page.within('#cluster-integration') { click_button 'Save changes' }
end
it 'user sees the successful message' do
@@ -76,7 +76,7 @@ feature 'User Cluster', :js do
before do
fill_in 'cluster_name', with: 'my-dev-cluster'
fill_in 'cluster_platform_kubernetes_attributes_namespace', with: 'my-namespace'
- click_button 'Save changes'
+ page.within('#js-cluster-details') { click_button 'Save changes' }
end
it 'user sees the successful message' do
diff --git a/spec/javascripts/groups/components/item_actions_spec.js b/spec/javascripts/groups/components/item_actions_spec.js
index 7a5c1da4d1d..6d6fb410859 100644
--- a/spec/javascripts/groups/components/item_actions_spec.js
+++ b/spec/javascripts/groups/components/item_actions_spec.js
@@ -47,17 +47,11 @@ describe('ItemActionsComponent', () => {
it('should change `modalStatus` prop to `false` and emit `leaveGroup` event with required params when called with `leaveConfirmed` as `true`', () => {
spyOn(eventHub, '$emit');
vm.modalStatus = true;
- vm.leaveGroup(true);
- expect(vm.modalStatus).toBeFalsy();
- expect(eventHub.$emit).toHaveBeenCalledWith('leaveGroup', vm.group, vm.parentGroup);
- });
- it('should change `modalStatus` prop to `false` and should NOT emit `leaveGroup` event when called with `leaveConfirmed` as `false`', () => {
- spyOn(eventHub, '$emit');
- vm.modalStatus = true;
- vm.leaveGroup(false);
+ vm.leaveGroup();
+
expect(vm.modalStatus).toBeFalsy();
- expect(eventHub.$emit).not.toHaveBeenCalled();
+ expect(eventHub.$emit).toHaveBeenCalledWith('leaveGroup', vm.group, vm.parentGroup);
});
});
});
diff --git a/spec/javascripts/profile/account/components/delete_account_modal_spec.js b/spec/javascripts/profile/account/components/delete_account_modal_spec.js
index 2e94948cfb2..588b61196a5 100644
--- a/spec/javascripts/profile/account/components/delete_account_modal_spec.js
+++ b/spec/javascripts/profile/account/components/delete_account_modal_spec.js
@@ -51,7 +51,7 @@ describe('DeleteAccountModal component', () => {
Vue.nextTick()
.then(() => {
expect(vm.enteredPassword).toBe(input.value);
- expect(submitButton).toHaveClass('disabled');
+ expect(submitButton).toHaveAttr('disabled', 'disabled');
submitButton.click();
expect(form.submit).not.toHaveBeenCalled();
})
@@ -68,7 +68,7 @@ describe('DeleteAccountModal component', () => {
Vue.nextTick()
.then(() => {
expect(vm.enteredPassword).toBe(input.value);
- expect(submitButton).not.toHaveClass('disabled');
+ expect(submitButton).not.toHaveAttr('disabled', 'disabled');
submitButton.click();
expect(form.submit).toHaveBeenCalled();
})
@@ -101,7 +101,7 @@ describe('DeleteAccountModal component', () => {
Vue.nextTick()
.then(() => {
expect(vm.enteredUsername).toBe(input.value);
- expect(submitButton).toHaveClass('disabled');
+ expect(submitButton).toHaveAttr('disabled', 'disabled');
submitButton.click();
expect(form.submit).not.toHaveBeenCalled();
})
@@ -118,7 +118,7 @@ describe('DeleteAccountModal component', () => {
Vue.nextTick()
.then(() => {
expect(vm.enteredUsername).toBe(input.value);
- expect(submitButton).not.toHaveClass('disabled');
+ expect(submitButton).not.toHaveAttr('disabled', 'disabled');
submitButton.click();
expect(form.submit).toHaveBeenCalled();
})
diff --git a/spec/javascripts/projects/project_new_spec.js b/spec/javascripts/projects/project_new_spec.js
index 850768f0e4f..c314ca8ab72 100644
--- a/spec/javascripts/projects/project_new_spec.js
+++ b/spec/javascripts/projects/project_new_spec.js
@@ -6,8 +6,12 @@ describe('New Project', () => {
beforeEach(() => {
setFixtures(`
- <input id="project_import_url" />
- <input id="project_path" />
+ <div class='toggle-import-form'>
+ <div class='import-url-data'>
+ <input id="project_import_url" />
+ <input id="project_path" />
+ </div>
+ </div>
`);
$projectImportUrl = $('#project_import_url');
@@ -25,7 +29,7 @@ describe('New Project', () => {
it('does not change project path for disabled $projectImportUrl', () => {
$projectImportUrl.attr('disabled', true);
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual(dummyImportUrl);
});
@@ -38,7 +42,7 @@ describe('New Project', () => {
it('does not change project path if it is set by user', () => {
$projectPath.keyup();
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual(dummyImportUrl);
});
@@ -46,7 +50,7 @@ describe('New Project', () => {
it('does not change project path for empty $projectImportUrl', () => {
$projectImportUrl.val('');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual(dummyImportUrl);
});
@@ -54,7 +58,7 @@ describe('New Project', () => {
it('does not change project path for whitespace $projectImportUrl', () => {
$projectImportUrl.val(' ');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual(dummyImportUrl);
});
@@ -62,7 +66,7 @@ describe('New Project', () => {
it('does not change project path for $projectImportUrl without slashes', () => {
$projectImportUrl.val('has-no-slash');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual(dummyImportUrl);
});
@@ -70,7 +74,7 @@ describe('New Project', () => {
it('changes project path to last $projectImportUrl component', () => {
$projectImportUrl.val('/this/is/last');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('last');
});
@@ -78,7 +82,7 @@ describe('New Project', () => {
it('ignores trailing slashes in $projectImportUrl', () => {
$projectImportUrl.val('/has/trailing/slash/');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('slash');
});
@@ -86,7 +90,7 @@ describe('New Project', () => {
it('ignores fragment identifier in $projectImportUrl', () => {
$projectImportUrl.val('/this/has/a#fragment-identifier/');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('a');
});
@@ -94,7 +98,7 @@ describe('New Project', () => {
it('ignores query string in $projectImportUrl', () => {
$projectImportUrl.val('/url/with?query=string');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('with');
});
@@ -102,7 +106,7 @@ describe('New Project', () => {
it('ignores trailing .git in $projectImportUrl', () => {
$projectImportUrl.val('/repository.git');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('repository');
});
@@ -110,7 +114,7 @@ describe('New Project', () => {
it('changes project path for HTTPS URL in $projectImportUrl', () => {
$projectImportUrl.val('https://username:password@gitlab.company.com/group/project.git');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('project');
});
@@ -118,7 +122,7 @@ describe('New Project', () => {
it('changes project path for SSH URL in $projectImportUrl', () => {
$projectImportUrl.val('git@gitlab.com:gitlab-org/gitlab-ce.git');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('gitlab-ce');
});
diff --git a/spec/javascripts/repo/components/new_dropdown/index_spec.js b/spec/javascripts/repo/components/new_dropdown/index_spec.js
index b001c1655b4..6efbbf6d75e 100644
--- a/spec/javascripts/repo/components/new_dropdown/index_spec.js
+++ b/spec/javascripts/repo/components/new_dropdown/index_spec.js
@@ -57,16 +57,17 @@ describe('new dropdown component', () => {
});
});
- describe('toggleModalOpen', () => {
+ describe('hideModal', () => {
+ beforeAll((done) => {
+ vm.openModal = true;
+ Vue.nextTick(done);
+ });
+
it('closes modal after toggling', (done) => {
- vm.toggleModalOpen();
+ vm.hideModal();
Vue.nextTick()
.then(() => {
- expect(vm.$el.querySelector('.modal')).not.toBeNull();
- })
- .then(vm.toggleModalOpen)
- .then(() => {
expect(vm.$el.querySelector('.modal')).toBeNull();
})
.then(done)
diff --git a/spec/javascripts/vue_shared/components/modal_spec.js b/spec/javascripts/vue_shared/components/modal_spec.js
index 721f4044659..fe75a86cac8 100644
--- a/spec/javascripts/vue_shared/components/modal_spec.js
+++ b/spec/javascripts/vue_shared/components/modal_spec.js
@@ -2,11 +2,65 @@ import Vue from 'vue';
import modal from '~/vue_shared/components/modal.vue';
import mountComponent from '../../helpers/vue_mount_component_helper';
+const modalComponent = Vue.extend(modal);
+
describe('Modal', () => {
- it('does not render a primary button if no primaryButtonLabel', () => {
- const modalComponent = Vue.extend(modal);
- const vm = mountComponent(modalComponent);
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('props', () => {
+ describe('without primaryButtonLabel', () => {
+ beforeEach(() => {
+ vm = mountComponent(modalComponent, {
+ primaryButtonLabel: null,
+ });
+ });
+
+ it('does not render a primary button', () => {
+ expect(vm.$el.querySelector('.js-primary-button')).toBeNull();
+ });
+ });
+
+ describe('with id', () => {
+ it('does not render a primary button', () => {
+ beforeEach(() => {
+ vm = mountComponent(modalComponent, {
+ id: 'my-modal',
+ });
+ });
+
+ it('assigns the id to the modal', () => {
+ expect(vm.$el.querySelector('#my-modal.modal')).not.toBeNull();
+ });
+
+ it('does not show the modal immediately', () => {
+ expect(vm.$el.querySelector('#my-modal.modal')).not.toHaveClass('show');
+ });
+
+ it('does not show a backdrop', () => {
+ expect(vm.$el.querySelector('modal-backdrop')).toBeNull();
+ });
+ });
+ });
+
+ it('works with data-toggle="modal"', (done) => {
+ setFixtures(`
+ <button id="modal-button" data-toggle="modal" data-target="#my-modal"></button>
+ <div id="modal-container"></div>
+ `);
+
+ const modalContainer = document.getElementById('modal-container');
+ const modalButton = document.getElementById('modal-button');
+ vm = mountComponent(modalComponent, {
+ id: 'my-modal',
+ }, modalContainer);
+ const modalElement = vm.$el.querySelector('#my-modal');
+ $(modalElement).on('shown.bs.modal', () => done());
- expect(vm.$el.querySelector('.js-primary-button')).toBeNull();
+ modalButton.click();
+ });
});
});
diff --git a/spec/lib/gitlab/encoding_helper_spec.rb b/spec/lib/gitlab/encoding_helper_spec.rb
index 87ec2698fc1..4e9367323cb 100644
--- a/spec/lib/gitlab/encoding_helper_spec.rb
+++ b/spec/lib/gitlab/encoding_helper_spec.rb
@@ -120,6 +120,24 @@ describe Gitlab::EncodingHelper do
it 'returns empty string on conversion errors' do
expect { ext_class.encode_utf8('') }.not_to raise_error(ArgumentError)
end
+
+ context 'with strings that can be forcefully encoded into utf8' do
+ let(:test_string) do
+ "refs/heads/FixSymbolsTitleDropdown".encode("ASCII-8BIT")
+ end
+ let(:expected_string) do
+ "refs/heads/FixSymbolsTitleDropdown".encode("UTF-8")
+ end
+
+ subject { ext_class.encode_utf8(test_string) }
+
+ it "doesn't use CharlockHolmes if the encoding can be forced into utf_8" do
+ expect(CharlockHolmes::EncodingDetector).not_to receive(:detect)
+
+ expect(subject).to eq(expected_string)
+ expect(subject.encoding.name).to eq('UTF-8')
+ end
+ end
end
describe '#clean' do
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index c04a9688503..7f5946b1658 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -202,16 +202,6 @@ describe Gitlab::Git::Blob, seed_helper: true do
context 'limiting' do
subject { described_class.batch(repository, blob_references, blob_size_limit: blob_size_limit) }
- context 'default' do
- let(:blob_size_limit) { nil }
-
- it 'limits to MAX_DATA_DISPLAY_SIZE' do
- stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', 100)
-
- expect(subject.first.data.size).to eq(100)
- end
- end
-
context 'positive' do
let(:blob_size_limit) { 10 }
@@ -221,7 +211,10 @@ describe Gitlab::Git::Blob, seed_helper: true do
context 'zero' do
let(:blob_size_limit) { 0 }
- it { expect(subject.first.data).to eq('') }
+ it 'only loads the metadata' do
+ expect(subject.first.size).not_to be(0)
+ expect(subject.first.data).to eq('')
+ end
end
context 'negative' do
diff --git a/spec/lib/gitlab/ldap/adapter_spec.rb b/spec/lib/gitlab/ldap/adapter_spec.rb
index d9ddb4326be..6132abd9b35 100644
--- a/spec/lib/gitlab/ldap/adapter_spec.rb
+++ b/spec/lib/gitlab/ldap/adapter_spec.rb
@@ -16,7 +16,7 @@ describe Gitlab::LDAP::Adapter do
expect(adapter).to receive(:ldap_search) do |arg|
expect(arg[:filter].to_s).to eq('(uid=johndoe)')
expect(arg[:base]).to eq('dc=example,dc=com')
- expect(arg[:attributes]).to match(%w{dn uid cn mail email userPrincipalName})
+ expect(arg[:attributes]).to match(ldap_attributes)
end.and_return({})
adapter.users('uid', 'johndoe')
@@ -26,7 +26,7 @@ describe Gitlab::LDAP::Adapter do
expect(adapter).to receive(:ldap_search).with(
base: 'uid=johndoe,ou=users,dc=example,dc=com',
scope: Net::LDAP::SearchScope_BaseObject,
- attributes: %w{dn uid cn mail email userPrincipalName},
+ attributes: ldap_attributes,
filter: nil
).and_return({})
@@ -63,7 +63,7 @@ describe Gitlab::LDAP::Adapter do
it 'uses the right uid attribute when non-default' do
stub_ldap_config(uid: 'sAMAccountName')
expect(adapter).to receive(:ldap_search).with(
- hash_including(attributes: %w{dn sAMAccountName cn mail email userPrincipalName})
+ hash_including(attributes: ldap_attributes)
).and_return({})
adapter.users('sAMAccountName', 'johndoe')
@@ -137,4 +137,8 @@ describe Gitlab::LDAP::Adapter do
end
end
end
+
+ def ldap_attributes
+ Gitlab::LDAP::Person.ldap_attributes(Gitlab::LDAP::Config.new('ldapmain'))
+ end
end
diff --git a/spec/lib/gitlab/ldap/person_spec.rb b/spec/lib/gitlab/ldap/person_spec.rb
index d204050ef66..ff29d9aa5be 100644
--- a/spec/lib/gitlab/ldap/person_spec.rb
+++ b/spec/lib/gitlab/ldap/person_spec.rb
@@ -8,13 +8,16 @@ describe Gitlab::LDAP::Person do
before do
stub_ldap_config(
options: {
+ 'uid' => 'uid',
'attributes' => {
- 'name' => 'cn',
- 'email' => %w(mail email userPrincipalName)
+ 'name' => 'cn',
+ 'email' => %w(mail email userPrincipalName),
+ 'username' => username_attribute
}
}
)
end
+ let(:username_attribute) { %w(uid sAMAccountName userid) }
describe '.normalize_dn' do
subject { described_class.normalize_dn(given) }
@@ -44,6 +47,34 @@ describe Gitlab::LDAP::Person do
end
end
+ describe '.ldap_attributes' do
+ it 'returns a compact and unique array' do
+ stub_ldap_config(
+ options: {
+ 'uid' => nil,
+ 'attributes' => {
+ 'name' => 'cn',
+ 'email' => 'mail',
+ 'username' => %w(uid mail memberof)
+ }
+ }
+ )
+ config = Gitlab::LDAP::Config.new('ldapmain')
+ ldap_attributes = described_class.ldap_attributes(config)
+
+ expect(ldap_attributes).to match_array(%w(dn uid cn mail memberof))
+ end
+ end
+
+ describe '.validate_entry' do
+ it 'raises InvalidEntryError' do
+ entry['foo'] = 'bar'
+
+ expect { described_class.new(entry, 'ldapmain') }
+ .to raise_error(Gitlab::LDAP::Person::InvalidEntryError)
+ end
+ end
+
describe '#name' do
it 'uses the configured name attribute and handles values as an array' do
name = 'John Doe'
@@ -72,6 +103,44 @@ describe Gitlab::LDAP::Person do
end
end
+ describe '#username' do
+ context 'with default uid username attribute' do
+ let(:username_attribute) { 'uid' }
+
+ it 'returns the proper username value' do
+ attr_value = 'johndoe'
+ entry[username_attribute] = attr_value
+ person = described_class.new(entry, 'ldapmain')
+
+ expect(person.username).to eq(attr_value)
+ end
+ end
+
+ context 'with a different username attribute' do
+ let(:username_attribute) { 'sAMAccountName' }
+
+ it 'returns the proper username value' do
+ attr_value = 'johndoe'
+ entry[username_attribute] = attr_value
+ person = described_class.new(entry, 'ldapmain')
+
+ expect(person.username).to eq(attr_value)
+ end
+ end
+
+ context 'with a non-standard username attribute' do
+ let(:username_attribute) { 'mail' }
+
+ it 'returns the proper username value' do
+ attr_value = 'john.doe@example.com'
+ entry[username_attribute] = attr_value
+ person = described_class.new(entry, 'ldapmain')
+
+ expect(person.username).to eq(attr_value)
+ end
+ end
+ end
+
def assert_generic_test(test_description, got, expected)
test_failure_message = "Failed test description: '#{test_description}'\n\n expected: #{expected}\n got: #{got}"
expect(got).to eq(expected), test_failure_message
diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb
index 6334bcd0156..45fff4c5787 100644
--- a/spec/lib/gitlab/o_auth/user_spec.rb
+++ b/spec/lib/gitlab/o_auth/user_spec.rb
@@ -275,6 +275,26 @@ describe Gitlab::OAuth::User do
end
end
+ context 'and a corresponding LDAP person with a non-default username' do
+ before do
+ allow(ldap_user).to receive(:uid) { uid }
+ allow(ldap_user).to receive(:username) { 'johndoe@example.com' }
+ allow(ldap_user).to receive(:email) { %w(johndoe@example.com john2@example.com) }
+ allow(ldap_user).to receive(:dn) { dn }
+ end
+
+ context 'and no account for the LDAP user' do
+ it 'creates a user favoring the LDAP username and strips email domain' do
+ allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
+
+ oauth_user.save
+
+ expect(gl_user).to be_valid
+ expect(gl_user.username).to eql 'johndoe'
+ end
+ end
+ end
+
context "and no corresponding LDAP person" do
before do
allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(nil)
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 1d99746b09f..664698fcbaf 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -1,4 +1,5 @@
require 'rspec/mocks'
+require 'toml'
module TestEnv
extend self
@@ -147,6 +148,9 @@ module TestEnv
version: Gitlab::GitalyClient.expected_server_version,
task: "gitlab:gitaly:install[#{gitaly_dir}]") do
+ # Always re-create config, in case it's outdated. This is fast anyway.
+ Gitlab::SetupHelper.create_gitaly_configuration(gitaly_dir, force: true)
+
start_gitaly(gitaly_dir)
end
end
@@ -347,6 +351,9 @@ module TestEnv
end
def component_needs_update?(component_folder, expected_version)
+ # Allow local overrides of the component for tests during development
+ return false if Rails.env.test? && File.symlink?(component_folder)
+
version = File.read(File.join(component_folder, 'VERSION')).strip
# Notice that this will always yield true when using branch versions
diff --git a/spec/tasks/gitlab/git_rake_spec.rb b/spec/tasks/gitlab/git_rake_spec.rb
new file mode 100644
index 00000000000..dacc5dc5ae7
--- /dev/null
+++ b/spec/tasks/gitlab/git_rake_spec.rb
@@ -0,0 +1,38 @@
+require 'rake_helper'
+
+describe 'gitlab:git rake tasks' do
+ before do
+ Rake.application.rake_require 'tasks/gitlab/git'
+
+ storages = { 'default' => { 'path' => Settings.absolute('tmp/tests/default_storage') } }
+
+ FileUtils.mkdir_p(Settings.absolute('tmp/tests/default_storage/@hashed/1/2/test.git'))
+ allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
+ allow_any_instance_of(String).to receive(:color) { |string, _color| string }
+
+ stub_warn_user_is_not_gitlab
+ end
+
+ after do
+ FileUtils.rm_rf(Settings.absolute('tmp/tests/default_storage'))
+ end
+
+ describe 'fsck' do
+ it 'outputs the integrity check for a repo' do
+ expect { run_rake_task('gitlab:git:fsck') }.to output(/Performed Checking integrity at .*@hashed\/1\/2\/test.git/).to_stdout
+ end
+
+ it 'errors out about config.lock issues' do
+ FileUtils.touch(Settings.absolute('tmp/tests/default_storage/@hashed/1/2/test.git/config.lock'))
+
+ expect { run_rake_task('gitlab:git:fsck') }.to output(/file exists\? ... yes/).to_stdout
+ end
+
+ it 'errors out about ref lock issues' do
+ FileUtils.mkdir_p(Settings.absolute('tmp/tests/default_storage/@hashed/1/2/test.git/refs/heads'))
+ FileUtils.touch(Settings.absolute('tmp/tests/default_storage/@hashed/1/2/test.git/refs/heads/blah.lock'))
+
+ expect { run_rake_task('gitlab:git:fsck') }.to output(/Ref lock files exist:/).to_stdout
+ end
+ end
+end