Commit f68107c4 authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2017-12-16

# Conflicts:
#	.rubocop.yml
#	app/controllers/concerns/service_params.rb
#	config/application.rb
#	doc/api/groups.md
#	lib/api/helpers.rb
#	lib/extracts_path.rb
#	lib/gitlab/shell.rb
#	lib/gitlab/slash_commands/presenters/issue_base.rb
#	spec/lib/gitlab/shell_spec.rb
#	spec/support/stub_configuration.rb

[ci skip]
parents a14b25c1 258633a6
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.5-golang-1.8-git-2.13-chrome-62.0-node-8.x-yarn-1.2-postgresql-9.6"
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.5-golang-1.8-git-2.14-chrome-63.0-node-8.x-yarn-1.2-postgresql-9.6"
.default-cache: &default-cache
key: "ruby-235-with-yarn"
......
......@@ -1197,10 +1197,15 @@ Gitlab/ModuleWithInstanceVariables:
Exclude:
# We ignore Rails helpers right now because it's hard to workaround it
- app/helpers/**/*_helper.rb
<<<<<<< HEAD
- ee/app/helpers/**/*_helper.rb
# We ignore Rails mailers right now because it's hard to workaround it
- app/mailers/emails/**/*.rb
- ee/**/emails/**/*.rb
=======
# We ignore Rails mailers right now because it's hard to workaround it
- app/mailers/emails/**/*.rb
>>>>>>> upstream/master
# We ignore spec helpers because it usually doesn't matter
- spec/support/**/*.rb
- features/steps/**/*.rb
......
......@@ -416,7 +416,7 @@ group :ed25519 do
end
# Gitaly GRPC client
gem 'gitaly-proto', '~> 0.59.0', require: 'gitaly'
gem 'gitaly-proto', '~> 0.61.0', require: 'gitaly'
gem 'toml-rb', '~> 0.3.15', require: false
......
......@@ -308,7 +308,7 @@ GEM
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gherkin-ruby (0.3.2)
gitaly-proto (0.59.0)
gitaly-proto (0.61.0)
google-protobuf (~> 3.1)
grpc (~> 1.0)
github-linguist (4.7.6)
......@@ -1077,7 +1077,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0)
gitaly-proto (~> 0.59.0)
gitaly-proto (~> 0.61.0)
github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-license (~> 1.0)
......
/* eslint-disable no-param-reassign, class-methods-use-this */
/* global Pager */
import Cookies from 'js-cookie';
import Pager from './pager';
import { localTimeAgo } from './lib/utils/datetime_utility';
export default class Activities {
......
import { n__ } from '../locale';
import { convertPermissionToBoolean } from '../lib/utils/common_utils';
export default class SecretValues {
constructor(container) {
this.container = container;
}
init() {
this.values = this.container.querySelectorAll('.js-secret-value');
this.placeholders = this.container.querySelectorAll('.js-secret-value-placeholder');
this.revealButton = this.container.querySelector('.js-secret-value-reveal-button');
this.revealText = n__('Reveal value', 'Reveal values', this.values.length);
this.hideText = n__('Hide value', 'Hide values', this.values.length);
const isRevealed = convertPermissionToBoolean(this.revealButton.dataset.secretRevealStatus);
this.updateDom(isRevealed);
this.revealButton.addEventListener('click', this.onRevealButtonClicked.bind(this));
}
onRevealButtonClicked() {
const previousIsRevealed = convertPermissionToBoolean(
this.revealButton.dataset.secretRevealStatus,
);
this.updateDom(!previousIsRevealed);
}
updateDom(isRevealed) {
this.values.forEach((value) => {
value.classList.toggle('hide', !isRevealed);
});
this.placeholders.forEach((placeholder) => {
placeholder.classList.toggle('hide', isRevealed);
});
this.revealButton.textContent = isRevealed ? this.hideText : this.revealText;
this.revealButton.dataset.secretRevealStatus = isRevealed;
}
}
/* eslint-disable func-names, wrap-iife, consistent-return,
no-return-assign, no-param-reassign, one-var-declaration-per-line, no-unused-vars,
prefer-template, object-shorthand, prefer-arrow-callback */
/* global Pager */
import { pluralize } from './lib/utils/text_utility';
import { localTimeAgo } from './lib/utils/datetime_utility';
import Pager from './pager';
export default (function () {
const CommitsList = {};
......
......@@ -7,8 +7,8 @@ import IssuableForm from './issuable_form';
import LabelsSelect from './labels_select';
/* global MilestoneSelect */
import NewBranchForm from './new_branch_form';
/* global NotificationsForm */
/* global NotificationsDropdown */
import NotificationsForm from './notifications_form';
import notificationsDropdown from './notifications_dropdown';
import groupAvatar from './group_avatar';
import GroupLabelSubscription from './group_label_subscription';
/* global LineHighlighter */
......@@ -39,6 +39,7 @@ import Flash from './flash';
import CommitsList from './commits';
import Issue from './issue';
import BindInOut from './behaviors/bind_in_out';
import SecretValues from './behaviors/secret_values';
import DeleteModal from './branches/branches_delete_modal';
import Group from './group';
import GroupsList from './groups_list';
......@@ -95,7 +96,6 @@ import memberExpirationDate from './member_expiration_date';
import DueDateSelectors from './due_date_select';
import Diff from './diff';
import ProjectLabelSubscription from './project_label_subscription';
import ProjectVariables from './project_variables';
import SearchAutocomplete from './search_autocomplete';
import Activities from './activities';
......@@ -455,7 +455,7 @@ import initGroupAnalytics from './init_group_analytics';
const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup');
shortcut_handler = new ShortcutsNavigation();
new NotificationsForm();
new NotificationsDropdown();
notificationsDropdown();
new ProjectsList();
if (newGroupChildWrapper) {
......@@ -589,8 +589,18 @@ import initGroupAnalytics from './init_group_analytics';
case 'projects:settings:ci_cd:show':
// Initialize expandable settings panels
initSettingsPanels();
const runnerToken = document.querySelector('.js-secret-runner-token');
if (runnerToken) {
const runnerTokenSecretValue = new SecretValues(runnerToken);
runnerTokenSecretValue.init();
}
case 'groups:settings:ci_cd:show':
new ProjectVariables();
const secretVariableTable = document.querySelector('.js-secret-variable-table');
if (secretVariableTable) {
const secretVariableTableValues = new SecretValues(secretVariableTable);
secretVariableTableValues.init();
}
break;
case 'ci:lints:create':
case 'ci:lints:show':
......@@ -702,7 +712,7 @@ import initGroupAnalytics from './init_group_analytics';
break;
case 'profiles':
new NotificationsForm();
new NotificationsDropdown();
notificationsDropdown();
break;
case 'projects':
new Project();
......@@ -726,7 +736,7 @@ import initGroupAnalytics from './init_group_analytics';
case 'show':
new Star();
new ProjectNew();
new NotificationsDropdown();
notificationsDropdown();
break;
case 'wikis':
new Wikis();
......
......@@ -34,16 +34,11 @@ import { getLocationHash, visitUrl } from './lib/utils/url_utility';
import './behaviors/';
// everything else
import './activities';
import './admin';
import loadAwardsHandler from './awards_handler';
import bp from './breakpoints';
import './confirm_danger_modal';
import Flash, { removeFlashClickListener } from './flash';
import './gl_dropdown';
import './gl_field_error';
import './gl_field_errors';
import './gl_form';
import initTodoToggle from './header';
import initImporterStatus from './importer_status';
import './layout_nav';
......@@ -54,11 +49,7 @@ import './merge_request';
import './merge_request_tabs';
import './milestone_select';
import './notes';
import './notifications_dropdown';
import './notifications_form';
import './pager';
import './preview_markdown';
import './project_import';
import './projects_dropdown';
import './render_gfm';
import './right_sidebar';
......
......@@ -3,7 +3,7 @@
import 'vendor/jquery.waitforimages';
import TaskList from './task_list';
import './merge_request_tabs';
import MergeRequestTabs from './merge_request_tabs';
import IssuablesHelper from './helpers/issuables_helper';
import { addDelimiter } from './lib/utils/text_utility';
......@@ -51,7 +51,7 @@ import { addDelimiter } from './lib/utils/text_utility';
if (window.mrTabs) {
window.mrTabs.unbindEvents();
}
window.mrTabs = new gl.MergeRequestTabs(this.opts);
window.mrTabs = new MergeRequestTabs(this.opts);
};
MergeRequest.prototype.showAllCommits = function() {
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, no-unused-vars, consistent-return, prefer-arrow-callback, no-else-return, max-len */
import Flash from './flash';
(function() {
this.NotificationsDropdown = (function() {
function NotificationsDropdown() {
$(document).off('click', '.update-notification').on('click', '.update-notification', function(e) {
var form, label, notificationLevel;
e.preventDefault();
if ($(this).is('.is-active') && $(this).data('notification-level') === 'custom') {
return;
}
notificationLevel = $(this).data('notification-level');
label = $(this).data('notification-title');
form = $(this).parents('.notification-form:first');
form.find('.js-notification-loading').toggleClass('fa-bell fa-spin fa-spinner');
form.find('#notification_setting_level').val(notificationLevel);
return form.submit();
});
$(document).off('ajax:success', '.notification-form').on('ajax:success', '.notification-form', function(e, data) {
if (data.saved) {
return $(e.currentTarget).closest('.js-notification-dropdown').replaceWith(data.html);
} else {
return new Flash('Failed to save new settings', 'alert');
}
});
export default function notificationsDropdown() {
$(document).on('click', '.update-notification', function updateNotificationCallback(e) {
e.preventDefault();
if ($(this).is('.is-active') && $(this).data('notification-level') === 'custom') {
return;
}
return NotificationsDropdown;
})();
}).call(window);
const notificationLevel = $(this).data('notification-level');
const form = $(this).parents('.notification-form:first');
form.find('.js-notification-loading').toggleClass('fa-bell fa-spin fa-spinner');
form.find('#notification_setting_level').val(notificationLevel);
form.submit();
});
$(document).on('ajax:success', '.notification-form', (e, data) => {
if (data.saved) {
$(e.currentTarget).closest('.js-notification-dropdown').replaceWith(data.html);
} else {
Flash('Failed to save new settings', 'alert');
}
});
}
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, newline-per-chained-call, comma-dangle, consistent-return, prefer-arrow-callback, max-len */
(function() {
this.NotificationsForm = (function() {
function NotificationsForm() {
this.toggleCheckbox = this.toggleCheckbox.bind(this);
this.removeEventListeners();
this.initEventListeners();
}
export default class NotificationsForm {
constructor() {
this.toggleCheckbox = this.toggleCheckbox.bind(this);
this.initEventListeners();
}
NotificationsForm.prototype.removeEventListeners = function() {
return $(document).off('change', '.js-custom-notification-event');
};
initEventListeners() {
$(document).on('change', '.js-custom-notification-event', this.toggleCheckbox);
}
NotificationsForm.prototype.initEventListeners = function() {
return $(document).on('change', '.js-custom-notification-event', this.toggleCheckbox);
};
toggleCheckbox(e) {
const $checkbox = $(e.currentTarget);
const $parent = $checkbox.closest('.checkbox');
NotificationsForm.prototype.toggleCheckbox = function(e) {
var $checkbox, $parent;
$checkbox = $(e.currentTarget);
$parent = $checkbox.closest('.checkbox');
return this.saveEvent($checkbox, $parent);
};
this.saveEvent($checkbox, $parent);
}
NotificationsForm.prototype.showCheckboxLoadingSpinner = function($parent) {
return $parent.addClass('is-loading').find('.custom-notification-event-loading').removeClass('fa-check').addClass('fa-spin fa-spinner').removeClass('is-done');
};
// eslint-disable-next-line class-methods-use-this
showCheckboxLoadingSpinner($parent) {
$parent.addClass('is-loading')
.find('.custom-notification-event-loading')
.removeClass('fa-check')
.addClass('fa-spin fa-spinner')
.removeClass('is-done');
}
NotificationsForm.prototype.saveEvent = function($checkbox, $parent) {
var form;
form = $parent.parents('form:first');
return $.ajax({
url: form.attr('action'),
method: form.attr('method'),
dataType: 'json',
data: form.serialize(),
beforeSend: (function(_this) {
return function() {
return _this.showCheckboxLoadingSpinner($parent);
};
})(this)
}).done(function(data) {
$checkbox.enable();
if (data.saved) {
$parent.find('.custom-notification-event-loading').toggleClass('fa-spin fa-spinner fa-check is-done');
return setTimeout(function() {
return $parent.removeClass('is-loading').find('.custom-notification-event-loading').toggleClass('fa-spin fa-spinner fa-check is-done');
}, 2000);
}
});
};
saveEvent($checkbox, $parent) {
const form = $parent.parents('form:first');
return NotificationsForm;
})();
}).call(window);
return $.ajax({
url: form.attr('action'),
method: form.attr('method'),
dataType: 'json',
data: form.serialize(),
beforeSend: () => {
this.showCheckboxLoadingSpinner($parent);
},
}).done((data) => {
$checkbox.enable();
if (data.saved) {
$parent.find('.custom-notification-event-loading').toggleClass('fa-spin fa-spinner fa-check is-done');
setTimeout(() => {
$parent.removeClass('is-loading')
.find('.custom-notification-event-loading')
.toggleClass('fa-spin fa-spinner fa-check is-done');
}, 2000);
}
});
}
}
import { getParameterByName } from '~/lib/utils/common_utils';
import { removeParams } from './lib/utils/url_utility';
(() => {
const ENDLESS_SCROLL_BOTTOM_PX = 400;
const ENDLESS_SCROLL_FIRE_DELAY_MS = 1000;
const ENDLESS_SCROLL_BOTTOM_PX = 400;
const ENDLESS_SCROLL_FIRE_DELAY_MS = 1000;
const Pager = {
init(limit = 0, preload = false, disable = false, prepareData = $.noop, callback = $.noop) {
this.url = $('.content_list').data('href') || removeParams(['limit', 'offset']);
this.limit = limit;
this.offset = parseInt(getParameterByName('offset'), 10) || this.limit;
this.disable = disable;
this.prepareData = prepareData;
this.callback = callback;
this.loading = $('.loading').first();
if (preload) {
this.offset = 0;
this.getOld();
}
this.initLoadMore();
},
export default {
init(limit = 0, preload = false, disable = false, prepareData = $.noop, callback = $.noop) {
this.url = $('.content_list').data('href') || removeParams(['limit', 'offset']);
this.limit = limit;
this.offset = parseInt(getParameterByName('offset'), 10) || this.limit;
this.disable = disable;
this.prepareData = prepareData;
this.callback = callback;
this.loading = $('.loading').first();
if (preload) {
this.offset = 0;
this.getOld();
}
this.initLoadMore();
},
getOld() {
this.loading.show();
$.ajax({
type: 'GET',
url: this.url,
data: `limit=${this.limit}&offset=${this.offset}`,
dataType: 'json',
error: () => this.loading.hide(),
success: (data) => {
this.append(data.count, this.prepareData(data.html));
this.callback();
getOld() {
this.loading.show();
$.ajax({
type: 'GET',
url: this.url,
data: `limit=${this.limit}&offset=${this.offset}`,
dataType: 'json',
error: () => this.loading.hide(),
success: (data) => {
this.append(data.count, this.prepareData(data.html));
this.callback();
// keep loading until we've filled the viewport height
if (!this.disable && !this.isScrollable()) {
this.getOld();
} else {
this.loading.hide();
}
},
});
},
// keep loading until we've filled the viewport height
if (!this.disable && !this.isScrollable()) {
this.getOld();
} else {
this.loading.hide();
}
},
});
},
append(count, html) {
$('.content_list').append(html);
if (count > 0) {
this.offset += count;
} else {
this.disable = true;
}
},
append(count, html) {
$('.content_list').append(html);
if (count > 0) {
this.offset += count;
} else {
this.disable = true;
}
},
isScrollable() {
const $w = $(window);
return $(document).height() > $w.height() + $w.scrollTop() + ENDLESS_SCROLL_BOTTOM_PX;
},
isScrollable() {
const $w = $(window);
return $(document).height() > $w.height() + $w.scrollTop() + ENDLESS_SCROLL_BOTTOM_PX;
},
initLoadMore() {
$(document).unbind('scroll');
$(document).endlessScroll({
bottomPixels: ENDLESS_SCROLL_BOTTOM_PX,
fireDelay: ENDLESS_SCROLL_FIRE_DELAY_MS,
fireOnce: true,
ceaseFire: () => this.disable === true,
callback: () => {
if (!this.loading.is(':visible')) {
this.loading.show();
this.getOld();
}
},
});
},
};
window.Pager = Pager;
})();
initLoadMore() {
$(document).unbind('scroll');
$(document).endlessScroll({
bottomPixels: ENDLESS_SCROLL_BOTTOM_PX,
fireDelay: ENDLESS_SCROLL_FIRE_DELAY_MS,
fireOnce: true,
ceaseFire: () => this.disable === true,
callback: () => {
if (!this.loading.is(':visible')) {
this.loading.show();
this.getOld();
}
},
});
},
};
const HIDDEN_VALUE_TEXT = '******';
export default class ProjectVariables {
constructor() {
this.$revealBtn = $('.js-btn-toggle-reveal-values');
this.$revealBtn.on('click', this.toggleRevealState.bind(this));
}
toggleRevealState(e) {
e.preventDefault();
const oldStatus = this.$revealBtn.attr('data-status');
let newStatus = 'hidden';
let newAction = 'Reveal Values';
if (oldStatus === 'hidden') {
newStatus = 'revealed';
newAction = 'Hide Values';
}
this.$revealBtn.attr('data-status', newStatus);
const $variables = $('.variable-value');
$variables.each((_, variable) => {
const $variable = $(variable);
let newText = HIDDEN_VALUE_TEXT;
if (newStatus === 'revealed') {
newText = $variable.attr('data-value');
}
$variable.text(newText);
});
this.$revealBtn.text(newAction);
}
}
......@@ -65,10 +65,12 @@ export default {
<div class="mr-widget-body media">
<status-icon status="success" />
<div class="media-body">
<h4>
Set by
<mr-widget-author :author="mr.setToMWPSBy" />
to be merged automatically when the pipeline succeeds
<h4 class="flex-container-block">
<span class="append-right-10">
Set by
<mr-widget-author :author="mr.setToMWPSBy" />
to be merged automatically when the pipeline succeeds
</span>
<a
v-if="mr.canCancelAutomaticMerge"
@click.prevent="cancelAutomaticMerge"
......@@ -94,8 +96,13 @@ export default {
<p v-if="mr.shouldRemoveSourceBranch">
The source branch will be removed
</p>
<p v-else>
The source branch will not be removed
<p
v-else
class="flex-container-block"
>
<span class="append-right-10">
The source branch will not be removed
</span>
<a
v-if="canRemoveSourceBranch"
:disabled="isRemovingSourceBranch"
......
<script>
import { s__ } from '../../locale';
import icon from './icon.vue';
import loadingIcon from './loading_icon.vue';
const ICON_ON = 'status_success_borderless';
const ICON_OFF = 'status_failed_borderless';
const LABEL_ON = s__('ToggleButton|Toggle Status: ON');
const LABEL_OFF = s__('ToggleButton|Toggle Status: OFF');
export default {
props: {
name: {
......@@ -22,19 +29,10 @@
required: false,
default: false,
},
enabledText: {
type: String,
required: false,
default: 'Enabled',
},
disabledText: {
type: String,
required: false,
default: 'Disabled',
},
},
components: {
icon,
loadingIcon,
},
......@@ -43,6 +41,15 @@
event: 'change',
},
computed: {
toggleIcon() {
return this.value ? ICON_ON : ICON_OFF;
},
ariaLabel() {
return this.value ? LABEL_ON : LABEL_OFF;
},
},
methods: {
toggleFeature() {
if (!this.disabledInput) this.$emit('change', !this.value);
......@@ -60,10 +67,8 @@
/>
<button
type="button"
aria-label="Toggle"
class="project-feature-toggle"
:data-enabled-text="enabledText"
:data-disabled-text="disabledText"
:aria-label="ariaLabel"
:class="{
'is-checked': value,
'is-disabled': disabledInput,
......@@ -72,6 +77,11 @@
@click="toggleFeature"
>
<loadingIcon class="loading-icon" />
<span class="toggle-icon">
<icon
css-classes="toggle-icon-svg"
:name="toggleIcon"/>
</span>
</button>
</label>
</template>
......@@ -396,3 +396,8 @@ span.idiff {
.file-fork-suggestion-note {
margin-right: 1.5em;
}
.label-lfs {
color: $common-gray-light;
border: 1px solid $common-gray-light;
}
......@@ -27,7 +27,7 @@
border: 0;
outline: 0;
display: block;
width: 100px;
width: 50px;
height: 24px;
cursor: pointer;
user-select: none;
......@@ -42,31 +42,31 @@
background: none;
}
&::before {
color: $feature-toggle-text-color;
font-size: 12px;
line-height: 24px;
position: absolute;
top: 0;
left: 25px;
right: 5px;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
animation: animate-disabled .2s ease-in;
content: attr(data-disabled-text);
}
&::after {
.toggle-icon {
position: relative;
display: block;
content: "";
width: 22px;
height: 18px;
left: 0;
border-radius: 9px;
background: $feature-toggle-color;
transition: all .2s ease;
&,
.toggle-icon-svg {
width: 18px;
height: 18px;
}
.toggle-icon-svg {
fill: $feature-toggle-color-disabled;
}
.toggle-status-checked {
display: none;
}
.toggle-status-unchecked {
display: inline;
}
}
.loading-icon {
......@@ -77,11 +77,10 @@
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
&.is-loading {
&::before {
.toggle-icon {
display: none;
}
......@@ -100,15 +99,20 @@
&.is-checked {
background: $feature-toggle-color-enabled;
&::before {
left: 5px;
right: 25px;
animation: animate-enabled .2s ease-in;
content: attr(data-enabled-text);
}
.toggle-icon {
left: calc(100% - 18px);
&::after {
left: calc(100% - 22px);
.toggle-icon-svg {
fill: $feature-toggle-color-enabled;
}
.toggle-status-checked {
display: inline;
}
.toggle-status-unchecked {
display: none;
}
}
}
......
......@@ -68,7 +68,11 @@ module ServiceParams
def service_params
dynamic_params = @service.event_channel_names + @service.event_names # rubocop:disable Gitlab/ModuleWithInstanceVariables
<<<<<<< HEAD
service_params = params.permit(:id, service: allowed_service_params + dynamic_params)
=======
service_params = params.permit(:id, service: ALLOWED_PARAMS_CE + dynamic_params)
>>>>>>> upstream/master
if service_params[:service].is_a?(Hash)
FILTER_BLANK_PARAMS.each do |param|
......
......@@ -110,7 +110,7 @@ class Projects::JobsController < Projects::ApplicationController
def erase
if @build.erase(erased_by: current_user)
redirect_to project_job_path(project, @build),
notice: "Build has been successfully erased!"
notice: "Job has been successfully erased!"
else
respond_422
end
......
......@@ -11,7 +11,10 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
before_action :build_merge_request, except: [:create]
def new
define_new_vars
# n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/40934
Gitlab::GitalyClient.allow_n_plus_1_calls do
define_new_vars
end
end
def create
......
......@@ -26,6 +26,7 @@ class Projects::TreeController < Projects::ApplicationController
respond_to do |format|
format.html do
lfs_blob_ids
@last_commit = @repository.last_commit_for_path(@commit.id, @tree.path) || @commit
end
......
......@@ -11,6 +11,7 @@ class ProjectsController < Projects::ApplicationController
before_action :assign_ref_vars, only: [:show], if: :repo_exists?
before_action :assign_tree_vars, only: [:show], if: [:repo_exists?, :project_view_files?]
before_action :tree, only: [:show], if: [:repo_exists?, :project_view_files?]
before_action :lfs_blob_ids, only: [:show], if: [:repo_exists?, :project_view_files?]
before_action :project_export_enabled, only: [:export, :download_export, :remove_export, :generate_new_export]
# Authorize
......
......@@ -16,11 +16,11 @@ class Identity < ActiveRecord::Base
end
def ldap?
provider.starts_with?('ldap')
Gitlab::OAuth::Provider.ldap_provider?(provider)
end
def self.normalize_uid(provider, uid)
if provider.to_s.starts_with?('ldap')
if Gitlab::OAuth::Provider.ldap_provider?(provider)
Gitlab::LDAP::Person.normalize_dn(uid)
else
uid.to_s
......
......@@ -983,7 +983,7 @@ class Repository
def merge_base(first_commit_id, second_commit_id)
first_commit_id = commit(first_commit_id).try(:id) || first_commit_id
second_commit_id = commit(second_commit_id).try(:id) || second_commit_id
rugged.merge_base(first_commit_id, second_commit_id)
raw_repository.merge_base(first_commit_id, second_commit_id)
rescue Rugged::ReferenceError
nil
end
......
......@@ -757,7 +757,7 @@ class User < ActiveRecord::Base
def ldap_user?
if identities.loaded?
identities.find { |identity| identity.provider.start_with?('ldap') && !identity.extern_uid.nil? }
identities.find { |identity| Gitlab::OAuth::Provider.ldap_provider?(identity.provider) && !identity.extern_uid.nil? }
else
identities.exists?(["provider LIKE ? AND extern_uid IS NOT NULL", "ldap%"])
end
......
......@@ -6,11 +6,11 @@ class UserSyncedAttributesMetadata < ActiveRecord::Base
SYNCABLE_ATTRIBUTES = %i[name email location].freeze
def read_only?(attribute)
Gitlab.config.omniauth.sync_profile_from_provider && synced?(attribute)
sync_profile_from_provider? && synced?(attribute)
end
def read_only_attributes
return [] unless Gitlab.config.omniauth.sync_profile_from_provider
return [] unless sync_profile_from_provider?
SYNCABLE_ATTRIBUTES.select { |key| synced?(key) }
end
......@@ -22,4 +22,10 @@ class UserSyncedAttributesMetadata < ActiveRecord::Base
def set_attribute_synced(attribute, value)
write_attribute("#{attribute}_synced", value)
end
private
def sync_profile_from_provider?
Gitlab::OAuth::Provider.sync_profile_from_provider?(provider)
end
end
......@@ -15,7 +15,7 @@
Unable to collect CPU info
.col-sm-4
.light-well
%h4 Memory
%h4 Memory Usage
.data
- if @memory
%h1 #{number_to_human_size(@memory.active_bytes)} / #{number_to_human_size(@memory.total_bytes)}
......@@ -24,7 +24,7 @@
Unable to collect memory info
.col-sm-4
.light-well
%h4 Disks
%h4 Disk Usage
.data
- @disks.each do |disk|
%h1 #{number_to_human_size(disk[:bytes_used])} / #{number_to_human_size(disk[:bytes_total])}
......@@ -34,4 +34,4 @@
.light-well
%h4 Uptime
.data
%h1= time_ago_with_tooltip(Rails.application.config.booted_at)
%h1= distance_of_time_in_words_to_now(Rails.application.config.booted_at)
......@@ -161,7 +161,6 @@
%ul
%li User will not be able to login
%li User will not be able to access git repositories
%li User will be removed from joined projects and groups
%li Personal projects will be left
%li Owned groups will be left
%br
......
......@@ -10,5 +10,7 @@
%p.settings-message.text-center.append-bottom-0
No variables found, add one with the form above.
- else
= render "ci/variables/table"
%button.btn.btn-info.js-btn-toggle-reveal-values{ "data-status" => 'hidden' } Reveal Values
.js-secret-variable-table
= render "ci/variables/table"
%button.btn.btn-info.js-secret-value-reveal-button{ data: { secret_reveal_status: 'false' } }
= n_('Reveal value', 'Reveal values', @variables.size)
......@@ -17,7 +17,11 @@
- if variable.id?
%tr
%td.variable-key= variable.key
%td.variable-value{ "data-value" => variable.value }******
%td.variable-value
%span.js-secret-value-placeholder
= '*' * 6
%span.hide.js-secret-value
= variable.value
%td.variable-protected= Gitlab::Utils.boolean_to_yes_no(variable.protected)
- if @variable.respond_to?(:environment_scope) && @project.feature_available?(:variable_environment_scope)
%td.variable-environment-scope= variable.environment_scope
......
......@@ -8,7 +8,7 @@
%br
%span.descr
Pipelines need to be configured to enable this feature.
= link_to icon('question-circle'), help_page_path('user/project/merge_requests/merge_when_pipeline_succeeds', anchor: 'only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds')
= link_to icon('question-circle'), help_page_path('user/project/merge_requests/merge_when_pipeline_succeeds', anchor: 'only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds'), target: '_blank'
.checkbox
= form.label :only_allow_merge_if_all_discussions_are_resolved do
= form.check_box :only_allow_merge_if_all_discussions_are_resolved
......
......@@ -8,3 +8,6 @@
%small
= number_to_human_size(blob.raw_size)
- if blob.stored_externally? && blob.external_storage == :lfs
%span.label.label-lfs.append-right-5 LFS
......@@ -16,7 +16,8 @@
class: "js-toggle-cluster-list project-feature-toggle #{'is-checked' if cluster.enabled?} #{'is-disabled' if !cluster.can_toggle_cluster?}",
"aria-label": s_("ClusterIntegration|Toggle Cluster"),
disabled: !cluster.can_toggle_cluster?,
data: { "enabled-text": s_("ClusterIntegration|Active"),
"disabled-text": s_("ClusterIntegration|Inactive"),
endpoint: namespace_project_cluster_path(@project.namespace, @project, cluster, format: :json) } }
data: { endpoint: namespace_project_cluster_path(@project.namespace, @project, cluster, format: :json) } }
= icon("spinner spin", class: "loading-icon")
%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')
......@@ -7,8 +7,10 @@
%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),
data: { "enabled-text": s_("ClusterIntegration|Active"), "disabled-text": s_("ClusterIntegration|Inactive"), } }
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
......
......@@ -40,10 +40,14 @@
= form.text_field :domain, class: 'form-control', placeholder: 'domain.com'
%hr
.form-group.append-bottom-default
.form-group.append-bottom-default.js-secret-runner-token
= f.label :runners_token, "Runner token", class: 'label-light'
= f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89'
.form-control.js-secret-value-placeholder
= '*' * 20
= f.text_field :runners_token, class: "form-control hide js-secret-value", placeholder: 'xEeFCaDAB89'
%p.help-block The secure token used by the Runner to checkout the project
%button.btn.btn-info.prepend-top-10.js-secret-value-reveal-button{ type: 'button', data: { secret_reveal_status: 'false' } }
= _('Reveal value')
%hr
.form-group
......
- is_lfs_blob = @lfs_blob_ids.include?(blob_item.id)
%tr{ class: "tree-item #{tree_hex_class(blob_item)}" }
%td.tree-item-file-name
= tree_icon(type, blob_item.mode, blob_item.name)
- file_name = blob_item.name
= link_to project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name)), class: 'str-truncated', title: file_name do
%span= file_name
- if is_lfs_blob
%span.label.label-lfs.prepend-left-5 LFS
%td.hidden-xs.tree-commit
%td.tree-time-ago.cgray.text-right
= render 'projects/tree/spinner'
---
title: Hide runner token in CI/CD settings page
merge_request:
author:
type: added
---
title: Fixes the wording of headers in system info page
merge_request: 15802
author: Gilbert Roulot
type: fixed
---
title: Update feature toggle design to use icons and make it i18n friendly
merge_request: 15904
author:
type: changed
---
title: fix button alignment on MWPS component
merge_request:
author:
type: fixed
---
title: Make sure user email is read only when synced with LDAP
merge_request: 15915
author:
type: fixed
---
title: Added badge to tree & blob views to indicate LFS tracked files
merge_request:
author:
type: added
---
title: Removed incorrect guidance stating blocked users will be removed from groups
and project as members
merge_request: 15947
author: CesarApodaca
type: fixed
......@@ -36,6 +36,7 @@ module Gitlab
config.generators.templates.push("#{config.root}/generator_templates")
<<<<<<< HEAD
# EE specific paths.
ee_paths = config.eager_load_paths.each_with_object([]) do |path, memo|
ee_path = config.root.join('ee', Pathname.new(path).relative_path_from(config.root))
......@@ -51,6 +52,8 @@ module Gitlab
#{config.root}/ee/app/helpers
])
=======
>>>>>>> upstream/master
# Rake tasks ignore the eager loading settings, so we need to set the
# autoload paths explicitly
config.autoload_paths = config.eager_load_paths.dup
......
......@@ -497,6 +497,7 @@ production: &base
# Sync user's profile from the specified Omniauth providers every time the user logs in (default: empty).
# Define the allowed providers using an array, e.g. ["cas3", "saml", "twitter"],
# or as true/false to allow all providers or none.
# When authenticating using LDAP, the user's email is always synced.
# sync_profile_from_provider: []
# Select which info to sync from the providers above. (default: email).
......
......@@ -375,7 +375,10 @@ Parameters:
| `lfs_enabled` | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group |
| `request_access_enabled` | boolean | no | Allow users to request member access. |
| `parent_id` | integer | no | The parent group id for creating nested group. |
<<<<<<< HEAD
| `shared_runners_minutes_limit` | integer | no | (admin-only) Pipeline minutes quota for this group. |
=======
>>>>>>> upstream/master
## Transfer project to group
......
......@@ -80,7 +80,7 @@ Make sure you have the right version of Git installed
# Install Git
sudo apt-get install -y git-core
# Make sure Git is version 2.13.6 or higher
# Make sure Git is version 2.14.3 or higher
git --version
Is the system packaged Git too old? Remove it and compile from source.
......
......@@ -229,16 +229,18 @@ In order to enable/disable an OmniAuth provider, go to Admin Area -> Settings ->
## Keep OmniAuth user profiles up to date
You can enable profile syncing from selected OmniAuth providers and for all or for specific user information.
When authenticating using LDAP, the user's email is always synced.
```ruby
gitlab_rails['sync_profile_from_provider'] = ['twitter', 'google_oauth2']
gitlab_rails['sync_profile_attributes'] = ['name', 'email', 'location']
```
**For installations from source**
```yaml
omniauth:
sync_profile_from_provider: ['twitter', 'google_oauth2']
sync_profile_claims_from_provider: ['email', 'location']
```
\ No newline at end of file
sync_profile_attributes: ['email', 'location']
```
......@@ -147,6 +147,10 @@ has a `.gitlab-ci.yml` or not:
All you need to do is remove your existing `.gitlab-ci.yml`, and you can even
do that in a branch to test Auto DevOps before committing to `master`.
NOTE: **Note:**
Starting with GitLab 10.3, when enabling Auto DevOps, a pipeline is
automatically run on the default branch.
NOTE: **Note:**
If you are a GitLab Administrator, you can enable Auto DevOps instance wide
in **Admin Area > Settings > Continuous Integration and Deployment**. Doing that,
......@@ -211,6 +215,18 @@ check out.
Any security warnings are also [shown in the merge request widget](../../user/project/merge_requests/sast.md).
### Auto SAST
> Introduced in [GitLab Enterprise Edition Ultimate][ee] 10.3.
Static Application Security Testing (SAST) uses the
[gl-sast Docker image](https://gitlab.com/gitlab-org/gl-sast) to run static
analysis on the current code and checks for potential security issues. Once the
report is created, it's uploaded as an artifact which you can later download and
check out.
Any security warnings are also [shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/sast.html).
### Auto Review Apps
NOTE: **Note:**
......
......@@ -434,6 +434,7 @@ module API
private
<<<<<<< HEAD
def private_token
params[APIGuard::PRIVATE_TOKEN_PARAM] || env[APIGuard::PRIVATE_TOKEN_HEADER]
end
......@@ -456,6 +457,9 @@ module API
warden.try(:authenticate) if verified_request?
end
=======
# rubocop:disable Gitlab/ModuleWithInstanceVariables
>>>>>>> upstream/master
def initial_current_user
return @initial_current_user if defined?(@initial_current_user) # rubocop:disable Gitlab/ModuleWithInstanceVariables
......@@ -465,6 +469,7 @@ module API
unauthorized!
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def sudo!
return unless sudo_identifier
......
......@@ -128,7 +128,6 @@ module ExtractsPath
@hex_path = Digest::SHA1.hexdigest(@path)
@logs_path = logs_file_project_ref_path(@project, @ref, @path)
rescue RuntimeError, NoMethodError, InvalidPathError
render_404
end
......@@ -136,6 +135,14 @@ module ExtractsPath
def tree
@tree ||= @repo.tree(@commit.id, @path) # rubocop:disable Gitlab/ModuleWithInstanceVariables
<<<<<<< HEAD
=======
end
def lfs_blob_ids
blob_ids = tree.blobs.map(&:id)
@lfs_blob_ids = Gitlab::Git::Blob.batch_lfs_pointers(@project.repository, blob_ids).map(&:id) # rubocop:disable Gitlab/ModuleWithInstanceVariables
>>>>>>> upstream/master
end
private
......
......@@ -4,11 +4,11 @@ module Gitlab
attr_reader :merge_request, :resolver
def initialize(merge_request)
source_repo = merge_request.source_project.repository.raw
our_commit = merge_request.source_branch_head.raw
their_commit = merge_request.target_branch_head.raw
target_repo = merge_request.target_project.repository.raw
@resolver = Gitlab::Git::Conflict::Resolver.new(source_repo, our_commit, target_repo, their_commit)
@source_repo = merge_request.source_project.repository.raw
@resolver = Gitlab::Git::Conflict::Resolver.new(target_repo, our_commit.id, their_commit.id)
@merge_request = merge_request
end
......@@ -18,7 +18,7 @@ module Gitlab
target_branch: merge_request.target_branch,
commit_message: commit_message || default_commit_message
}
resolver.resolve_conflicts(user, files, args)
resolver.resolve_conflicts(@source_repo, user, files, args)
ensure
@merge_request.clear_memoized_shas
end
......
......@@ -5,38 +5,31 @@ module Gitlab
ConflictSideMissing = Class.new(StandardError)
ResolutionError = Class.new(StandardError)
def initialize(repository, our_commit, target_repository, their_commit)
@repository = repository
@our_commit = our_commit.rugged_commit
def initialize(target_repository, our_commit_oid, their_commit_oid)
@target_repository = target_repository
@their_commit = their_commit.rugged_commit
@our_commit_oid = our_commit_oid
@their_commit_oid = their_commit_oid
end
def conflicts
@conflicts ||= begin
target_index = @target_repository.rugged.merge_commits(@our_commit, @their_commit)
target_index = @target_repository.rugged.merge_commits(@our_commit_oid, @their_commit_oid)
# We don't need to do `with_repo_branch_commit` here, because the target
# project always fetches source refs when creating merge request diffs.
target_index.conflicts.map do |conflict|
raise ConflictSideMissing unless conflict[:theirs] && conflict[:ours]
Gitlab::Git::Conflict::File.new(
@target_repository,
@our_commit.oid,
conflict,
target_index.merge_file(conflict[:ours][:path])[:data]
)
end
conflict_files(@target_repository, target_index)
end
end
def resolve_conflicts(user, files, source_branch:, target_branch:, commit_message:)
@repository.with_repo_branch_commit(@target_repository, target_branch) do
def resolve_conflicts(source_repository, user, files, source_branch:, target_branch:, commit_message:)
source_repository.with_repo_branch_commit(@target_repository, target_branch) do
index = source_repository.rugged.merge_commits(@our_commit_oid, @their_commit_oid)
conflicts = conflict_files(source_repository, index)
files.each do |file_params|
conflict_file = conflict_for_path(file_params[:old_path], file_params[:new_path])
conflict_file = conflict_for_path(conflicts, file_params[:old_path], file_params[:new_path])
write_resolved_file_to_index(conflict_file, file_params)
write_resolved_file_to_index(source_repository, index, conflict_file, file_params)
end
unless index.conflicts.empty?
......@@ -47,14 +40,14 @@ module Gitlab
commit_params = {
message: commit_message,
parents: [@our_commit, @their_commit].map(&:oid)
parents: [@our_commit_oid, @their_commit_oid]
}
@repository.commit_index(user, source_branch, index, commit_params)
source_repository.commit_index(user, source_branch, index, commit_params)
end
end
def conflict_for_path(old_path, new_path)
def conflict_for_path(conflicts, old_path, new_path)
conflicts.find do |conflict|
conflict.their_path == old_path && conflict.our_path == new_path
end
......@@ -62,15 +55,20 @@ module Gitlab
private
# We can only write when getting the merge index from the source
# project, because we will write to that project. We don't use this all
# the time because this fetches a ref into the source project, which
# isn't needed for reading.
def index
@index ||= @repository.rugged.merge_commits(@our_commit, @their_commit)
def conflict_files(repository, index)
index.conflicts.map do |conflict|
raise ConflictSideMissing unless conflict[:theirs] && conflict[:ours]
Gitlab::Git::Conflict::File.new(
repository,
@our_commit_oid,
conflict,
index.merge_file(conflict[:ours][:path])[:data]
)
end
end
def write_resolved_file_to_index(file, params)
def write_resolved_file_to_index(repository, index, file, params)
if params[:sections]
resolved_lines = file.resolve_lines(params[:sections])
new_file = resolved_lines.map { |line| line[:full_line] }.join("\n")
......@@ -82,7 +80,8 @@ module Gitlab
our_path = file.our_path
index.add(path: our_path, oid: @repository.rugged.write(new_file, :blob), mode: file.our_mode)
oid = repository.rugged.write(new_file, :blob)
index.add(path: our_path, oid: oid, mode: file.our_mode)
index.conflict_remove(our_path)
end
end
......
......@@ -131,7 +131,7 @@ module Gitlab
oldrev = branch.target
if oldrev == repository.rugged.merge_base(newrev, branch.target)
if oldrev == repository.merge_base(newrev, branch.target)
oldrev
else
raise Gitlab::Git::CommitError.new('Branch diverged')
......
......@@ -538,8 +538,15 @@ module Gitlab
# Returns the SHA of the most recent common ancestor of +from+ and +to+
def merge_base_commit(from, to)
rugged.merge_base(from, to)
gitaly_migrate(:merge_base) do |is_enabled|
if is_enabled
gitaly_repository_client.find_merge_base(from, to)
else
rugged.merge_base(from, to)
end
end
end
alias_method :merge_base, :merge_base_commit
# Gitaly note: JV: check gitlab-ee before removing this method.
def rugged_is_ancestor?(ancestor_id, descendant_id)
......
......@@ -69,6 +69,16 @@ module Gitlab
response.value
end
def find_merge_base(*revisions)
request = Gitaly::FindMergeBaseRequest.new(
repository: @gitaly_repo,
revisions: revisions.map { |r| GitalyClient.encode(r) }
)
response = GitalyClient.call(@storage, :repository_service, :find_merge_base, request)
response.base.presence
end
def fetch_source_branch(source_repository, source_branch, local_ref)
request = Gitaly::FetchSourceBranchRequest.new(
repository: @gitaly_repo,
......
......@@ -38,10 +38,6 @@ module Gitlab
ldap_config.block_auto_created_users
end
def sync_profile_from_provider?
true
end
def allowed?
Gitlab::LDAP::Access.allowed?(gl_user)
end
......
......@@ -19,6 +19,18 @@ module Gitlab
name.to_s.start_with?('ldap')
end
def self.sync_profile_from_provider?(provider)
return true if ldap_provider?(provider)
providers = Gitlab.config.omniauth.sync_profile_from_provider
if providers.is_a?(Array)
providers.include?(provider)
else
providers
end
end
def self.config_for(name)
name = name.to_s
if ldap_provider?(name)
......
......@@ -14,7 +14,7 @@ module Gitlab
def initialize(auth_hash)
self.auth_hash = auth_hash
update_profile if sync_profile_from_provider?
update_profile
add_or_update_user_identities
end
......@@ -197,29 +197,31 @@ module Gitlab
end
def sync_profile_from_provider?
providers = Gitlab.config.omniauth.sync_profile_from_provider
if providers.is_a?(Array)
providers.include?(auth_hash.provider)
else
providers
end
Gitlab::OAuth::Provider.sync_profile_from_provider?(auth_hash.provider)
end
def update_profile
user_synced_attributes_metadata = gl_user.user_synced_attributes_metadata || gl_user.build_user_synced_attributes_metadata
UserSyncedAttributesMetadata::SYNCABLE_ATTRIBUTES.each do |key|
if auth_hash.has_attribute?(key) && gl_user.sync_attribute?(key)
gl_user[key] = auth_hash.public_send(key) # rubocop:disable GitlabSecurity/PublicSend
user_synced_attributes_metadata.set_attribute_synced(key, true)
else
user_synced_attributes_metadata.set_attribute_synced(key, false)
return unless sync_profile_from_provider? || creating_linked_ldap_user?
metadata = gl_user.user_synced_attributes_metadata || gl_user.build_user_synced_attributes_metadata
if sync_profile_from_provider?
UserSyncedAttributesMetadata::SYNCABLE_ATTRIBUTES.each do |key|
if auth_hash.has_attribute?(key) && gl_user.sync_attribute?(key)
gl_user[key] = auth_hash.public_send(key) # rubocop:disable GitlabSecurity/PublicSend
metadata.set_attribute_synced(key, true)
else
metadata.set_attribute_synced(key, false)
end
end
metadata.provider = auth_hash.provider
end
user_synced_attributes_metadata.provider = auth_hash.provider
gl_user.user_synced_attributes_metadata = user_synced_attributes_metadata
if creating_linked_ldap_user? && gl_user.email == ldap_person.email.first
metadata.set_attribute_synced(:email, true)
metadata.provider = ldap_person.provider
end
end
def log
......
......@@ -385,6 +385,11 @@ module Gitlab
success
end
<<<<<<< HEAD
=======
# Delete branch from remote repository
#
>>>>>>> upstream/master
# storage - project's storage path
# project_name - project's disk path
# remote_name - remote name
......
......@@ -33,11 +33,14 @@ module Gitlab
{
title: "Labels",
value: resource.labels.any? ? resource.label_names.join(', ') : "_None_",
<<<<<<< HEAD
short: true
},
{
title: "Weight",
value: resource.weight? ? resource.weight : "_None_",
=======
>>>>>>> upstream/master
short: true
}
]
......
......@@ -12,6 +12,27 @@ module QA
autoload :Browser, 'qa/runtime/browser'
end
##
# GitLab QA fabrication mechanisms
#
module Factory
autoload :Base, 'qa/factory/base'
module Resource
autoload :Sandbox, 'qa/factory/resource/sandbox'
autoload :Group, 'qa/factory/resource/group'
autoload :Project, 'qa/factory/resource/project'
end
module Repository
autoload :Push, 'qa/factory/repository/push'
end
module Settings
autoload :HashedStorage, 'qa/factory/settings/hashed_storage'
end
end
##
# GitLab QA Scenarios
#
......@@ -34,31 +55,6 @@ module QA
autoload :Mattermost, 'qa/scenario/test/integration/mattermost'
end
end
##
# GitLab instance scenarios.
#
module Gitlab
module Group
autoload :Create, 'qa/scenario/gitlab/group/create'
end
module Project
autoload :Create, 'qa/scenario/gitlab/project/create'
end
module Repository
autoload :Push, 'qa/scenario/gitlab/repository/push'
end
module Sandbox
autoload :Prepare, 'qa/scenario/gitlab/sandbox/prepare'
end
module Admin
autoload :HashedStorage, 'qa/scenario/gitlab/admin/hashed_storage'
end
end
end
##
......
module QA
module Factory
class Base
def self.fabricate!(*args)
new.tap do |factory|
yield factory if block_given?
return factory.fabricate!(*args)
end
end
def fabricate!(*_args)
raise NotImplementedError
end
end
end
end
require "pry-byebug"
module QA
module Factory
module Repository
class Push < Factory::Base
PAGE_REGEX_CHECK =
%r{\/#{Runtime::Namespace.sandbox_name}\/qa-test[^\/]+\/{1}[^\/]+\z}.freeze
attr_writer :file_name,
:file_content,
:commit_message,
:branch_name
def initialize
@file_name = 'file.txt'
@file_content = '# This is test project'
@commit_message = "Add #{@file_name}"
@branch_name = 'master'
end
def fabricate!
Git::Repository.perform do |repository|
repository.location = Page::Project::Show.act do
unless PAGE_REGEX_CHECK.match(current_path)
raise "To perform this scenario the current page should be project show."
end
choose_repository_clone_http
repository_location
end
repository.use_default_credentials
repository.clone
repository.configure_identity('GitLab QA', 'root@gitlab.com')
repository.add_file(@file_name, @file_content)
repository.commit(@commit_message)
repository.push_changes(@branch_name)
end
end
end
end
end
end
module QA
module Factory
module Resource
class Group < Factory::Base
attr_writer :path, :description
def initialize
@path = Runtime::Namespace.name
@description = "QA test run at #{Runtime::Namespace.time}"
end
def fabricate!
Page::Group::New.perform do |group|
group.set_path(@path)
group.set_description(@description)
group.set_visibility('Private')
group.create
end
end
end
end
end
end
require 'securerandom'
module QA
module Factory
module Resource
class Project < Factory::Base
attr_writer :description
def name=(name)
@name = "#{name}-#{SecureRandom.hex(8)}"
end
def fabricate!
Factory::Resource::Sandbox.fabricate!
Page::Group::Show.perform do |page|
if page.has_subgroup?(Runtime::Namespace.name)
page.go_to_subgroup(Runtime::Namespace.name)
else
page.go_to_new_subgroup
Factory::Resource::Group.fabricate! do |group|
group.path = Runtime::Namespace.name
end
end
page.go_to_new_project
end
Page::Project::New.perform do |page|
page.choose_test_namespace
page.choose_name(@name)
page.add_description(@description)
page.create_new_project
end
end
end
end
end
end
module QA
module Factory
module Resource
##
# Ensure we're in our sandbox namespace, either by navigating to it or by
# creating it if it doesn't yet exist.
#
class Sandbox < Factory::Base
def fabricate!
Page::Main::Menu.act { go_to_groups }
Page::Dashboard::Groups.perform do |page|
if page.has_group?(Runtime::Namespace.sandbox_name)
page.go_to_group(Runtime::Namespace.sandbox_name)
else
page.go_to_new_group
Resource::Group.fabricate! do |group|
group.path = Runtime::Namespace.sandbox_name
group.description = 'GitLab QA Sandbox'
end
end
end
end
end
end
end
end
module QA
module Factory
module Settings
class HashedStorage < Factory::Base
def fabricate!(*traits)
raise ArgumentError unless traits.include?(:enabled)
Page::Main::Login.act { sign_in_using_credentials }
Page::Main::Menu.act { go_to_admin_area }
Page::Admin::Menu.act { go_to_settings }
Page::Admin::Settings.act do
enable_hashed_storage
save_settings
end
QA::Page::Main::Menu.act { sign_out }
end
end
end
end
end
module QA
module Scenario
module Gitlab
module Admin
class HashedStorage < Scenario::Template
def perform(*traits)
raise ArgumentError unless traits.include?(:enabled)
Page::Main::Login.act { sign_in_using_credentials }
Page::Main::Menu.act { go_to_admin_area }
Page::Admin::Menu.act { go_to_settings }
Page::Admin::Settings.act do
enable_hashed_storage
save_settings
end
QA::Page::Main::Menu.act { sign_out }
end
end
end
end
end
end
require 'securerandom'
module QA
module Scenario
module Gitlab
module Group
class Create < Scenario::Template
attr_writer :path, :description
def initialize
@path = Runtime::Namespace.name
@description = "QA test run at #{Runtime::Namespace.time}"
end
def perform
Page::Group::New.perform do |group|
group.set_path(@path)
group.set_description(@description)
group.set_visibility('Private')
group.create
end
end
end
end
end
end
end
require 'securerandom'
module QA
module Scenario
module Gitlab
module Project
class Create < Scenario::Template
attr_writer :description
def name=(name)
@name = "#{name}-#{SecureRandom.hex(8)}"
end
def perform
Scenario::Gitlab::Sandbox::Prepare.perform
Page::Group::Show.perform do |page|
if page.has_subgroup?(Runtime::Namespace.name)
page.go_to_subgroup(Runtime::Namespace.name)
else
page.go_to_new_subgroup
Scenario::Gitlab::Group::Create.perform do |group|
group.path = Runtime::Namespace.name
end
end
page.go_to_new_project
end
Page::Project::New.perform do |page|
page.choose_test_namespace
page.choose_name(@name)
page.add_description(@description)
page.create_new_project
end
end
end
end
end
end
end
require "pry-byebug"
module QA
module Scenario
module Gitlab
module Repository
class Push < Scenario::Template
PAGE_REGEX_CHECK =
%r{\/#{Runtime::Namespace.sandbox_name}\/qa-test[^\/]+\/{1}[^\/]+\z}.freeze
attr_writer :file_name,
:file_content,
:commit_message,
:branch_name
def initialize
@file_name = 'file.txt'
@file_content = '# This is test project'
@commit_message = "Add #{@file_name}"
@branch_name = 'master'
end
def perform
Git::Repository.perform do |repository|
repository.location = Page::Project::Show.act do
unless PAGE_REGEX_CHECK.match(current_path)
raise "To perform this scenario the current page should be project show."
end
choose_repository_clone_http
repository_location
end
repository.use_default_credentials
repository.clone
repository.configure_identity('GitLab QA', 'root@gitlab.com')
repository.add_file(@file_name, @file_content)
repository.commit(@commit_message)
repository.push_changes(@branch_name)
end
end
end
end
end
end
end
module QA
module Scenario
module Gitlab
module Sandbox
# Ensure we're in our sandbox namespace, either by navigating to it or
# by creating it if it doesn't yet exist
class Prepare < Scenario::Template
def perform
Page::Main::Menu.act { go_to_groups }
Page::Dashboard::Groups.perform do |page|
if page.has_group?(Runtime::Namespace.sandbox_name)
page.go_to_group(Runtime::Namespace.sandbox_name)
else
page.go_to_new_group
Scenario::Gitlab::Group::Create.perform do |group|
group.path = Runtime::Namespace.sandbox_name
group.description = 'QA sandbox'
end
end
end
end
end
end
end
end
end
......@@ -4,7 +4,7 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Scenario::Gitlab::Project::Create.perform do |project|
Factory::Resource::Project.fabricate! do |project|
project.name = 'awesome-project'
project.description = 'create awesome project test'
end
......
......@@ -12,7 +12,7 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Scenario::Gitlab::Project::Create.perform do |scenario|
Factory::Resource::Project.fabricate! do |scenario|
scenario.name = 'project-with-code'
scenario.description = 'project for git clone tests'
end
......
......@@ -5,12 +5,12 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
Scenario::Gitlab::Project::Create.perform do |scenario|
Factory::Resource::Project.fabricate! do |scenario|
scenario.name = 'project_with_code'
scenario.description = 'project with repository'
end
Scenario::Gitlab::Repository::Push.perform do |scenario|
Factory::Repository::Push.fabricate! do |scenario|
scenario.file_name = 'README.md'
scenario.file_content = '# This is test project'
scenario.commit_message = 'Add README.md'
......
......@@ -21,6 +21,7 @@ module Omnibus
if id
puts "Triggered https://gitlab.com/#{Omnibus::PROJECT_PATH}/pipelines/#{id}"
puts "Waiting for downstream pipeline status"
else
raise "Trigger failed! The response from the trigger is: #{res.body}"
end
......@@ -39,7 +40,9 @@ module Omnibus
"ref" => ENV["OMNIBUS_BRANCH"] || "master",
"variables[GITLAB_VERSION]" => ENV["CI_COMMIT_SHA"],
"variables[ALTERNATIVE_SOURCES]" => true,
"variables[ee]" => ee? ? 'true' : 'false'
"variables[ee]" => ee? ? 'true' : 'false',
"variables[TRIGGERED_USER]" => ENV["GITLAB_USER_NAME"],
"variables[TRIGGER_SOURCE]" => "https://gitlab.com/gitlab-org/#{ENV['CI_PROJECT_NAME']}/-/jobs/#{ENV['CI_JOB_ID']}"
}
end
......@@ -63,14 +66,14 @@ module Omnibus
def wait!
loop do
raise 'Pipeline timeout!' if timeout?
raise "Pipeline timed out after waiting for #{duration} minutes!" if timeout?
case status
when :created, :pending, :running
puts "Waiting another #{INTERVAL} seconds ..."
print "."
sleep INTERVAL
when :success
puts "Omnibus pipeline succeeded!"
puts "Omnibus pipeline succeeded in #{duration} minutes!"
break
else
raise "Omnibus pipeline did not succeed!"
......@@ -84,6 +87,10 @@ module Omnibus
Time.now.to_i > (@start + MAX_DURATION)
end
def duration
(Time.now.to_i - @start) / 60
end
def status
req = Net::HTTP::Get.new(@uri)
req['PRIVATE-TOKEN'] = ENV['GITLAB_QA_ACCESS_TOKEN']
......
......@@ -18,8 +18,8 @@ describe 'Admin System Info' do
it 'shows system info page' do
expect(page).to have_content 'CPU 2 cores'
expect(page).to have_content 'Memory 4 GB / 16 GB'
expect(page).to have_content 'Disks'
expect(page).to have_content 'Memory Usage 4 GB / 16 GB'
expect(page).to have_content 'Disk Usage'
expect(page).to have_content 'Uptime'
end
end
......@@ -33,8 +33,8 @@ describe 'Admin System Info' do
it 'shows system info page with no CPU info' do
expect(page).to have_content 'CPU Unable to collect CPU info'
expect(page).to have_content 'Memory 4 GB / 16 GB'
expect(page).to have_content 'Disks'
expect(page).to have_content 'Memory Usage 4 GB / 16 GB'
expect(page).to have_content 'Disk Usage'
expect(page).to have_content 'Uptime'
end
end
......@@ -48,8 +48,8 @@ describe 'Admin System Info' do
it 'shows system info page with no CPU info' do
expect(page).to have_content 'CPU 2 cores'
expect(page).to have_content 'Memory Unable to collect memory info'
expect(page).to have_content 'Disks'
expect(page).to have_content 'Memory Usage Unable to collect memory info'
expect(page).to have_content 'Disk Usage'
expect(page).to have_content 'Uptime'
end
end
......
......@@ -24,7 +24,7 @@ feature 'Group variables', :js do
expect(find(".variable-value")).to have_content('******')
expect(find(".variable-protected")).to have_content('Yes')
end
click_on 'Reveal Values'
click_on 'Reveal value'
page.within('.variables-table') do
expect(find(".variable-value")).to have_content('AAA123')
end
......
require 'spec_helper'
feature 'Projects tree' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
before do
project.add_master(user)
sign_in(user)
visit project_tree_path(project, 'master')
end
it 'renders tree table' do
expect(page).to have_selector('.tree-item')
expect(page).not_to have_selector('.label-lfs', text: 'LFS')
end
context 'LFS' do
before do
visit project_tree_path(project, File.join('master', 'files/lfs'))
end
it 'renders LFS badge on blob item' do
expect(page).to have_selector('.label-lfs', text: 'LFS')
end
end
end
......@@ -65,14 +65,14 @@ describe 'Project variables', :js do
expect(page).to have_content('******')
end
click_button('Reveal Values')
click_button('Reveal values')
page.within('.variables-table') do
expect(page).to have_content('key')
expect(page).to have_content('key value')
end
click_button('Hide Values')
click_button('Hide values')
page.within('.variables-table') do
expect(page).to have_content('key')
......
......@@ -9,6 +9,7 @@ describe TreeHelper do
before do
@id = sha
@project = project
@lfs_blob_ids = []
end
it 'displays all entries without a warning' do
......
/* eslint-disable no-unused-expressions, no-prototype-builtins, no-new, no-shadow, max-len */
import 'vendor/jquery.endless-scroll';
import '~/pager';
import Activities from '~/activities';
(() => {
......
import SecretValues from '~/behaviors/secret_values';
function generateFixtureMarkup(secrets, isRevealed) {
return `
<div class="js-secret-container">
${secrets.map(secret => `
<div class="js-secret-value-placeholder">
***
</div>
<div class="hide js-secret-value">
${secret}
</div>
`).join('')}
<button
class="js-secret-value-reveal-button"
data-secret-reveal-status="${isRevealed}"
>
...
</button>
</div>
`;
}
function setupSecretFixture(secrets, isRevealed) {
const wrapper = document.createElement('div');
wrapper.innerHTML = generateFixtureMarkup(secrets, isRevealed);
const secretValues = new SecretValues(wrapper.querySelector('.js-secret-container'));
secretValues.init();
return wrapper;
}
describe('setupSecretValues', () => {
describe('with a single secret', () => {
const secrets = ['mysecret123'];
it('should have correct "Reveal" label when values are hidden', () => {
const wrapper = setupSecretFixture(secrets, false);
const revealButton = wrapper.querySelector('.js-secret-value-reveal-button');
expect(revealButton.textContent).toEqual('Reveal value');
});
it('should have correct "Hide" label when values are shown', () => {
const wrapper = setupSecretFixture(secrets, true);
const revealButton = wrapper.querySelector('.js-secret-value-reveal-button');
expect(revealButton.textContent).toEqual('Hide value');
});
it('should value hidden initially', () => {
const wrapper = setupSecretFixture(secrets, false);
const values = wrapper.querySelectorAll('.js-secret-value');
const placeholders = wrapper.querySelectorAll('.js-secret-value-placeholder');
expect(values.length).toEqual(1);
expect(values[0].classList.contains('hide')).toEqual(true);
expect(placeholders.length).toEqual(1);
expect(placeholders[0].classList.contains('hide')).toEqual(false);
});
it('should toggle value and placeholder', () => {
const wrapper = setupSecretFixture(secrets, false);
const revealButton = wrapper.querySelector('.js-secret-value-reveal-button');
const values = wrapper.querySelectorAll('.js-secret-value');
const placeholders = wrapper.querySelectorAll('.js-secret-value-placeholder');
revealButton.click();
expect(values.length).toEqual(1);
expect(values[0].classList.contains('hide')).toEqual(false);
expect(placeholders.length).toEqual(1);
expect(placeholders[0].classList.contains('hide')).toEqual(true);
revealButton.click();
expect(values.length).toEqual(1);
expect(values[0].classList.contains('hide')).toEqual(true);
expect(placeholders.length).toEqual(1);
expect(placeholders[0].classList.contains('hide')).toEqual(false);
});
});
describe('with a multiple secrets', () => {
const secrets = ['mysecret123', 'happygoat456', 'tanuki789'];
it('should have correct "Reveal" label when values are hidden', () => {
const wrapper = setupSecretFixture(secrets, false);
const revealButton = wrapper.querySelector('.js-secret-value-reveal-button');
expect(revealButton.textContent).toEqual('Reveal values');
});
it('should have correct "Hide" label when values are shown', () => {
const wrapper = setupSecretFixture(secrets, true);
const revealButton = wrapper.querySelector('.js-secret-value-reveal-button');
expect(revealButton.textContent).toEqual('Hide values');
});
it('should have all values hidden initially', () => {
const wrapper = setupSecretFixture(secrets, false);
const values = wrapper.querySelectorAll('.js-secret-value');
const placeholders = wrapper.querySelectorAll('.js-secret-value-placeholder');
expect(values.length).toEqual(3);
values.forEach((value) => {
expect(value.classList.contains('hide')).toEqual(true);
});
expect(placeholders.length).toEqual(3);
placeholders.forEach((placeholder) => {
expect(placeholder.classList.contains('hide')).toEqual(false);
});
});
it('should toggle values and placeholders', () => {
const wrapper = setupSecretFixture(secrets, false);
const revealButton = wrapper.querySelector('.js-secret-value-reveal-button');
const values = wrapper.querySelectorAll('.js-secret-value');
const placeholders = wrapper.querySelectorAll('.js-secret-value-placeholder');
revealButton.click();
expect(values.length).toEqual(3);
values.forEach((value) => {
expect(value.classList.contains('hide')).toEqual(false);
});
expect(placeholders.length).toEqual(3);
placeholders.forEach((placeholder) => {
expect(placeholder.classList.contains('hide')).toEqual(true);
});
revealButton.click();
expect(values.length).toEqual(3);
values.forEach((value) => {
expect(value.classList.contains('hide')).toEqual(true);
});
expect(placeholders.length).toEqual(3);
placeholders.forEach((placeholder) => {
expect(placeholder.classList.contains('hide')).toEqual(false);
});
});
});
});
import 'vendor/jquery.endless-scroll';
import '~/pager';
import CommitsList from '~/commits';
describe('Commits List', () => {
......
......@@ -2,7 +2,7 @@
/* global Notes */
import * as urlUtils from '~/lib/utils/url_utility';
import '~/merge_request_tabs';
import MergeRequestTabs from '~/merge_request_tabs';
import '~/commit/pipelines/pipelines_bundle';
import '~/breakpoints';
import '~/lib/utils/common_utils';
......@@ -32,7 +32,7 @@ import 'vendor/jquery.scrollTo';
);
beforeEach(function () {
this.class = new gl.MergeRequestTabs({ stubLocation: stubLocation });
this.class = new MergeRequestTabs({ stubLocation: stubLocation });
setLocation();
this.spies = {
......
/* global fixture */
import * as utils from '~/lib/utils/url_utility';
import '~/pager';
import Pager from '~/pager';
describe('pager', () => {
const Pager = window.Pager;
it('is defined on window', () => {
expect(window.Pager).toBeDefined();
});
describe('init', () => {
const originalHref = window.location.href;
......
......@@ -30,9 +30,9 @@ describe('Toggle Button', () => {
expect(vm.$el.querySelector('input').getAttribute('value')).toEqual('true');
});
it('renders Enabled and Disabled text data attributes', () => {
expect(vm.$el.querySelector('button').getAttribute('data-enabled-text')).toEqual('Enabled');
expect(vm.$el.querySelector('button').getAttribute('data-disabled-text')).toEqual('Disabled');
it('renders input status icon', () => {
expect(vm.$el.querySelectorAll('span.toggle-icon').length).toEqual(1);
expect(vm.$el.querySelectorAll('svg.s16.toggle-icon-svg').length).toEqual(1);
});
});
......@@ -49,6 +49,14 @@ describe('Toggle Button', () => {
expect(vm.$el.querySelector('button').classList.contains('is-checked')).toEqual(true);
});
it('sets aria-label representing toggle state', () => {
vm.value = true;
expect(vm.ariaLabel).toEqual('Toggle Status: ON');
vm.value = false;
expect(vm.ariaLabel).toEqual('Toggle Status: OFF');
});
it('emits change event when clicked', () => {
vm.$el.querySelector('button').click();
......
......@@ -41,7 +41,6 @@ describe Gitlab::LDAP::User do
it "does not mark existing ldap user as changed" do
create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=john smith,ou=people,dc=example,dc=com', provider: 'ldapmain')
ldap_user.gl_user.user_synced_attributes_metadata(provider: 'ldapmain', email: true)
expect(ldap_user.changed?).to be_falsey
end
end
......@@ -147,11 +146,15 @@ describe Gitlab::LDAP::User do
expect(ldap_user.gl_user.email).to eq(info[:email])
end
it "has user_synced_attributes_metadata email set to true" do
it "has email set as synced" do
expect(ldap_user.gl_user.user_synced_attributes_metadata.email_synced).to be_truthy
end
it "has synced_attribute_provider set to ldapmain" do
it "has email set as read-only" do
expect(ldap_user.gl_user.read_only_attribute?(:email)).to be_truthy
end
it "has synced attributes provider set to ldapmain" do
expect(ldap_user.gl_user.user_synced_attributes_metadata.provider).to eql 'ldapmain'
end
end
......@@ -165,9 +168,13 @@ describe Gitlab::LDAP::User do
expect(ldap_user.gl_user.temp_oauth_email?).to be_truthy
end
it "has synced attribute email set to false" do
it "has email set as not synced" do
expect(ldap_user.gl_user.user_synced_attributes_metadata.email_synced).to be_falsey
end
it "does not have email set as read-only" do
expect(ldap_user.gl_user.read_only_attribute?(:email)).to be_falsey
end
end
end
......
......@@ -202,11 +202,13 @@ describe Gitlab::OAuth::User do
end
context "and no account for the LDAP user" do
it "creates a user with dual LDAP and omniauth identities" do
before do
allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
oauth_user.save
end
it "creates a user with dual LDAP and omniauth identities" do
expect(gl_user).to be_valid
expect(gl_user.username).to eql uid
expect(gl_user.email).to eql 'johndoe@example.com'
......@@ -219,6 +221,18 @@ describe Gitlab::OAuth::User do
]
)
end
it "has email set as synced" do
expect(gl_user.user_synced_attributes_metadata.email_synced).to be_truthy
end
it "has email set as read-only" do
expect(gl_user.read_only_attribute?(:email)).to be_truthy
end
it "has synced attributes provider set to ldapmain" do
expect(gl_user.user_synced_attributes_metadata.provider).to eql 'ldapmain'
end
end
context "and LDAP user has an account already" do
......@@ -440,11 +454,15 @@ describe Gitlab::OAuth::User do
expect(gl_user.email).to eq(info_hash[:email])
end
it "has external_attributes set to true" do
expect(gl_user.user_synced_attributes_metadata).not_to be_nil
it "has email set as synced" do
expect(gl_user.user_synced_attributes_metadata.email_synced).to be_truthy
end
it "has email set as read-only" do
expect(gl_user.read_only_attribute?(:email)).to be_truthy
end
it "has attributes_provider set to my-provider" do
it "has synced attributes provider set to my-provider" do
expect(gl_user.user_synced_attributes_metadata.provider).to eql 'my-provider'
end
end
......@@ -458,10 +476,13 @@ describe Gitlab::OAuth::User do
expect(gl_user.email).not_to eq(info_hash[:email])
end
it "has user_synced_attributes_metadata set to nil" do
expect(gl_user.user_synced_attributes_metadata.provider).to eql 'my-provider'
it "has email set as not synced" do
expect(gl_user.user_synced_attributes_metadata.email_synced).to be_falsey
end
it "does not have email set as read-only" do
expect(gl_user.read_only_attribute?(:email)).to be_falsey
end
end
end
......@@ -508,11 +529,15 @@ describe Gitlab::OAuth::User do
expect(gl_user.email).to eq(info_hash[:email])
end
it "has email_synced_attribute set to true" do
it "has email set as synced" do
expect(gl_user.user_synced_attributes_metadata.email_synced).to be(true)
end
it "has my-provider as attributes_provider" do
it "has email set as read-only" do
expect(gl_user.read_only_attribute?(:email)).to be_truthy
end
it "has synced attributes provider set to my-provider" do
expect(gl_user.user_synced_attributes_metadata.provider).to eql 'my-provider'
end
end
......@@ -524,7 +549,14 @@ describe Gitlab::OAuth::User do
it "does not update the user email" do
expect(gl_user.email).not_to eq(info_hash[:email])
expect(gl_user.user_synced_attributes_metadata.email_synced).to be(false)
end
it "has email set as not synced" do
expect(gl_user.user_synced_attributes_metadata.email_synced).to be_falsey
end
it "does not have email set as read-only" do
expect(gl_user.read_only_attribute?(:email)).to be_falsey
end
end
end
......
......@@ -51,6 +51,7 @@ describe Gitlab::Shell do
end
end
<<<<<<< HEAD
describe '#add_key' do
context 'when authorized_keys_enabled is true' do
it 'removes trailing garbage' do
......@@ -356,6 +357,8 @@ describe Gitlab::Shell do
end
end
=======
>>>>>>> upstream/master
describe Gitlab::Shell::KeyAdder do
describe '#add_key' do
it 'removes trailing garbage' do
......@@ -646,6 +649,7 @@ describe Gitlab::Shell do
it 'raises an exception when the command fails' do
allow(gitlab_projects).to receive(:output) { 'error' }
expect(gitlab_projects).to receive(:import_project) { false }
<<<<<<< HEAD
expect do
gitlab_shell.import_repository(project.repository_storage_path, project.disk_path, import_url)
......@@ -705,6 +709,67 @@ describe Gitlab::Shell do
.with('downstream-remote', ['master'])
.and_return(false)
=======
expect do
gitlab_shell.import_repository(project.repository_storage_path, project.disk_path, import_url)
end.to raise_error(Gitlab::Shell::Error, "error")
end
end
describe '#push_remote_branches' do
subject(:result) do
gitlab_shell.push_remote_branches(
project.repository_storage_path,
project.disk_path,
'downstream-remote',
['master']
)
end
it 'executes the command' do
expect(gitlab_projects).to receive(:push_branches)
.with('downstream-remote', timeout, true, ['master'])
.and_return(true)
is_expected.to be_truthy
end
it 'fails to execute the command' do
allow(gitlab_projects).to receive(:output) { 'error' }
expect(gitlab_projects).to receive(:push_branches)
.with('downstream-remote', timeout, true, ['master'])
.and_return(false)
expect { result }.to raise_error(Gitlab::Shell::Error, 'error')
end
end
describe '#delete_remote_branches' do
subject(:result) do
gitlab_shell.delete_remote_branches(
project.repository_storage_path,
project.disk_path,
'downstream-remote',
['master']
)
end
it 'executes the command' do
expect(gitlab_projects).to receive(:delete_remote_branches)
.with('downstream-remote', ['master'])
.and_return(true)
is_expected.to be_truthy
end
it 'fails to execute the command' do
allow(gitlab_projects).to receive(:output) { 'error' }
expect(gitlab_projects).to receive(:delete_remote_branches)
.with('downstream-remote', ['master'])
.and_return(false)
>>>>>>> upstream/master
expect { result }.to raise_error(Gitlab::Shell::Error, 'error')
end
end
......
......@@ -1013,7 +1013,7 @@ describe Repository do
it 'runs without errors' do
# old_rev is an ancestor of new_rev
expect(repository.rugged.merge_base(old_rev, new_rev)).to eq(old_rev)
expect(repository.merge_base(old_rev, new_rev)).to eq(old_rev)
# old_rev is not a direct ancestor (parent) of new_rev
expect(repository.rugged.lookup(new_rev).parent_ids).not_to include(old_rev)
......@@ -1035,7 +1035,7 @@ describe Repository do
it 'raises an exception' do
# The 'master' branch is NOT an ancestor of new_rev.
expect(repository.rugged.merge_base(old_rev, new_rev)).not_to eq(old_rev)
expect(repository.merge_base(old_rev, new_rev)).not_to eq(old_rev)
# Updating 'master' to new_rev would lose the commits on 'master' that
# are not contained in new_rev. This should not be allowed.
......
......@@ -213,7 +213,7 @@ describe MergeRequests::Conflicts::ResolveService do
MergeRequests::Conflicts::ListService.new(merge_request).conflicts.resolver
end
let(:regex_conflict) do
resolver.conflict_for_path('files/ruby/regex.rb', 'files/ruby/regex.rb')
resolver.conflict_for_path(resolver.conflicts, 'files/ruby/regex.rb', 'files/ruby/regex.rb')
end
let(:invalid_params) do
......
......@@ -10,6 +10,7 @@ module StubConfiguration
# Ensure that we don't use the Markdown cache when stubbing these values
allow_any_instance_of(ApplicationSetting).to receive(:cached_html_up_to_date?).and_return(false)
<<<<<<< HEAD
end
def stub_application_setting_on_object(object, messages)
......@@ -20,6 +21,8 @@ module StubConfiguration
messages.each do |setting, value|
allow(object).to receive_message_chain(:current_application_settings, setting) { value }
end
=======
>>>>>>> upstream/master
end
def stub_not_protect_default_branch
......
require 'spec_helper'
describe 'projects/tree/_blob_item' do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:blob_item) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID, 'files/ruby').first }
before do
assign(:project, project)
assign(:repository, repository)
assign(:id, File.join('master', ''))
assign(:lfs_blob_ids, [])
end
it 'renders blob item' do
render_partial(blob_item)
expect(rendered).to have_content(blob_item.name)
expect(rendered).not_to have_selector('.label-lfs', text: 'LFS')
end
describe 'LFS blob' do
before do
assign(:lfs_blob_ids, [blob_item].map(&:id))
render_partial(blob_item)
end
it 'renders LFS badge' do
expect(rendered).to have_selector('.label-lfs', text: 'LFS')
end
end
def render_partial(blob_item)
render partial: 'projects/tree/blob_item', locals: {
blob_item: blob_item,
type: 'blob'
}
end
end
......@@ -9,6 +9,7 @@ describe 'projects/tree/show' do
before do
assign(:project, project)
assign(:repository, repository)
assign(:lfs_blob_ids, [])
allow(view).to receive(:can?).and_return(true)
allow(view).to receive(:can_collaborate_with_project?).and_return(true)
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment