Commit 3f293df3 authored by Robert Speicher's avatar Robert Speicher

Merge branch 'ce-to-ee-2018-01-17' into 'master'

CE upstream - Wednesday

Closes #4378

See merge request gitlab-org/gitlab-ee!4126
parents eb7cd4b6 8bb7968f
......@@ -992,6 +992,11 @@ entry.
- Added type to CHANGELOG entries. (Jacopo Beschi @jacopo-beschi)
- [BUGIFX] Improves subgroup creation permissions. !13418
## 9.5.10 (2017-11-08)
- [SECURITY] Add SSRF protections for hostnames that will never resolve but will still connect to localhost
- [SECURITY] Include X-Content-Type-Options (XCTO) header into API responses
## 9.5.9 (2017-10-16)
- [SECURITY] Move project repositories between namespaces when renaming users.
......
......@@ -118,7 +118,7 @@ gem 'google-api-client', '~> 0.13.6'
gem 'unf', '~> 0.1.4'
# Seed data
gem 'seed-fu', '2.3.6' # Upgrade to > 2.3.7 once https://github.com/mbleigh/seed-fu/issues/123 is solved
gem 'seed-fu', '~> 2.3.7'
# Search
gem 'elasticsearch-model', '~> 0.1.9'
......
......@@ -861,7 +861,7 @@ GEM
rake (>= 0.9, < 13)
sass (~> 3.5.3)
securecompare (1.0.0)
seed-fu (2.3.6)
seed-fu (2.3.7)
activerecord (>= 3.1)
activesupport (>= 3.1)
select2-rails (3.5.9.3)
......@@ -1210,7 +1210,7 @@ DEPENDENCIES
sanitize (~> 2.0)
sass-rails (~> 5.0.6)
scss_lint (~> 0.56.0)
seed-fu (= 2.3.6)
seed-fu (~> 2.3.7)
select2-rails (~> 3.5.9)
selenium-webdriver (~> 3.5)
sentry-raven (~> 2.5.3)
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */
import { s__ } from './locale';
import projectSelect from './project_select';
import Milestone from './milestone';
import IssuableForm from './issuable_form';
......@@ -14,7 +13,6 @@ import Project from './project';
import projectAvatar from './project_avatar';
import MergeRequest from './merge_request';
import Compare from './compare';
import initCompareAutocomplete from './compare_autocomplete';
import ProjectNew from './project_new';
import Labels from './labels';
import LabelManager from './label_manager';
......@@ -22,12 +20,10 @@ import Sidebar from './right_sidebar';
import IssuableTemplateSelectors from './templates/issuable_template_selectors';
import Flash from './flash';
import CommitsList from './commits';
import BindInOut from './behaviors/bind_in_out';
import SecretValues from './behaviors/secret_values';
import Group from './group';
import ProjectsList from './projects_list';
import MiniPipelineGraph from './mini_pipeline_graph_dropdown';
import UserCallout from './user_callout';
import ShortcutsWiki from './shortcuts_wiki';
import BlobViewer from './blob/viewer/index';
......@@ -42,8 +38,6 @@ import PerformanceBar from './performance_bar';
import initNotes from './init_notes';
import initIssuableSidebar from './init_issuable_sidebar';
import initProjectVisibilitySelector from './project_visibility';
import GpgBadges from './gpg_badges';
import initChangesDropdown from './init_changes_dropdown';
import NewGroupChild from './groups/new_group_child';
import { ajaxGet, convertPermissionToBoolean } from './lib/utils/common_utils';
import GlFieldErrors from './gl_field_errors';
......@@ -58,7 +52,6 @@ import Diff from './diff';
import ProjectLabelSubscription from './project_label_subscription';
import SearchAutocomplete from './search_autocomplete';
import Activities from './activities';
import { fetchCommitMergeRequests } from './commit_merge_requests';
// EE-only
import ApproversSelect from 'ee/approvers_select'; // eslint-disable-line import/first
......@@ -233,9 +226,9 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
new ZenMode();
break;
case 'projects:compare:show':
new Diff();
const paddingTop = 16;
initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - paddingTop);
import('./pages/projects/compare/show')
.then(callDefault)
.catch(fail);
break;
case 'projects:branches:new':
import('./pages/projects/branches/new')
......@@ -289,7 +282,9 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
new MilestoneSelect();
new IssuableTemplateSelectors();
// ee-start
initApprovals();
// ee-end
break;
case 'projects:tags:new':
import('./pages/projects/tags/new')
......@@ -351,23 +346,15 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
.catch(fail);
break;
case 'projects:commit:show':
new Diff();
new ZenMode();
shortcut_handler = new ShortcutsNavigation();
new MiniPipelineGraph({
container: '.js-commit-pipeline-graph',
}).bindEvents();
initNotes();
const stickyBarPaddingTop = 16;
initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - stickyBarPaddingTop);
$('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
fetchCommitMergeRequests();
import('./pages/projects/commit/show')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:commit:pipelines':
new MiniPipelineGraph({
container: '.js-commit-pipeline-graph',
}).bindEvents();
$('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
import('./pages/projects/commit/pipelines')
.then(callDefault)
.catch(fail);
break;
case 'projects:activity':
import('./pages/projects/activity')
......@@ -376,9 +363,10 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
shortcut_handler = true;
break;
case 'projects:commits:show':
CommitsList.init(document.querySelector('.js-project-commits-show').dataset.commitsLimit);
shortcut_handler = new ShortcutsNavigation();
GpgBadges.fetch();
import('./pages/projects/commits/show')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:show':
shortcut_handler = new ShortcutsNavigation();
......@@ -624,20 +612,14 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
case 'projects:clusters:show':
case 'projects:clusters:update':
case 'projects:clusters:destroy':
import(/* webpackChunkName: "clusters" */ './clusters/clusters_bundle')
.then(cluster => new cluster.default()) // eslint-disable-line new-cap
.catch((err) => {
Flash(s__('ClusterIntegration|Problem setting up the cluster'));
throw err;
});
import('./pages/projects/clusters/show')
.then(callDefault)
.catch(fail);
break;
case 'projects:clusters:index':
import(/* webpackChunkName: "clusters_index" */ './clusters/clusters_index')
.then(clusterIndex => clusterIndex.default())
.catch((err) => {
Flash(s__('ClusterIntegration|Problem setting up the clusters list'));
throw err;
});
import('./pages/projects/clusters/index')
.then(callDefault)
.catch(fail);
break;
case 'admin:licenses:new':
import(/* webpackChunkName: "admin_licenses" */ 'ee/pages/admin/licenses/new').then(m => m.default()).catch(fail);
......@@ -735,7 +717,9 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
projectAvatar();
switch (path[1]) {
case 'compare':
initCompareAutocomplete();
import('./pages/projects/compare')
.then(callDefault)
.catch(fail);
break;
case 'edit':
shortcut_handler = new ShortcutsNavigation();
......
......@@ -3,7 +3,6 @@ import { visitUrl } from './lib/utils/url_utility';
import bp from './breakpoints';
import { numberToHumanSize } from './lib/utils/number_utils';
import { setCiStatusFavicon } from './lib/utils/common_utils';
import { timeFor } from './lib/utils/datetime_utility';
export default class Job {
constructor(options) {
......@@ -71,7 +70,6 @@ export default class Job {
.off('resize.build')
.on('resize.build', _.throttle(this.sidebarOnResize.bind(this), 100));
this.updateArtifactRemoveDate();
this.initAffixTopArea();
this.getBuildTrace();
......@@ -261,16 +259,7 @@ export default class Job {
sidebarOnClick() {
if (this.shouldHideSidebarForViewport()) this.toggleSidebar();
}
// eslint-disable-next-line class-methods-use-this, consistent-return
updateArtifactRemoveDate() {
const $date = $('.js-artifacts-remove');
if ($date.length) {
const date = $date.text();
return $date.text(
timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3'))),
);
}
}
// eslint-disable-next-line class-methods-use-this
populateJobs(stage) {
$('.build-job').hide();
......
......@@ -76,6 +76,7 @@
<loading-icon
v-if="isLoading"
size="2"
class="prepend-top-default append-bottom-default"
/>
</div>
</template>
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, no-underscore-dangle, one-var, one-var-declaration-per-line, consistent-return, dot-notation, quote-props, comma-dangle, object-shorthand, max-len, prefer-arrow-callback */
import 'vendor/jquery.waitforimages';
import { __ } from '~/locale';
import TaskList from './task_list';
import MergeRequestTabs from './merge_request_tabs';
import IssuablesHelper from './helpers/issuables_helper';
......@@ -110,12 +111,12 @@ MergeRequest.prototype.initCommitMessageListeners = function() {
});
};
MergeRequest.updateStatusText = function(classToRemove, classToAdd, newStatusText) {
MergeRequest.setStatusBoxToMerged = function() {
$('.detail-page-header .status-box')
.removeClass(classToRemove)
.addClass(classToAdd)
.removeClass('status-box-open')
.addClass('status-box-mr-merged')
.find('span')
.text(newStatusText);
.text(__('Merged'));
};
MergeRequest.decreaseCounter = function(by = 1) {
......
import ClustersIndex from '~/clusters/clusters_index';
export default () => {
new ClustersIndex(); // eslint-disable-line no-new
};
import ClustersBundle from '~/clusters/clusters_bundle';
export default () => {
new ClustersBundle(); // eslint-disable-line no-new
};
import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown';
export default () => {
new MiniPipelineGraph({
container: '.js-commit-pipeline-graph',
}).bindEvents();
$('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
};
/* eslint-disable no-new */
import Diff from '~/diff';
import ZenMode from '~/zen_mode';
import ShortcutsNavigation from '~/shortcuts_navigation';
import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown';
import initNotes from '~/init_notes';
import initChangesDropdown from '~/init_changes_dropdown';
import { fetchCommitMergeRequests } from '~/commit_merge_requests';
export default () => {
new Diff();
new ZenMode();
new ShortcutsNavigation();
new MiniPipelineGraph({
container: '.js-commit-pipeline-graph',
}).bindEvents();
initNotes();
const stickyBarPaddingTop = 16;
initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - stickyBarPaddingTop);
$('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
fetchCommitMergeRequests();
};
import CommitsList from '~/commits';
import GpgBadges from '~/gpg_badges';
import ShortcutsNavigation from '~/shortcuts_navigation';
export default () => {
CommitsList.init(document.querySelector('.js-project-commits-show').dataset.commitsLimit);
new ShortcutsNavigation(); // eslint-disable-line no-new
GpgBadges.fetch();
};
import initCompareAutocomplete from '~/compare_autocomplete';
export default () => {
initCompareAutocomplete();
};
import Diff from '~/diff';
import initChangesDropdown from '~/init_changes_dropdown';
export default () => {
new Diff(); // eslint-disable-line no-new
const paddingTop = 16;
initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - paddingTop);
};
......@@ -3,6 +3,7 @@
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import icon from '../../vue_shared/components/icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
export default {
......@@ -12,6 +13,7 @@
components: {
loadingIcon,
icon,
},
props: {
endpoint: {
......@@ -42,9 +44,6 @@
};
},
computed: {
iconClass() {
return `fa fa-${this.icon}`;
},
buttonClass() {
return `btn ${this.cssClass}`;
},
......@@ -77,10 +76,9 @@
data-container="body"
data-placement="top"
:disabled="isLoading">
<i
:class="iconClass"
aria-hidden="true">
</i>
<icon
:name="icon"
/>
<loading-icon v-if="isLoading" />
</button>
</template>
......@@ -93,6 +93,7 @@
<loading-icon
v-if="isLoading"
size="2"
class="prepend-top-default append-bottom-default"
/>
</div>
</template>
<script>
import playIconSvg from 'icons/_icon_play.svg';
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import icon from '../../vue_shared/components/icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
export default {
......@@ -10,6 +10,7 @@
},
components: {
loadingIcon,
icon,
},
props: {
actions: {
......@@ -19,7 +20,6 @@
},
data() {
return {
playIconSvg,
isLoading: false,
};
},
......@@ -52,7 +52,10 @@
aria-label="Manual job"
:disabled="isLoading"
>
<span v-html="playIconSvg"></span>
<icon
name="play"
class="icon-play"
/>
<i
class="fa fa-caret-down"
aria-hidden="true">
......
......@@ -313,7 +313,7 @@
:endpoint="pipeline.cancel_path"
css-class="js-pipelines-cancel-button btn-remove"
title="Cancel"
icon="remove"
icon="close"
confirm-action-message="Are you sure you want to cancel this pipeline?"
/>
</div>
......
......@@ -62,7 +62,7 @@ export default class Shortcuts {
e.preventDefault();
const performanceBarCookieName = 'perf_bar_enabled';
if (Cookies.get(performanceBarCookieName) === 'true') {
Cookies.remove(performanceBarCookieName, { path: '/' });
Cookies.set(performanceBarCookieName, 'false', { path: '/' });
} else {
Cookies.set(performanceBarCookieName, 'true', { path: '/' });
}
......
......@@ -170,7 +170,7 @@ export default {
// If state is merged we should update the widget and stop the polling
eventHub.$emit('MRWidgetUpdateRequested');
eventHub.$emit('FetchActionsContent');
MergeRequest.updateStatusText('status-box-open', 'status-box-merged', 'Merged');
MergeRequest.setStatusBoxToMerged();
MergeRequest.hideCloseButton();
MergeRequest.decreaseCounter();
stopPolling();
......
......@@ -122,7 +122,7 @@
>
<button
type="button"
class="btn pull-left"
class="btn"
:class="btnCancelKindClass"
@click="emitCancel($event)"
data-dismiss="modal"
......@@ -132,7 +132,7 @@
<button
v-if="primaryButtonLabel"
type="button"
class="btn pull-right js-primary-button"
class="btn js-primary-button"
:disabled="submitDisabled"
:class="btnKindClass"
@click="emitSubmit($event)"
......
......@@ -30,7 +30,7 @@
@include set-visible;
min-height: $dropdown-min-height;
max-height: $dropdown-max-height;
overflow: auto;
overflow-y: auto;
@media (max-width: $screen-xs-max) {
width: 100%;
......
......@@ -303,6 +303,8 @@
.projects-dropdown-menu {
padding: 0;
overflow-y: initial;
max-height: initial;
}
.dropdown-chevron {
......
......@@ -24,15 +24,13 @@
font-size: $gl-font-size;
line-height: 25px;
&.status-box-closed,
&.status-box-mr-closed {
background-color: $gl-danger;
}
&.status-box-issue-closed {
background-color: $gl-primary;
}
&.status-box-merged {
&.status-box-issue-closed,
&.status-box-mr-merged {
background-color: $gl-primary;
}
......
.modal-header {
background-color: $modal-body-bg;
padding: #{3 * $grid-size} #{2 * $grid-size};
.page-title {
......@@ -8,6 +9,7 @@
.modal-body {
background-color: $modal-body-bg;
min-height: $modal-body-height;
position: relative;
padding: #{3 * $grid-size} #{2 * $grid-size};
......@@ -20,6 +22,30 @@
}
}
.modal-footer {
display: flex;
flex-direction: row;
.btn + .btn {
margin-left: $grid-size;
}
@media (max-width: $screen-xs-max) {
flex-direction: column;
.btn + .btn {
margin-left: 0;
margin-top: $grid-size;
}
}
@media (min-width: $screen-sm-min) {
.btn:first-of-type {
margin-left: auto;
}
}
}
body.modal-open {
overflow: hidden;
}
......@@ -32,12 +58,6 @@ body.modal-open {
}
}
@media (min-width: $screen-md-min) {
.modal-dialog {
width: 860px;
}
}
@media (min-width: $screen-lg-min) {
.modal-full {
width: 98%;
......
......@@ -194,6 +194,6 @@ $modal-body-bg: $white-light;
//** Modal footer border color
// $modal-footer-border-color: $modal-header-border-color
// $modal-lg: 900px
// $modal-md: 600px
$modal-lg: 860px;
$modal-md: 540px;
// $modal-sm: 300px
......@@ -766,3 +766,8 @@ $popup-box-shadow-color: rgba(90, 90, 90, 0.05);
Multi file editor
*/
$border-color-settings: #e1e1e1;
/*
Modals
*/
$modal-body-height: 134px;
......@@ -96,13 +96,6 @@
border-color: $border-white-normal;
}
}
.btn {
.icon-play {
height: 13px;
width: 12px;
}
}
}
.btn .text-center {
......
......@@ -3,22 +3,21 @@
// see also: https://gist.github.com/jasonm23/2868981
$black: #000;
$red: #cd0000;
$green: #00cd00;
$yellow: #cdcd00;
$blue: #00e; // according to wikipedia, this is the xterm standard
//$blue: #1e90ff; // this is used by all the terminals I tried (when configured with the xterm color profile)
$magenta: #cd00cd;
$cyan: #00cdcd;
$white: #e5e5e5;
$red: #ea1010;
$green: #009900;
$yellow: #999900;
$blue: #0073e6;
$magenta: #d411d4;
$cyan: #009999;
$white: #ccc;
$l-black: #373b41;
$l-red: #c66;
$l-green: #b5bd68;
$l-yellow: #f0c674;
$l-blue: #81a2be;
$l-magenta: #b294bb;
$l-cyan: #8abeb7;
$l-white: $gray-darkest;
$l-red: #ff6161;
$l-green: #00d600;
$l-yellow: #bdbd00;
$l-blue: #5797ff;
$l-magenta: #d96dd9;
$l-cyan: #00bdbd;
$l-white: #fff;
/*
* xterm colors
......
......@@ -6,13 +6,22 @@ module WithPerformanceBar
end
def peek_enabled?
return true if Rails.env.development?
return false unless Gitlab::PerformanceBar.enabled?(current_user)
if RequestStore.active?
RequestStore.fetch(:peek_enabled) { cookies[:perf_bar_enabled].present? }
RequestStore.fetch(:peek_enabled) { cookie_or_default_value }
else
cookies[:perf_bar_enabled].present?
cookie_or_default_value
end
end
private
def cookie_or_default_value
if cookies[:perf_bar_enabled].present?
cookies[:perf_bar_enabled] == 'true'
else
cookies[:perf_bar_enabled] = 'true' if Rails.env.development?
end
end
end
......@@ -74,7 +74,7 @@ module IssuesHelper
if item.try(:expired?)
'status-box-expired'
elsif item.try(:merged?)
'status-box-merged'
'status-box-mr-merged'
elsif item.closed?
'status-box-mr-closed'
elsif item.try(:upcoming?)
......
......@@ -54,8 +54,16 @@ module TodosHelper
def todo_target_state_pill(todo)
return unless show_todo_state?(todo)
type =
case todo.target
when MergeRequest
'mr'
when Issue
'issue'
end
content_tag(:span, nil, class: 'target-status') do
content_tag(:span, nil, class: "status-box status-box-#{todo.target.state.dasherize}") do
content_tag(:span, nil, class: "status-box status-box-#{type}-#{todo.target.state.dasherize}") do
todo.target.state.capitalize
end
end
......
......@@ -46,10 +46,11 @@ class PushEvent < Event
# Returns PushEvent instances for which no merge requests have been created.
def self.without_existing_merge_requests
existing_mrs = MergeRequest.except(:order)
existing_mrs = MergeRequest.except(:order, :where)
.select(1)
.where('merge_requests.source_project_id = events.project_id')
.where('merge_requests.source_branch = push_event_payloads.ref')
.where(state: :opened)
# For reasons unknown the use of #eager_load will result in the
# "push_event_payload" association not being set. Because of this we're
......
......@@ -13,6 +13,7 @@ module Labels
update_issuables(new_label, batched_ids)
update_issue_board_lists(new_label, batched_ids)
update_priorities(new_label, batched_ids)
subscribe_users(new_label, batched_ids)
# Order is important, project labels need to be last
update_project_labels(batched_ids)
end
......@@ -26,6 +27,15 @@ module Labels
private
def subscribe_users(new_label, label_ids)
# users can be subscribed to multiple labels that will be merged into the group one
# we want to keep only one subscription / user
ids_to_update = Subscription.where(subscribable_id: label_ids, subscribable_type: 'Label')
.group(:user_id)
.pluck('MAX(id)')
Subscription.where(id: ids_to_update).update_all(subscribable_id: new_label.id)
end
def label_ids_for_merge(new_label)
LabelsFinder
.new(current_user, title: new_label.title, group_id: project.group.id)
......@@ -53,7 +63,7 @@ module Labels
end
def update_project_labels(label_ids)
Label.where(id: label_ids).delete_all
Label.where(id: label_ids).destroy_all
end
def clone_label_to_group_label(label)
......
......@@ -54,6 +54,7 @@ module MergeRequests
source_project_id: project.id,
source_branch: branch_name,
target_project_id: project.id,
target_branch: ref,
milestone_id: issue.milestone_id
}
end
......
......@@ -34,7 +34,7 @@
.form-group.visibility-level-setting
= f.label :visibility_level, class: 'label-light' do
Visibility Level
= link_to icon('question-circle'), help_page_path("public_access/public_access"), aria: { label: 'Documentation for Visibility Level' }
= link_to icon('question-circle'), help_page_path("public_access/public_access"), aria: { label: 'Documentation for Visibility Level' }, target: '_blank', rel: 'noopener noreferrer'
= render 'shared/visibility_level', f: f, visibility_level: visibility_level.to_i, can_change_visibility_level: true, form_model: @project, with_label: false
= f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
......
#modal-create-new-dir.modal
.modal-dialog
.modal-dialog.modal-lg
.modal-content
.modal-header
%a.close{ href: "#", "data-dismiss" => "modal" } ×
......
#modal-upload-blob.modal
.modal-dialog
.modal-dialog.modal-lg
.modal-content
.modal-header
%a.close{ href: "#", "data-dismiss" => "modal" } ×
......
......@@ -24,7 +24,7 @@
- elsif @build.has_expiring_artifacts?
%p.build-detail-row
The artifacts will be removed in
%span.js-artifacts-remove= @build.artifacts_expire_at
%span= time_ago_in_words @build.artifacts_expire_at
- if @build.artifacts?
.btn-group.btn-group-justified{ role: :group }
......
---
title: Keep subscribers when promoting labels to group labels
merge_request:
author:
type: fixed
---
title: increase-readability-of-colored-text-in-job-output-log
merge_request:
author:
type: other
---
title: Last push widget will show banner for new pushes to previously merged branch
merge_request:
author:
type: changed
---
title: Adds sorting to deployments API
merge_request: !16396
author: Jacopo Beschi @jacopo-beschi
type: added
---
title: Set target_branch to the ref branch when creating MR from issue
merge_request:
author:
type: fixed
---
title: Fix closed text for issues on Todos page
merge_request:
author:
type: fixed
---
title: Fix links to uploaded files on wiki pages
merge_request: 16499
author:
type: fixed
---
title: Support PostgreSQL 10
merge_request: 16471
author:
type: added
---
title: Open visibility level help in a new tab
merge_request:
author: Jussi Räsänen
type: fixed
---
title: Fix tooltip displayed for running manual actions
merge_request: 16489
author:
type: fixed
---
title: Prevent RevList failing on non utf8 paths
merge_request: 16440
author:
type: fixed
---
title: Adjust modal style to new design
merge_request: 16310
author:
type: other
raise "Vendored ActiveRecord 5 code! Delete #{__FILE__}!" if ActiveRecord::VERSION::MAJOR >= 5
require 'active_record/connection_adapters/postgresql_adapter'
require 'active_record/connection_adapters/postgresql/schema_statements'
#
# Monkey-patch the refused Rails 4.2 patch at https://github.com/rails/rails/pull/31330
#
# Updates sequence logic to support PostgreSQL 10.
#
# rubocop:disable all
module ActiveRecord
module ConnectionAdapters
# We need #postgresql_version to be public as in ActiveRecord 5 for seed_fu
# to work. In ActiveRecord 4, it is protected.
# https://github.com/mbleigh/seed-fu/issues/123
class PostgreSQLAdapter
public :postgresql_version
end
module PostgreSQL
module SchemaStatements
# Resets the sequence of a table's primary key to the maximum value.
def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
unless pk and sequence
default_pk, default_sequence = pk_and_sequence_for(table)
pk ||= default_pk
sequence ||= default_sequence
end
if @logger && pk && !sequence
@logger.warn "#{table} has primary key #{pk} with no default sequence"
end
if pk && sequence
quoted_sequence = quote_table_name(sequence)
max_pk = select_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}")
if max_pk.nil?
if postgresql_version >= 100000
minvalue = select_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass")
else
minvalue = select_value("SELECT min_value FROM #{quoted_sequence}")
end
end
select_value <<-end_sql, 'SCHEMA'
SELECT setval(#{quote(quoted_sequence)}, #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})
end_sql
end
end
end
end
end
end
# rubocop:enable all
class AddMergeRequestStateIndex < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :merge_requests, [:source_project_id, :source_branch],
where: "state = 'opened'",
name: 'index_merge_requests_on_source_project_and_branch_state_opened'
end
def down
remove_concurrent_index_by_name :merge_requests,
'index_merge_requests_on_source_project_and_branch_state_opened'
end
end
......@@ -1468,6 +1468,7 @@ ActiveRecord::Schema.define(version: 20180105233807) do
add_index "merge_requests", ["merge_user_id"], name: "index_merge_requests_on_merge_user_id", where: "(merge_user_id IS NOT NULL)", using: :btree
add_index "merge_requests", ["milestone_id"], name: "index_merge_requests_on_milestone_id", using: :btree
add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree
add_index "merge_requests", ["source_project_id", "source_branch"], name: "index_merge_requests_on_source_project_and_branch_state_opened", where: "((state)::text = 'opened'::text)", using: :btree
add_index "merge_requests", ["source_project_id", "source_branch"], name: "index_merge_requests_on_source_project_id_and_source_branch", using: :btree
add_index "merge_requests", ["target_branch"], name: "index_merge_requests_on_target_branch", using: :btree
add_index "merge_requests", ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid", unique: true, using: :btree
......
......@@ -11,6 +11,8 @@ GET /projects/:id/deployments
| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `order_by`| string | no | Return deployments ordered by `id` or `iid` or `created_at` or `ref` fields. Default is `id` |
| `sort` | string | no | Return deployments sorted in `asc` or `desc` order. Default is `asc` |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/deployments"
......
......@@ -10,6 +10,7 @@ This is what the `.gitlab-ci.yml` file looks like for this project:
```yaml
test:
stage: test
script:
- apt-get update -qy
- apt-get install -y nodejs
......@@ -18,7 +19,7 @@ test:
- bundle exec rake test
staging:
type: deploy
stage: deploy
script:
- gem install dpl
- dpl --provider=heroku --app=gitlab-ci-ruby-test-staging --api-key=$HEROKU_STAGING_API_KEY
......@@ -26,7 +27,7 @@ staging:
- master
production:
type: deploy
stage: deploy
script:
- gem install dpl
- dpl --provider=heroku --app=gitlab-ci-ruby-test-prod --api-key=$HEROKU_PRODUCTION_API_KEY
......
......@@ -123,7 +123,7 @@ Existing users using GitLab with MySQL/MariaDB are advised to
### PostgreSQL Requirements
As of GitLab 10.0, PostgreSQL 9.6 or newer (but less than 10) is required, and earlier versions are
As of GitLab 10.0, PostgreSQL 9.6 or newer is required, and earlier versions are
not supported. We highly recommend users to use PostgreSQL 9.6 as this
is the PostgreSQL version used for development and testing.
......
......@@ -15,11 +15,13 @@ module API
end
params do
use :pagination
optional :order_by, type: String, values: %w[id iid created_at ref], default: 'id', desc: 'Return deployments ordered by `id` or `iid` or `created_at` or `ref`'
optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
end
get ':id/deployments' do
authorize! :read_deployment, user_project
present paginate(user_project.deployments), with: Entities::Deployment
present paginate(user_project.deployments.order(params[:order_by] => params[:sort])), with: Entities::Deployment
end
desc 'Gets a specific deployment' do
......
......@@ -108,7 +108,10 @@ module Backup
$progress.puts "Please make sure that file name ends with #{FILE_NAME_SUFFIX}"
exit 1
elsif backup_file_list.many? && ENV["BACKUP"].nil?
$progress.puts 'Found more than one backup, please specify which one you want to restore:'
$progress.puts 'Found more than one backup:'
# print list of available backups
$progress.puts " " + available_timestamps.join("\n ")
$progress.puts 'Please specify which one you want to restore:'
$progress.puts 'rake gitlab:backup:restore BACKUP=timestamp_of_backup'
exit 1
end
......@@ -169,6 +172,10 @@ module Backup
@backup_file_list ||= Dir.glob("*#{FILE_NAME_SUFFIX}")
end
def available_timestamps
@backup_file_list.map {|item| item.gsub("#{FILE_NAME_SUFFIX}", "")}
end
def connect_to_remote_directory(connection_settings)
# our settings use string keys, but Fog expects symbols
connection = ::Fog::Storage.new(connection_settings.symbolize_keys)
......
......@@ -9,6 +9,10 @@ module Banzai
end
def apply_rules
# Special case: relative URLs beginning with `/uploads/` refer to
# user-uploaded files and will be handled elsewhere.
return @uri.to_s if @uri.relative? && @uri.path.starts_with?('/uploads/')
apply_file_link_rules!
apply_hierarchical_link_rules!
apply_relative_link_rules!
......
......@@ -2,6 +2,9 @@ module Gitlab
module Ci
module Status
module Build
##
# Extended status for playable manual actions.
#
class Action < Status::Extended
def label
if has_action?
......@@ -12,7 +15,7 @@ module Gitlab
end
def self.matches?(build, user)
build.action?
build.playable?
end
end
end
......
......@@ -621,37 +621,6 @@ module Gitlab
end
end
# Returns branch names collection that contains the special commit(SHA1
# or name)
#
# Ex.
# repo.branch_names_contains('master')
#
def branch_names_contains(commit)
branches_contains(commit).map { |c| c.name }
end
# Returns branch collection that contains the special commit(SHA1 or name)
#
# Ex.
# repo.branch_names_contains('master')
#
def branches_contains(commit)
commit_obj = rugged.rev_parse(commit)
parent = commit_obj.parents.first unless commit_obj.parents.empty?
walker = Rugged::Walker.new(rugged)
rugged.branches.select do |branch|
walker.push(branch.target_id)
walker.hide(parent) if parent
result = walker.any? { |c| c.oid == commit_obj.oid }
walker.reset
result
end
end
# Get refs hash which key is SHA1
# and value is a Rugged::Reference
def refs_hash
......
......@@ -95,7 +95,7 @@ module Gitlab
object_output.map do |output_line|
sha, path = output_line.split(' ', 2)
next if require_path && path.blank?
next if require_path && path.to_s.empty?
sha
end.reject(&:nil?)
......
......@@ -6,6 +6,7 @@ module Gitlab
EXPIRY_TIME = 5.minutes
def self.enabled?(user = nil)
return true if Rails.env.development?
return false unless user && allowed_group_id
allowed_user_ids.include?(user.id)
......
......@@ -3,9 +3,9 @@ module QA
module Project
module Settings
module Common
def expand(selector)
def expand(element_name)
page.within('#content-body') do
find(selector).click
click_element(element_name)
yield
end
......
......@@ -3,12 +3,19 @@ module QA
module Project
module Settings
class DeployKeys < Page::Base
##
# TODO, define all selectors required by this page object
#
# See gitlab-org/gitlab-qa#154
#
view 'app/views/projects/deploy_keys/edit.html.haml'
view 'app/views/projects/deploy_keys/_form.html.haml' do
element :deploy_key_title, 'text_field :title'
element :deploy_key_key, 'text_area :key'
end
view 'app/assets/javascripts/deploy_keys/components/app.vue' do
element :deploy_keys_section, /class=".*deploy\-keys.*"/
end
view 'app/assets/javascripts/deploy_keys/components/key.vue' do
element :key_title, /class=".*title.*"/
element :key_title_field, '{{ deployKey.title }}'
end
def fill_key_title(title)
fill_in 'deploy_key_title', with: title
......
......@@ -5,15 +5,12 @@ module QA
class Repository < Page::Base
include Common
##
# TODO, define all selectors required by this page object
#
# See gitlab-org/gitlab-qa#154
#
view 'app/views/projects/settings/repository/show.html.haml'
view 'app/views/projects/deploy_keys/_index.html.haml' do
element :expand_deploy_keys
end
def expand_deploy_keys(&block)
expand('.qa-expand-deploy-keys') do
expand(:expand_deploy_keys) do
DeployKeys.perform(&block)
end
end
......
require 'rails_helper'
describe 'User can display performance bar', :js do
shared_examples 'performance bar is disabled' do
shared_examples 'performance bar cannot be displayed' do
it 'does not show the performance bar by default' do
expect(page).not_to have_css('#peek')
end
......@@ -17,7 +17,7 @@ describe 'User can display performance bar', :js do
end
end
shared_examples 'performance bar is enabled' do
shared_examples 'performance bar can be displayed' do
it 'does not show the performance bar by default' do
expect(page).not_to have_css('#peek')
end
......@@ -33,6 +33,18 @@ describe 'User can display performance bar', :js do
end
end
shared_examples 'performance bar is enabled by default in development' do
before do
allow(Rails.env).to receive(:development?).and_return(true)
end
it 'shows the performance bar by default' do
refresh # Because we're stubbing Rails.env after the 1st visit to root_path
expect(page).to have_css('#peek')
end
end
let(:group) { create(:group) }
context 'when user is logged-out' do
......@@ -45,7 +57,7 @@ describe 'User can display performance bar', :js do
stub_application_setting(performance_bar_allowed_group_id: nil)
end
it_behaves_like 'performance bar is disabled'
it_behaves_like 'performance bar cannot be displayed'
end
context 'when the performance_bar feature is enabled' do
......@@ -53,7 +65,7 @@ describe 'User can display performance bar', :js do
stub_application_setting(performance_bar_allowed_group_id: group.id)
end
it_behaves_like 'performance bar is disabled'
it_behaves_like 'performance bar cannot be displayed'
end
end
......@@ -72,7 +84,8 @@ describe 'User can display performance bar', :js do
stub_application_setting(performance_bar_allowed_group_id: nil)
end
it_behaves_like 'performance bar is disabled'
it_behaves_like 'performance bar cannot be displayed'
it_behaves_like 'performance bar is enabled by default in development'
end
context 'when the performance_bar feature is enabled' do
......@@ -80,7 +93,8 @@ describe 'User can display performance bar', :js do
stub_application_setting(performance_bar_allowed_group_id: group.id)
end
it_behaves_like 'performance bar is enabled'
it_behaves_like 'performance bar is enabled by default in development'
it_behaves_like 'performance bar can be displayed'
end
end
end
......@@ -52,11 +52,6 @@ describe('Job', () => {
expect($('.build-job[data-stage="test"]').is(':visible')).toBe(false);
expect($('.build-job[data-stage="deploy"]').is(':visible')).toBe(false);
});
it('displays the remove date correctly', () => {
const removeDateElement = document.querySelector('.js-artifacts-remove');
expect(removeDateElement.innerText.trim()).toBe('1 year remaining');
});
});
describe('running build', () => {
......
......@@ -13,7 +13,7 @@ describe('Pipelines Async Button', () => {
propsData: {
endpoint: '/foo',
title: 'Foo',
icon: 'fa fa-foo',
icon: 'repeat',
cssClass: 'bar',
},
}).$mount();
......@@ -23,8 +23,8 @@ describe('Pipelines Async Button', () => {
expect(component.$el.tagName).toEqual('BUTTON');
});
it('should render the provided icon', () => {
expect(component.$el.querySelector('i').getAttribute('class')).toContain('fa fa-foo');
it('should render svg icon', () => {
expect(component.$el.querySelector('svg')).not.toBeNull();
});
it('should render the provided title', () => {
......
......@@ -404,7 +404,7 @@ describe('MRWidgetReadyToMerge', () => {
setTimeout(() => {
const statusBox = document.querySelector('.status-box');
expect(statusBox.classList.contains('status-box-merged')).toBeTruthy();
expect(statusBox.classList.contains('status-box-mr-merged')).toBeTruthy();
expect(statusBox.textContent).toContain('Merged');
done();
......
......@@ -194,6 +194,12 @@ describe Backup::Manager do
)
end
it 'prints the list of available backups' do
expect { subject.unpack }.to raise_error SystemExit
expect(progress).to have_received(:puts)
.with(a_string_matching('1451606400_2016_01_01_1.2.3\n 1451520000_2015_12_31'))
end
it 'fails the operation and prints an error' do
expect { subject.unpack }.to raise_error SystemExit
expect(progress).to have_received(:puts)
......
......@@ -10,15 +10,23 @@ describe Banzai::Filter::WikiLinkFilter do
it "doesn't rewrite absolute links" do
filtered_link = filter("<a href='http://example.com:8000/'>Link</a>", project_wiki: wiki).children[0]
expect(filtered_link.attribute('href').value).to eq('http://example.com:8000/')
end
it "doesn't rewrite links to project uploads" do
filtered_link = filter("<a href='/uploads/a.test'>Link</a>", project_wiki: wiki).children[0]
expect(filtered_link.attribute('href').value).to eq('/uploads/a.test')
end
describe "invalid links" do
invalid_links = ["http://:8080", "http://", "http://:8080/path"]
invalid_links.each do |invalid_link|
it "doesn't rewrite invalid invalid_links like #{invalid_link}" do
filtered_link = filter("<a href='#{invalid_link}'>Link</a>", project_wiki: wiki).children[0]
expect(filtered_link.attribute('href').value).to eq(invalid_link)
end
end
......
......@@ -37,16 +37,16 @@ describe Gitlab::Ci::Status::Build::Action do
describe '.matches?' do
subject { described_class.matches?(build, user) }
context 'when build is an action' do
let(:build) { create(:ci_build, :manual) }
context 'when build is playable action' do
let(:build) { create(:ci_build, :playable) }
it 'is a correct match' do
expect(subject).to be true
end
end
context 'when build is not manual' do
let(:build) { create(:ci_build) }
context 'when build is not playable action' do
let(:build) { create(:ci_build, :non_playable) }
it 'does not match' do
expect(subject).to be false
......
......@@ -1104,14 +1104,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
describe "branch_names_contains" do
subject { repository.branch_names_contains(SeedRepo::LastCommit::ID) }
it { is_expected.to include('master') }
it { is_expected.not_to include('feature') }
it { is_expected.not_to include('fix') }
end
describe '#autocrlf' do
before(:all) do
@repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
......
......@@ -39,7 +39,7 @@ describe Gitlab::Git::RevList do
]
expect(rev_list).to receive(:popen).with(*params) do |*_, lazy_block:|
lazy_block.call(output.split("\n").lazy)
lazy_block.call(output.lines.lazy.map(&:chomp))
end
end
......@@ -64,6 +64,15 @@ describe Gitlab::Git::RevList do
expect(rev_list.new_objects(require_path: true)).to eq(%w[sha2])
end
it 'can handle non utf-8 paths' do
non_utf_char = [0x89].pack("c*").force_encoding("UTF-8")
stub_lazy_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha2 πå†h/†ø/ƒîlé#{non_utf_char}\nsha1")
rev_list.new_objects(require_path: true) do |object_ids|
expect(object_ids.force).to eq(%w[sha2])
end
end
it 'can yield a lazy enumerator' do
stub_lazy_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2")
......
......@@ -63,12 +63,14 @@ describe PushEvent do
let(:event2) { create(:push_event, project: project) }
let(:event3) { create(:push_event, project: project) }
let(:event4) { create(:push_event, project: project) }
let(:event5) { create(:push_event, project: project) }
before do
create(:push_event_payload, event: event1, ref: 'foo', action: :created)
create(:push_event_payload, event: event2, ref: 'bar', action: :created)
create(:push_event_payload, event: event3, ref: 'baz', action: :removed)
create(:push_event_payload, event: event4, ref: 'baz', ref_type: :tag)
create(:push_event_payload, event: event3, ref: 'qux', action: :created)
create(:push_event_payload, event: event4, ref: 'baz', action: :removed)
create(:push_event_payload, event: event5, ref: 'baz', ref_type: :tag)
project.repository.create_branch('bar', 'master')
......@@ -78,6 +80,16 @@ describe PushEvent do
target_project: project,
source_branch: 'bar'
)
project.repository.create_branch('qux', 'master')
create(
:merge_request,
:closed,
source_project: project,
target_project: project,
source_branch: 'qux'
)
end
let(:relation) { described_class.without_existing_merge_requests }
......@@ -86,16 +98,20 @@ describe PushEvent do
expect(relation).to include(event1)
end
it 'does not include events that have a corresponding merge request' do
it 'does not include events that have a corresponding open merge request' do
expect(relation).not_to include(event2)
end
it 'includes events that has corresponding closed/merged merge requests' do
expect(relation).to include(event3)
end
it 'does not include events for removed refs' do
expect(relation).not_to include(event3)
expect(relation).not_to include(event4)
end
it 'does not include events for pushing to tags' do
expect(relation).not_to include(event4)
expect(relation).not_to include(event5)
end
end
......
......@@ -3,24 +3,65 @@ require 'spec_helper'
describe API::Deployments do
let(:user) { create(:user) }
let(:non_member) { create(:user) }
let(:project) { deployment.environment.project }
let!(:deployment) { create(:deployment) }
before do
project.add_master(user)
end
describe 'GET /projects/:id/deployments' do
let(:project) { create(:project) }
let!(:deployment_1) { create(:deployment, project: project, iid: 11, ref: 'master', created_at: Time.now) }
let!(:deployment_2) { create(:deployment, project: project, iid: 12, ref: 'feature', created_at: 1.day.ago) }
let!(:deployment_3) { create(:deployment, project: project, iid: 8, ref: 'feature', created_at: 2.days.ago) }
context 'as member of the project' do
it 'returns projects deployments' do
it 'returns projects deployments sorted by id asc' do
get api("/projects/#{project.id}/deployments", user)
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(1)
expect(json_response.first['iid']).to eq(deployment.iid)
expect(json_response.size).to eq(3)
expect(json_response.first['iid']).to eq(deployment_1.iid)
expect(json_response.first['sha']).to match /\A\h{40}\z/
expect(json_response.second['iid']).to eq(deployment_2.iid)
expect(json_response.last['iid']).to eq(deployment_3.iid)
end
describe 'ordering' do
using RSpec::Parameterized::TableSyntax
let(:order_by) { nil }
let(:sort) { nil }
subject { get api("/projects/#{project.id}/deployments?order_by=#{order_by}&sort=#{sort}", user) }
def expect_deployments(ordered_deployments)
json_response.each_with_index do |deployment_json, index|
expect(deployment_json['id']).to eq(public_send(ordered_deployments[index]).id)
end
end
before do
subject
end
where(:order_by, :sort, :ordered_deployments) do
'created_at' | 'asc' | [:deployment_3, :deployment_2, :deployment_1]
'created_at' | 'desc' | [:deployment_1, :deployment_2, :deployment_3]
'id' | 'asc' | [:deployment_1, :deployment_2, :deployment_3]
'id' | 'desc' | [:deployment_3, :deployment_2, :deployment_1]
'iid' | 'asc' | [:deployment_3, :deployment_1, :deployment_2]
'iid' | 'desc' | [:deployment_2, :deployment_1, :deployment_3]
'ref' | 'asc' | [:deployment_2, :deployment_3, :deployment_1]
'ref' | 'desc' | [:deployment_1, :deployment_2, :deployment_3]
end
with_them do
it 'returns the deployments ordered' do
expect_deployments(ordered_deployments)
end
end
end
end
......@@ -34,6 +75,9 @@ describe API::Deployments do
end
describe 'GET /projects/:id/deployments/:deployment_id' do
let(:project) { deployment.environment.project }
let!(:deployment) { create(:deployment) }
context 'as a member of the project' do
it 'returns the projects deployment' do
get api("/projects/#{project.id}/deployments/#{deployment.id}", user)
......
......@@ -15,6 +15,7 @@ describe API::V3::Builds do
before do |example|
build
create(:ci_build, :skipped, pipeline: pipeline)
unless example.metadata[:skip_before_request]
......
......@@ -85,6 +85,19 @@ describe Labels::PromoteService do
change(project_3.labels, :count).by(-1)
end
it 'keeps users\' subscriptions' do
user2 = create(:user)
project_label_1_1.subscriptions.create(user: user, subscribed: true)
project_label_2_1.subscriptions.create(user: user, subscribed: true)
project_label_3_2.subscriptions.create(user: user, subscribed: true)
project_label_2_1.subscriptions.create(user: user2, subscribed: true)
expect { service.execute(project_label_1_1) }.to change { Subscription.count }.from(4).to(3)
expect(new_label.subscribed?(user)).to be_truthy
expect(new_label.subscribed?(user2)).to be_truthy
end
it 'recreates priorities' do
service.execute(project_label_1_1)
......
......@@ -112,5 +112,24 @@ describe MergeRequests::CreateFromIssueService do
expect(result[:merge_request].assignee).to eq(user)
end
context 'when ref branch is set' do
subject { described_class.new(project, user, issue_iid: issue.iid, ref: 'feature').execute }
it 'sets the merge request source branch to the new issue branch' do
expect(subject[:merge_request].source_branch).to eq(issue.to_branch_name)
end
it 'sets the merge request target branch to the ref branch' do
expect(subject[:merge_request].target_branch).to eq('feature')
end
context 'when ref branch does not exist' do
it 'does not create a merge request' do
expect { described_class.new(project, user, issue_iid: issue.iid, ref: 'nobr').execute }
.not_to change { project.merge_requests.count }
end
end
end
end
end
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