Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into 41563-fix-branch-creation-from-issue-in-firefox
This commit is contained in:
commit
578c930f41
|
@ -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);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
<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()">
|
||||
|
||||
<template slot="body" slot-scope="props">
|
||||
<p v-html="props.text"></p>
|
||||
<template slot="body" slot-scope="props">
|
||||
<p v-html="props.text"></p>
|
||||
|
||||
<form
|
||||
ref="form"
|
||||
:action="actionUrl"
|
||||
method="post">
|
||||
<form
|
||||
ref="form"
|
||||
:action="actionUrl"
|
||||
method="post">
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="_method"
|
||||
value="delete" />
|
||||
<input
|
||||
type="hidden"
|
||||
name="authenticity_token"
|
||||
:value="csrfToken" />
|
||||
<input
|
||||
type="hidden"
|
||||
name="_method"
|
||||
value="delete" />
|
||||
<input
|
||||
type="hidden"
|
||||
name="authenticity_token"
|
||||
:value="csrfToken" />
|
||||
|
||||
<p id="input-label" v-html="inputLabel"></p>
|
||||
<p id="input-label" v-html="inputLabel"></p>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
|
||||
</modal>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-danger"
|
||||
@click="toggleOpen(true)">
|
||||
{{ s__('Profiles|Delete account') }}
|
||||
</button>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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">×</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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.")})
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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'
|
|
@ -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'
|
|
@ -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')
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix gitlab-rake gitlab:import:repos import schedule
|
||||
merge_request: 15931
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add online and status attribute to runner api entity
|
||||
merge_request: 11750
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix import project url not updating project name
|
||||
merge_request: 16120
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Modify `LDAP::Person` to return username value based on attributes
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add id to modal.vue to support data-toggle="modal"
|
||||
merge_request: 16189
|
||||
author:
|
||||
type: other
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -862,6 +862,8 @@ module API
|
|||
expose :active
|
||||
expose :is_shared
|
||||
expose :name
|
||||
expose :online?, as: :online
|
||||
expose :status
|
||||
end
|
||||
|
||||
class RunnerDetails < Runner
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -47,18 +47,12 @@ 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);
|
||||
|
||||
vm.leaveGroup();
|
||||
|
||||
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);
|
||||
expect(vm.modalStatus).toBeFalsy();
|
||||
expect(eventHub.$emit).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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();
|
||||
})
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
|
|
|
@ -57,15 +57,16 @@ 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();
|
||||
})
|
||||
|
|
|
@ -2,11 +2,65 @@ import Vue from 'vue';
|
|||
import modal from '~/vue_shared/components/modal.vue';
|
||||
import mountComponent from '../../helpers/vue_mount_component_helper';
|
||||
|
||||
describe('Modal', () => {
|
||||
it('does not render a primary button if no primaryButtonLabel', () => {
|
||||
const modalComponent = Vue.extend(modal);
|
||||
const vm = mountComponent(modalComponent);
|
||||
const modalComponent = Vue.extend(modal);
|
||||
|
||||
expect(vm.$el.querySelector('.js-primary-button')).toBeNull();
|
||||
describe('Modal', () => {
|
||||
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());
|
||||
|
||||
modalButton.click();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue