Commit c3bad68d authored by Amit Rathi's avatar Amit Rathi

Merge branch 'master' into certmanager-temp

parents 3a3841bc fa5900fb
......@@ -463,19 +463,19 @@ danger-review:
rspec-pg:
<<: *rspec-metadata-pg
parallel: 30
parallel: 50
rspec-mysql:
<<: *rspec-metadata-mysql
parallel: 30
parallel: 50
rspec-pg-rails4:
<<: *rspec-metadata-pg-rails4
parallel: 30
parallel: 50
rspec-mysql-rails4:
<<: *rspec-metadata-mysql-rails4
parallel: 30
parallel: 50
static-analysis:
<<: *dedicated-no-docs-no-db-pull-cache-job
......
......@@ -2,6 +2,13 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 11.4.6 (2018-11-18)
### Security (1 change)
- Escape user fullname while rendering autocomplete template to prevent XSS.
## 11.4.5 (2018-11-04)
### Fixed (4 changes, 1 of them is from the community)
......@@ -271,6 +278,13 @@ entry.
- Check frozen string in style builds. (gfyoung)
## 11.3.10 (2018-11-18)
### Security (1 change)
- Escape user fullname while rendering autocomplete template to prevent XSS.
## 11.3.9 (2018-10-31)
### Security (1 change)
......
......@@ -389,7 +389,7 @@ group :test do
gem 'rails-controller-testing' if rails5? # Rails5 only gem.
gem 'test_after_commit', '~> 1.1' unless rails5? # Remove this gem when migrated to rails 5.0. It's been integrated to rails 5.0.
gem 'sham_rack', '~> 1.3.6'
gem 'concurrent-ruby', '~> 1.0.5'
gem 'concurrent-ruby', '~> 1.1'
gem 'test-prof', '~> 0.2.5'
gem 'rspec_junit_formatter'
end
......
......@@ -128,9 +128,9 @@ GEM
concord (0.1.5)
adamantium (~> 0.2.0)
equalizer (~> 0.0.9)
concurrent-ruby (1.0.5)
concurrent-ruby-ext (1.0.5)
concurrent-ruby (= 1.0.5)
concurrent-ruby (1.1.3)
concurrent-ruby-ext (1.1.3)
concurrent-ruby (= 1.1.3)
connection_pool (2.2.2)
crack (0.4.3)
safe_yaml (~> 1.0.0)
......@@ -379,7 +379,7 @@ GEM
json (~> 1.8)
multi_xml (>= 0.5.2)
httpclient (2.8.3)
i18n (1.1.0)
i18n (1.1.1)
concurrent-ruby (~> 1.0)
icalendar (2.4.1)
ice_nine (0.11.2)
......@@ -444,7 +444,7 @@ GEM
activesupport (>= 4)
railties (>= 4)
request_store (~> 1.0)
loofah (2.2.2)
loofah (2.2.3)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.0)
......@@ -453,7 +453,7 @@ GEM
memoist (0.16.0)
memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1)
method_source (0.9.0)
method_source (0.9.2)
mime-types (3.2.2)
mime-types-data (~> 3.2015)
mime-types-data (3.2018.0812)
......@@ -475,7 +475,7 @@ GEM
net-ssh (5.0.1)
netrc (0.11.0)
nio4r (2.3.1)
nokogiri (1.8.4)
nokogiri (1.8.5)
mini_portile2 (~> 2.3.0)
nokogumbo (1.5.0)
nokogiri
......@@ -603,7 +603,7 @@ GEM
get_process_mem (~> 0.2)
puma (>= 2.7, < 4)
pyu-ruby-sasl (0.0.3.3)
rack (2.0.5)
rack (2.0.6)
rack-accept (0.4.5)
rack (>= 0.4)
rack-attack (4.4.1)
......@@ -767,8 +767,8 @@ GEM
ruby-progressbar (1.9.0)
ruby-saml (1.7.2)
nokogiri (>= 1.5.10)
ruby_parser (3.9.0)
sexp_processor (~> 4.1)
ruby_parser (3.11.0)
sexp_processor (~> 4.9)
rubyntlm (0.6.2)
rubypants (0.2.0)
rubyzip (1.2.2)
......@@ -808,7 +808,7 @@ GEM
sentry-raven (2.7.2)
faraday (>= 0.7.6, < 1.0)
settingslogic (2.0.9)
sexp_processor (4.9.0)
sexp_processor (4.11.0)
sham_rack (1.3.6)
rack
shoulda-matchers (3.1.2)
......@@ -967,7 +967,7 @@ DEPENDENCIES
chronic (~> 0.10.2)
chronic_duration (~> 0.10.6)
commonmarker (~> 0.17)
concurrent-ruby (~> 1.0.5)
concurrent-ruby (~> 1.1)
connection_pool (~> 2.0)
creole (~> 0.5.0)
database_cleaner (~> 1.5.0)
......
......@@ -125,9 +125,9 @@ GEM
concord (0.1.5)
adamantium (~> 0.2.0)
equalizer (~> 0.0.9)
concurrent-ruby (1.0.5)
concurrent-ruby-ext (1.0.5)
concurrent-ruby (= 1.0.5)
concurrent-ruby (1.1.3)
concurrent-ruby-ext (1.1.3)
concurrent-ruby (= 1.1.3)
connection_pool (2.2.2)
crack (0.4.3)
safe_yaml (~> 1.0.0)
......@@ -441,7 +441,7 @@ GEM
activesupport (>= 4)
railties (>= 4)
request_store (~> 1.0)
loofah (2.2.2)
loofah (2.2.3)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.0)
......@@ -471,7 +471,7 @@ GEM
net-ldap (0.16.0)
net-ssh (5.0.1)
netrc (0.11.0)
nokogiri (1.8.4)
nokogiri (1.8.5)
mini_portile2 (~> 2.3.0)
nokogumbo (1.5.0)
nokogiri
......@@ -759,8 +759,8 @@ GEM
ruby-progressbar (1.9.0)
ruby-saml (1.7.2)
nokogiri (>= 1.5.10)
ruby_parser (3.9.0)
sexp_processor (~> 4.1)
ruby_parser (3.11.0)
sexp_processor (~> 4.9)
rubyntlm (0.6.2)
rubypants (0.2.0)
rubyzip (1.2.2)
......@@ -800,7 +800,7 @@ GEM
sentry-raven (2.7.2)
faraday (>= 0.7.6, < 1.0)
settingslogic (2.0.9)
sexp_processor (4.9.0)
sexp_processor (4.11.0)
sham_rack (1.3.6)
rack
shoulda-matchers (3.1.2)
......@@ -958,7 +958,7 @@ DEPENDENCIES
chronic (~> 0.10.2)
chronic_duration (~> 0.10.6)
commonmarker (~> 0.17)
concurrent-ruby (~> 1.0.5)
concurrent-ruby (~> 1.1)
connection_pool (~> 2.0)
creole (~> 0.5.0)
database_cleaner (~> 1.5.0)
......
......@@ -95,6 +95,12 @@ picked into the stable branches) up to the 19th of the month. Such merge
requests should have the ~"feature flag" label assigned, and don't require a
corresponding exception request to be created.
In order to build the final package and present the feature for self-hosted
customers, the feature flag should be removed. This should happen before the
22nd, ideally _at least_ 2 days before. That means MRs with feature
flags being picked at the 19th would have a quite tight schedule, so picking
these _earlier_ is preferable.
While rare, release managers may decide to reject picking a change into a stable
branch, even when feature flags are used. This might be necessary if the changes
are deemed problematic, too invasive, or there simply isn't enough time to
......
......@@ -112,7 +112,7 @@ const JumpToDiscussion = Vue.extend({
if (!hasDiscussionsToJumpTo) {
// If there are no discussions to jump to on the current page,
// switch to the notes tab and jump to the first disucssion there.
// switch to the notes tab and jump to the first discussion there.
window.mrTabs.activateTab('show');
activeTab = 'show';
jumpToFirstDiscussion = true;
......
......@@ -23,7 +23,7 @@ export const diffHasAllExpandedDiscussions = (state, getters) => diff => {
};
/**
* Checks if the diff has all discussions collpased
* Checks if the diff has all discussions collapsed
* @param {Object} diff
* @returns {Boolean}
*/
......
......@@ -151,10 +151,16 @@ class GfmAutoComplete {
// Team Members
$input.atwho({
at: '@',
alias: 'users',
displayTpl(value) {
let tmpl = GfmAutoComplete.Loading.template;
if (value.username != null) {
tmpl = GfmAutoComplete.Members.template;
const { avatarTag, username, title } = value;
if (username != null) {
tmpl = GfmAutoComplete.Members.templateFunction({
avatarTag,
username,
title,
});
}
return tmpl;
},
......@@ -565,8 +571,9 @@ GfmAutoComplete.Emoji = {
};
// Team Members
GfmAutoComplete.Members = {
// eslint-disable-next-line no-template-curly-in-string
template: '<li>${avatarTag} ${username} <small>${title}</small></li>',
templateFunction({ avatarTag, username, title }) {
return `<li>${avatarTag} ${username} <small>${_.escape(title)}</small></li>`;
},
};
GfmAutoComplete.Labels = {
template:
......
......@@ -39,7 +39,7 @@ function blockTagText(text, textArea, blockTag, selected) {
}
}
function moveCursor({ textArea, tag, wrapped, removedLastNewLine, select }) {
function moveCursor({ textArea, tag, positionBetweenTags, removedLastNewLine, select }) {
var pos;
if (!textArea.setSelectionRange) {
return;
......@@ -51,7 +51,7 @@ function moveCursor({ textArea, tag, wrapped, removedLastNewLine, select }) {
return textArea.setSelectionRange(startPosition, endPosition);
}
if (textArea.selectionStart === textArea.selectionEnd) {
if (wrapped) {
if (positionBetweenTags) {
pos = textArea.selectionStart - tag.length;
} else {
pos = textArea.selectionStart;
......@@ -67,7 +67,6 @@ function moveCursor({ textArea, tag, wrapped, removedLastNewLine, select }) {
export function insertMarkdownText({ textArea, text, tag, blockTag, selected, wrap, select }) {
var textToInsert,
inserted,
selectedSplit,
startChar,
removedLastNewLine,
......@@ -155,7 +154,7 @@ export function insertMarkdownText({ textArea, text, tag, blockTag, selected, wr
return moveCursor({
textArea,
tag: tag.replace(textPlaceholder, selected),
wrap,
positionBetweenTags: wrap && selected.length === 0,
removedLastNewLine,
select,
});
......@@ -171,10 +170,6 @@ function updateText({ textArea, tag, blockTag, wrap, select }) {
return insertMarkdownText({ textArea, text, tag, blockTag, selected, wrap, select });
}
function replaceRange(s, start, end, substitute) {
return s.substring(0, start) + substitute + s.substring(end);
}
export function addMarkdownListeners(form) {
return $('.js-md', form)
.off('click')
......
......@@ -22,7 +22,7 @@ export default {
},
output: {
type: Object,
requred: true,
required: true,
default: () => ({}),
},
},
......
......@@ -10,6 +10,7 @@ export default class UserOverviewBlock {
limit: DEFAULT_LIMIT,
...options.requestParams,
};
this.postRenderCallback = options.postRenderCallback;
this.loadData();
}
......@@ -43,5 +44,9 @@ export default class UserOverviewBlock {
}
loadingEl.classList.add('hide');
if (this.postRenderCallback) {
this.postRenderCallback.call(this);
}
}
}
......@@ -2,7 +2,8 @@ import $ from 'jquery';
import axios from '~/lib/utils/axios_utils';
import Activities from '~/activities';
import { localTimeAgo } from '~/lib/utils/datetime_utility';
import { __, sprintf } from '~/locale';
import AjaxCache from '~/lib/utils/ajax_cache';
import { __ } from '~/locale';
import flash from '~/flash';
import ActivityCalendar from './activity_calendar';
import UserOverviewBlock from './user_overview_block';
......@@ -62,23 +63,20 @@ import UserOverviewBlock from './user_overview_block';
* </div>
*/
const CALENDAR_TEMPLATES = {
activity: `
<div class="clearfix calendar">
<div class="js-contrib-calendar"></div>
<div class="calendar-hint bottom-right"></div>
</div>
`,
overview: `
<div class="clearfix calendar">
<div class="calendar-hint"></div>
<div class="js-contrib-calendar prepend-top-20"></div>
</div>
`,
};
const CALENDAR_TEMPLATE = `
<div class="clearfix calendar">
<div class="js-contrib-calendar"></div>
<div class="calendar-hint bottom-right"></div>
</div>
`;
const CALENDAR_PERIOD_6_MONTHS = 6;
const CALENDAR_PERIOD_12_MONTHS = 12;
/* computation based on
* width = (group + 1) * this.daySizeWithSpace + this.getExtraWidthPadding(group);
* (see activity_calendar.js)
*/
const OVERVIEW_CALENDAR_BREAKPOINT = 918;
export default class UserTabs {
constructor({ defaultAction, action, parentEl }) {
......@@ -105,6 +103,12 @@ export default class UserTabs {
.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]')
.on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event))
.on('click', '.gl-pagination a', event => this.changeProjectsPage(event));
window.addEventListener('resize', () => this.onResize());
}
onResize() {
this.loadActivityCalendar();
}
changeProjectsPage(e) {
......@@ -167,8 +171,6 @@ export default class UserTabs {
return;
}
this.loadActivityCalendar('activity');
// eslint-disable-next-line no-new
new Activities('#activity');
......@@ -180,10 +182,10 @@ export default class UserTabs {
return;
}
this.loadActivityCalendar('overview');
this.loadActivityCalendar();
UserTabs.renderMostRecentBlocks('#js-overview .activities-block', {
requestParams: { limit: 5 },
requestParams: { limit: 10 },
});
UserTabs.renderMostRecentBlocks('#js-overview .projects-block', {
requestParams: { limit: 10, skip_pagination: true },
......@@ -198,52 +200,39 @@ export default class UserTabs {
container,
url: $(`${container} .overview-content-list`).data('href'),
...options,
postRenderCallback: () => localTimeAgo($('.js-timeago', container)),
});
}
loadActivityCalendar(action) {
const monthsAgo = action === 'overview' ? CALENDAR_PERIOD_6_MONTHS : CALENDAR_PERIOD_12_MONTHS;
loadActivityCalendar() {
const $calendarWrap = this.$parentEl.find('.tab-pane.active .user-calendar');
const calendarPath = $calendarWrap.data('calendarPath');
AjaxCache.retrieve(calendarPath)
.then(data => UserTabs.renderActivityCalendar(data, $calendarWrap))
.catch(() => flash(__('There was an error loading users activity calendar.')));
}
static renderActivityCalendar(data, $calendarWrap) {
const monthsAgo = UserTabs.getVisibleCalendarPeriod($calendarWrap);
const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath');
const utcOffset = $calendarWrap.data('utcOffset');
let utcFormatted = 'UTC';
if (utcOffset !== 0) {
utcFormatted = `UTC${utcOffset > 0 ? '+' : ''}${utcOffset / 3600}`;
}
const calendarHint = __('Issues, merge requests, pushes and comments.');
axios
.get(calendarPath)
.then(({ data }) => {
$calendarWrap.html(CALENDAR_TEMPLATES[action]);
let calendarHint = '';
if (action === 'activity') {
calendarHint = sprintf(
__(
'Summary of issues, merge requests, push events, and comments (Timezone: %{utcFormatted})',
),
{ utcFormatted },
);
} else if (action === 'overview') {
calendarHint = __('Issues, merge requests, pushes and comments.');
}
$calendarWrap.find('.calendar-hint').text(calendarHint);
// eslint-disable-next-line no-new
new ActivityCalendar(
'.tab-pane.active .js-contrib-calendar',
'.tab-pane.active .user-calendar-activities',
data,
calendarActivitiesPath,
utcOffset,
0,
monthsAgo,
);
})
.catch(() => flash(__('There was an error loading users activity calendar.')));
$calendarWrap.html(CALENDAR_TEMPLATE);
$calendarWrap.find('.calendar-hint').text(calendarHint);
// eslint-disable-next-line no-new
new ActivityCalendar(
'.tab-pane.active .js-contrib-calendar',
'.tab-pane.active .user-calendar-activities',
data,
calendarActivitiesPath,
utcOffset,
0,
monthsAgo,
);
}
toggleLoading(status) {
......@@ -267,4 +256,11 @@ export default class UserTabs {
getCurrentAction() {
return this.$parentEl.find('.nav-links a.active').data('action');
}
static getVisibleCalendarPeriod($calendarWrap) {
const width = $calendarWrap.width();
return width < OVERVIEW_CALENDAR_BREAKPOINT
? CALENDAR_PERIOD_6_MONTHS
: CALENDAR_PERIOD_12_MONTHS;
}
}
......@@ -34,14 +34,7 @@ export default {
fileSizeReadable() {
return numberToHumanSize(this.fileSize);
},
dimensionStyles() {
if (!this.isLoaded) return {};
return {
width: `${this.width}px`,
height: `${this.height}px`,
};
},
hasFileSize() {
return this.fileSize > 0;
},
......@@ -89,7 +82,6 @@ export default {
<div>
<div
:class="innerCssClasses"
:style="dimensionStyles"
class="position-relative"
>
<img
......
......@@ -25,7 +25,7 @@ export default {
swipeMaxWidth: undefined,
swipeMaxHeight: undefined,
swipeBarPos: 1,
swipeWrapWidth: undefined,
swipeWrapWidth: 0,
};
},
computed: {
......@@ -63,7 +63,7 @@ export default {
leftValue = clientWidth - spaceLeft;
}
this.swipeWrapWidth = this.swipeMaxWidth - leftValue;
this.swipeWrapWidth = (leftValue / clientWidth) * 100;
this.swipeBarPos = leftValue;
},
startDrag() {
......@@ -81,7 +81,6 @@ export default {
// Add 2 for border width
this.swipeMaxWidth =
Math.max(this.swipeOldImgInfo.renderedWidth, this.swipeNewImgInfo.renderedWidth) + 2;
this.swipeWrapWidth = this.swipeMaxWidth;
this.swipeMaxHeight =
Math.max(this.swipeOldImgInfo.renderedHeight, this.swipeNewImgInfo.renderedHeight) + 2;
......@@ -107,10 +106,6 @@ export default {
<div class="swipe view">
<div
ref="swipeFrame"
:style="{
'width': swipeMaxPixelWidth,
'height': swipeMaxPixelHeight,
}"
class="swipe-frame">
<image-viewer
key="swipeOldImg"
......@@ -123,14 +118,17 @@ export default {
<div
ref="swipeWrap"
:style="{
'width': swipeWrapPixelWidth,
'height': swipeMaxPixelHeight,
width: `${swipeWrapWidth}%`
}"
class="swipe-wrap">
class="swipe-wrap"
>
<image-viewer
key="swipeNewImg"
:render-info="false"
:path="newPath"
:style="{
width: swipeMaxPixelWidth
}"
class="frame added"
@imgLoaded="swipeNewImgLoaded"
>
......
......@@ -19,18 +19,18 @@ export default {
</script>
<template>
<div class="two-up view">
<div class="two-up view d-flex">
<image-viewer
:path="oldPath"
:render-info="true"
inner-css-classes="frame deleted"
class="wrap"
class="wrap w-50"
/>
<image-viewer
:path="newPath"
:render-info="true"
:inner-css-classes="['frame', 'added']"
class="wrap"
class="wrap w-50"
>
<slot
slot="image-overlay"
......
......@@ -253,19 +253,6 @@
right: 7px;
}
.frame {
top: 0;
right: 0;
position: absolute;
&.deleted {
margin: 0;
display: block;
top: 13px;
right: 7px;
}
}
.swipe-bar {
display: block;
height: 100%;
......@@ -435,7 +422,7 @@
.onion-skin.view {
.swipe-wrap {
top: 0;
right: 0;
left: 0;
}
.frame.deleted {
......
......@@ -58,7 +58,7 @@ class EventsFinder
def by_target_type(events)
return events unless Event::TARGET_TYPES[params[:target_type]]
events.where(target_type: Event::TARGET_TYPES[params[:target_type]])
events.where(target_type: Event::TARGET_TYPES[params[:target_type]].name)
end
# rubocop: enable CodeReuse/ActiveRecord
......
......@@ -178,7 +178,7 @@ class GroupDescendantsFinder
end
def sort
params.fetch(:sort, 'id_asc')
params.fetch(:sort, 'created_desc')
end
# rubocop: disable CodeReuse/ActiveRecord
......
......@@ -98,7 +98,7 @@ module Ci
scope :matches_tag_ids, -> (tag_ids) do
matcher = ::ActsAsTaggableOn::Tagging
.where(taggable_type: CommitStatus)
.where(taggable_type: CommitStatus.name)
.where(context: 'tags')
.where('taggable_id = ci_builds.id')
.where.not(tag_id: tag_ids).select('1')
......@@ -108,7 +108,7 @@ module Ci
scope :with_any_tags, -> do
matcher = ::ActsAsTaggableOn::Tagging
.where(taggable_type: CommitStatus)
.where(taggable_type: CommitStatus.name)
.where(context: 'tags')
.where('taggable_id = ci_builds.id').select('1')
......
......@@ -86,7 +86,7 @@ module Avatarable
params[:model].upload_paths(params[:identifier])
end
Upload.where(uploader: AvatarUploader, path: paths).find_each do |upload|
Upload.where(uploader: AvatarUploader.name, path: paths).find_each do |upload|
model = model_class.instantiate('id' => upload.model_id)
loader.call({ model: model, identifier: File.basename(upload.path) }, upload)
......
......@@ -1049,11 +1049,19 @@ class Repository
end
def cache
@cache ||= Gitlab::RepositoryCache.new(self)
@cache ||= if is_wiki
Gitlab::RepositoryCache.new(self, extra_namespace: 'wiki')
else
Gitlab::RepositoryCache.new(self)
end
end
def request_store_cache
@request_store_cache ||= Gitlab::RepositoryCache.new(self, backend: Gitlab::SafeRequestStore)
@request_store_cache ||= if is_wiki
Gitlab::RepositoryCache.new(self, extra_namespace: 'wiki', backend: Gitlab::SafeRequestStore)
else
Gitlab::RepositoryCache.new(self, backend: Gitlab::SafeRequestStore)
end
end
def tags_sorted_by_committed_date
......
......@@ -11,6 +11,12 @@ module Issues
move_issue_to_new_project(issue) || update(issue)
end
def update(issue)
create_merge_request_from_quick_action
super
end
def before_update(issue)
spam_check(issue, current_user)
end
......@@ -93,6 +99,13 @@ module Issues
private
def create_merge_request_from_quick_action
create_merge_request_params = params.delete(:create_merge_request)
return unless create_merge_request_params
MergeRequests::CreateFromIssueService.new(project, current_user, create_merge_request_params).execute
end
def handle_milestone_change(issue)
return if skip_milestone_email
......
......@@ -19,13 +19,15 @@ module MergeRequests
result = CreateBranchService.new(project, current_user).execute(branch_name, ref)
return result if result[:status] == :error
SystemNoteService.new_issue_branch(issue, project, current_user, branch_name)
new_merge_request = create(merge_request)
if new_merge_request.valid?
SystemNoteService.new_merge_request(issue, project, current_user, new_merge_request)
success(new_merge_request)
else
SystemNoteService.new_issue_branch(issue, project, current_user, branch_name)
error(new_merge_request.errors)
end
end
......
......@@ -635,6 +635,22 @@ module QuickActions
@updates[:tag_message] = message
end
desc 'Create a merge request.'
explanation do |branch_name = nil|
branch_text = branch_name ? "branch '#{branch_name}'" : 'a branch'
"Creates #{branch_text} and a merge request to resolve this issue"
end
params "<branch name>"
condition do
issuable.is_a?(Issue) && current_user.can?(:create_merge_request_in, project) && current_user.can?(:push_code, project)
end
command :create_merge_request do |branch_name = nil|
@updates[:create_merge_request] = {
branch_name: branch_name,
issue_iid: issuable.iid
}
end
# rubocop: disable CodeReuse/ActiveRecord
def extract_users(params)
return [] if params.nil?
......
......@@ -407,11 +407,17 @@ module SystemNoteService
def new_issue_branch(issue, project, author, branch)
link = url_helpers.project_compare_url(project, from: project.default_branch, to: branch)
body = "created branch [`#{branch}`](#{link})"
body = "created branch [`#{branch}`](#{link}) to address this issue"
create_note(NoteSummary.new(issue, project, author, body, action: 'branch'))
end
def new_merge_request(issue, project, author, merge_request)
body = "created merge request #{merge_request.to_reference} to address this issue"
create_note(NoteSummary.new(issue, project, author, body, action: 'merge'))
end
# Called when a Mentionable references a Noteable
#
# noteable - Noteable object being referenced
......
......@@ -45,7 +45,7 @@ module Todos
# rubocop: disable CodeReuse/ActiveRecord
def remove_confidential_issue_todos
Todo.where(
target_id: confidential_issues.select(:id), target_type: Issue, user_id: user.id
target_id: confidential_issues.select(:id), target_type: Issue.name, user_id: user.id
).delete_all
end
# rubocop: enable CodeReuse/ActiveRecord
......
......@@ -14,9 +14,9 @@ module Todos
def execute
ProjectFeature.where(project_id: project_ids).each do |project_features|
target_types = []
target_types << Issue if private?(project_features.issues_access_level)
target_types << MergeRequest if private?(project_features.merge_requests_access_level)
target_types << Commit if private?(project_features.repository_access_level)
target_types << Issue.name if private?(project_features.issues_access_level)
target_types << MergeRequest.name if private?(project_features.merge_requests_access_level)
target_types << Commit.name if private?(project_features.repository_access_level)
next if target_types.empty?
......
......@@ -5,7 +5,7 @@
.project-header.d-flex.flex-row.flex-wrap.align-items-center.append-bottom-8
.project-title-row.d-flex.align-items-center
.avatar-container.project-avatar.float-none
= project_icon(@project, alt: @project.name, class: 'avatar avatar-tile')
= project_icon(@project, alt: @project.name, class: 'avatar avatar-tile s24', width: 24, height: 24)
%h1.project-title.d-flex.align-items-baseline.qa-project-name
= @project.name
.project-metadata.d-flex.flex-row.flex-wrap.align-items-baseline
......
- diff_file = viewer.diff_file
- url = url_for(safe_params.merge(action: :diff_for_path, old_path: diff_file.old_path, new_path: diff_file.new_path, file_identifier: diff_file.file_identifier))
.nothing-here-block.diff-collapsed{ data: { diff_for_path: url } }
This diff is collapsed.
%button.click-to-expand.btn.btn-link Click to expand it.
= _("This diff is collapsed.")
%button.click-to-expand.btn.btn-link= _("Click to expand it.")
......@@ -8,7 +8,7 @@
.files-changed-inner
.inline-parallel-buttons.d-none.d-sm-none.d-md-block
- if !diffs_expanded? && diff_files.any? { |diff_file| diff_file.collapsed? }
= link_to 'Expand all', url_for(safe_params.merge(expanded: 1, format: nil)), class: 'btn btn-default'
= link_to _('Expand all'), url_for(safe_params.merge(expanded: 1, format: nil)), class: 'btn btn-default'
- if show_whitespace_toggle
- if current_controller?(:commit)
= commit_diff_whitespace_link(diffs.project, @commit, class: 'd-none d-sm-inline-block')
......
......@@ -12,7 +12,7 @@
- blob = diff_file.blob
.file-actions.d-none.d-sm-block
- if blob&.readable_text?
= link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip', title: "Toggle comments for this file", disabled: @diff_notes_disabled do
= link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip', title: _("Toggle comments for this file"), disabled: @diff_notes_disabled do
= icon('comment')
\
- if editable_diff?(diff_file)
......
......@@ -28,7 +28,7 @@
= diff_file.file_path
- if diff_file.deleted_file?
deleted
= _("deleted")
= copy_file_path_button(diff_file.file_path)
......
.nothing-here-block
This #{viewer.switcher_title} could not be displayed because #{diff_render_error_reason(viewer)}.
= _("This %{viewer} could not be displayed because %{reason}.") % { viewer: viewer.switcher_title, reason: diff_render_error_reason(viewer) }
You can
= diff_render_error_options(viewer).to_sentence(two_words_connector: ' or ', last_word_connector: ', or ').html_safe
......
......@@ -38,4 +38,4 @@
\-#{diff_file.removed_lines}
%li.dropdown-menu-empty-item.hidden
%a
No files found.
= _("No files found.")
- too_big = diff_file.diff_lines.count > Commit::DIFF_SAFE_LINES
- if too_big
.suppressed-container
%a.show-suppressed-diff.js-show-suppressed-diff Changes suppressed. Click to show.
%a.show-suppressed-diff.js-show-suppressed-diff= _("Changes suppressed. Click to show.")
%table.text-file.diff-wrap-lines.code.js-syntax-highlight.commit-diff{ data: diff_view_data, class: too_big ? 'hide' : '' }
= render partial: "projects/diffs/line",
......
.alert.alert-warning
%h4
Too many changes to show.
= _("Too many changes to show.")
.float-right
- if current_controller?(:commit)
= link_to "Plain diff", project_commit_path(@project, @commit, format: :diff), class: "btn btn-sm"
= link_to "Email patch", project_commit_path(@project, @commit, format: :patch), class: "btn btn-sm"
= link_to _("Plain diff"), project_commit_path(@project, @commit, format: :diff), class: "btn btn-sm"
= link_to _("Email patch"), project_commit_path(@project, @commit, format: :patch), class: "btn btn-sm"
- elsif current_controller?('projects/merge_requests/diffs') && @merge_request&.persisted?
= link_to "Plain diff", merge_request_path(@merge_request, format: :diff), class: "btn btn-sm"
= link_to "Email patch", merge_request_path(@merge_request, format: :patch), class: "btn btn-sm"
= link_to _("Plain diff"), merge_request_path(@merge_request, format: :diff), class: "btn btn-sm"
= link_to _("Email patch"), merge_request_path(@merge_request, format: :patch), class: "btn btn-sm"
%p
To preserve performance only
%strong #{diff_files.size} of #{diff_files.real_size}
files are displayed.
= _("To preserve performance only <strong>%{display_size} of ${real_size}</strong> files are displayed.").html_safe % { display_size: diff_files.size, real_size: diff_files.real_size }
.nothing-here-block
File added
= _("File added")
.nothing-here-block
File deleted
= _("File deleted")
- diff_file = viewer.diff_file
.nothing-here-block
File mode changed from #{diff_file.a_mode} to #{diff_file.b_mode}
= _("File mode changed from %{a_mode} to %{b_mode}") % { a_mode: diff_file.a_mode, b_mode: diff_file.b_mode }
.nothing-here-block
No preview for this file type
= _("No preview for this file type")
.nothing-here-block
This diff was suppressed by a .gitattributes entry.
= _("This diff was suppressed by a .gitattributes entry.")
- if environment.external_url && can?(current_user, :read_environment, environment)
= link_to environment.external_url, target: '_blank', rel: 'noopener noreferrer', class: 'btn external-url has-tooltip qa-view-deployment', title: s_('Environments|Open live environment') do
= sprite_icon('external-link')
View deployment
= _("View deployment")
.row.prepend-top-default.append-bottom-default
.col-lg-3
%h4.prepend-top-0
Environments
= _("Environments")
%p
Environments allow you to track deployments of your application
= succeed "." do
= link_to "Read more about environments", help_page_path("ci/environments")
- link_to_read_more = link_to(_("Read more about environments"), help_page_path("ci/environments"))
= _("Environments allow you to track deployments of your application %{link_to_read_more}.").html_safe % { link_to_read_more: link_to_read_more }
= form_for [@project.namespace.becomes(Namespace), @project, @environment], html: { class: 'col-lg-9' } do |f|
= form_errors(@environment)
.form-group
= f.label :name, 'Name', class: 'label-bold'
= f.label :name, _('Name'), class: 'label-bold'
= f.text_field :name, required: true, class: 'form-control'
.form-group
= f.label :external_url, 'External URL', class: 'label-bold'
= f.label :external_url, _('External URL'), class: 'label-bold'
= f.url_field :external_url, class: 'form-control'
.form-actions
= f.submit 'Save', class: 'btn btn-success'
= link_to 'Cancel', project_environments_path(@project), class: 'btn btn-cancel'
= f.submit _('Save'), class: 'btn btn-save'
= link_to _('Cancel'), project_environments_path(@project), class: 'btn btn-cancel'
......@@ -2,6 +2,6 @@
- return unless can?(current_user, :read_environment, environment)
= link_to environment_metrics_path(environment), title: 'See metrics', class: 'btn metrics-button' do
= link_to environment_metrics_path(environment), title: _('See metrics'), class: 'btn metrics-button' do
= sprite_icon('chart')
Monitoring
= _("Monitoring")
- @no_container = true
- page_title "Edit", @environment.name, "Environments"
- page_title _("Edit"), @environment.name, _("Environments")
%div{ class: container_class }
%h3.page-title
Edit environment
= _('Edit environment')
%hr
= render 'form'
- @no_container = true
- page_title "Environments"
- page_title _("Environments")
#environments-folder-list-view{ data: { endpoint: folder_project_environments_path(@project, @folder, format: :json),
"folder-name" => @folder,
......
- @no_container = true
- page_title "Environments"
- add_to_breadcrumbs("Pipelines", project_pipelines_path(@project))
- page_title _("Environments")
- add_to_breadcrumbs(_("Pipelines"), project_pipelines_path(@project))
#environments-list-view{ data: { environments_data: environments_list_data,
"can-create-deployment" => can?(current_user, :create_deployment, @project).to_s,
......
- @no_container = true
- page_title "Metrics for environment", @environment.name
- page_title _("Metrics for environment"), @environment.name
.prometheus-container{ class: container_class }
#prometheus-graphs{ data: metrics_data(@project, @environment) }
- @no_container = true
- breadcrumb_title "Environments"
- page_title 'New Environment'
- breadcrumb_title _("Environments")
- page_title _("New Environment")
%div{ class: container_class }
%h3.page-title
New environment
= _("New environment")
%hr
= render 'form'
- @no_container = true
- add_to_breadcrumbs "Environments", project_environments_path(@project)
- add_to_breadcrumbs _("Environments"), project_environments_path(@project)
- breadcrumb_title @environment.name
- page_title "Environments"
- page_title _("Environments")
%div{ class: container_class }
- if can?(current_user, :stop_environment, @environment)
......@@ -10,7 +10,7 @@
.modal-content
.modal-header
%h4.modal-title.d-flex.mw-100
Stopping
= s_("Environments|Stopping")
%span.has-tooltip.text-truncate.ml-1.mr-1.flex-fill{ title: @environment.name, data: { container: '#stop-environment-modal' } }
= @environment.name
?
......@@ -40,7 +40,7 @@
= render 'projects/environments/external_url', environment: @environment
= render 'projects/environments/metrics_button', environment: @environment
- if can?(current_user, :update_environment, @environment)
= link_to 'Edit', edit_project_environment_path(@project, @environment), class: 'btn'
= link_to _('Edit'), edit_project_environment_path(@project, @environment), class: 'btn'
- if can?(current_user, :stop_environment, @environment)
= button_tag class: 'btn btn-danger', type: 'button', data: { toggle: 'modal',
target: '#stop-environment-modal' } do
......@@ -52,21 +52,19 @@
.empty-state
.text-content
%h4.state-title
You don't have any deployments right now.
= _("You don't have any deployments right now.")
%p.blank-state-text
Define environments in the deploy stage(s) in
%code .gitlab-ci.yml
to track deployments here.
= _("Define environments in the deploy stage(s) in <code>.gitlab-ci.yml</code> to track deployments here.").html_safe
.text-center
= link_to _("Read more"), help_page_path("ci/environments"), class: "btn btn-success"
- else
.table-holder
.ci-table.environments{ role: 'grid' }
.gl-responsive-table-row.table-row-header{ role: 'row' }
.table-section.section-10{ role: 'columnheader' } ID
.table-section.section-30{ role: 'columnheader' } Commit
.table-section.section-25{ role: 'columnheader' } Job
.table-section.section-15{ role: 'columnheader' } Created
.table-section.section-10{ role: 'columnheader' }= _('ID')
.table-section.section-30{ role: 'columnheader' }= _('Commit')
.table-section.section-25{ role: 'columnheader' }= _('Job')
.table-section.section-15{ role: 'columnheader' }= _('Created')
= render @deployments
......
- @no_container = true
- page_title "Terminal for environment", @environment.name
- page_title _("Terminal for environment"), @environment.name
- content_for :page_specific_javascripts do
= stylesheet_link_tag "xterm.css"
......@@ -9,7 +9,7 @@
.row
.col-sm-6
%h3.page-title
Terminal for environment
= _("Terminal for environment")
= @environment.name
.col-sm-6
......
- @no_container = true
- page_title "Charts"
- page_title _("Contribution Charts")
.repo-charts{ class: container_class }
%h4.sub-header
......
- @no_container = true
- breadcrumb_title "CI / CD Charts"
- page_title _("Charts"), _("Pipelines")
- page_title _("CI / CD Charts")
%div{ class: container_class }
.sub-header-block
......
- labels.each do |label|
%span.label-row.btn-group{ role: "group", aria: { label: label.name }, style: "color: #{text_color_for_bg(label.color)}" }
= link_to_label(label, subject: @project, css_class: 'btn btn-transparent')
%button.btn.btn-transparent.label-remove.js-label-filter-remove{ type: "button", style: "background-color: #{label.color};", data: { label: label.title } }
= icon("times")
#js-authenticate-u2f
%a.btn.btn-block.btn-info#js-login-2fa-device{ href: '#' } Sign in via 2FA code
%a.btn.btn-block.btn-info#js-login-2fa-device{ href: '#' }= _("Sign in via 2FA code")
-# haml-lint:disable InlineJavaScript
%script#js-authenticate-u2f-in-progress{ type: "text/template" }
%p Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.
%p= _("Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.")
%script#js-authenticate-u2f-error{ type: "text/template" }
%div
%p <%= error_message %> (error code: <%= error_code %>)
%a.btn.btn-block.btn-warning#js-u2f-try-again Try again?
%p <%= error_message %> (#{_("error code:")} <%= error_code %>)
%a.btn.btn-block.btn-warning#js-u2f-try-again= _("Try again?")
%script#js-authenticate-u2f-authenticated{ type: "text/template" }
%div
%p We heard back from your U2F device. You have been authenticated.
%p= _("We heard back from your U2F device. You have been authenticated.")
= form_tag(new_user_session_path, method: :post, id: 'js-login-u2f-form') do |f|
- resource_params = params[resource_name].presence || params
= hidden_field_tag 'user[remember_me]', resource_params.fetch(:remember_me, 0)
......
......@@ -2,39 +2,39 @@
-# haml-lint:disable InlineJavaScript
%script#js-register-u2f-not-supported{ type: "text/template" }
%p Your browser doesn't support U2F. Please use Google Chrome desktop (version 41 or newer).
%p= _("Your browser doesn't support U2F. Please use Google Chrome desktop (version 41 or newer).")
%script#js-register-u2f-setup{ type: "text/template" }
- if current_user.two_factor_otp_enabled?
.row.append-bottom-10
.col-md-4
%button#js-setup-u2f-device.btn.btn-info.btn-block Set up new U2F device
%button#js-setup-u2f-device.btn.btn-info.btn-block= _("Set up new U2F device")
.col-md-8
%p Your U2F device needs to be set up. Plug it in (if not already) and click the button on the left.
%p= _("Your U2F device needs to be set up. Plug it in (if not already) and click the button on the left.")
- else
.row.append-bottom-10
.col-md-4
%button#js-setup-u2f-device.btn.btn-info.btn-block{ disabled: true } Set up new U2F device
%button#js-setup-u2f-device.btn.btn-info.btn-block{ disabled: true }= _("Set up new U2F device")
.col-md-8
%p.text-warning You need to register a two-factor authentication app before you can set up a U2F device.
%p.text-warning= _("You need to register a two-factor authentication app before you can set up a U2F device.")
%script#js-register-u2f-in-progress{ type: "text/template" }
%p Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.
%p= _("Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.")
%script#js-register-u2f-error{ type: "text/template" }
%div
%p
%span <%= error_message %> (error code: <%= error_code %>)
%a.btn.btn-warning#js-u2f-try-again Try again?
%span <%= error_message %> (#{_("error code:")} <%= error_code %>)
%a.btn.btn-warning#js-u2f-try-again= _("Try again?")
%script#js-register-u2f-registered{ type: "text/template" }
.row.append-bottom-10
.col-md-12
%p Your device was successfully set up! Give it a name and register it with the GitLab server.
%p= _("Your device was successfully set up! Give it a name and register it with the GitLab server.")
= form_tag(create_u2f_profile_two_factor_auth_path, method: :post) do
.row.append-bottom-10
.col-md-3
= text_field_tag 'u2f_registration[name]', nil, class: 'form-control', placeholder: "Pick a name"
= text_field_tag 'u2f_registration[name]', nil, class: 'form-control', placeholder: _("Pick a name")
.col-md-3
= hidden_field_tag 'u2f_registration[device_response]', nil, class: 'form-control', required: true, id: "js-device-response"
= submit_tag "Register U2F device", class: "btn btn-success"
= submit_tag _("Register U2F device"), class: "btn btn-success"
.row
.col-12
.calendar-block.prepend-top-default.append-bottom-default
.user-calendar.d-none.d-sm-block{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } }
%h4.center.light
= spinner nil, true
.user-calendar-activities.d-none.d-sm-block
.row
.col-md-12.col-lg-6
.calendar-block
.content-block.hide-bottom-border
%h4
= s_('UserProfile|Activity')
.user-calendar.d-none.d-sm-block.text-left{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } }
%h4.center.light
%i.fa.fa-spinner.fa-spin
.user-calendar-activities.d-none.d-sm-block
- if can?(current_user, :read_cross_project)
.activities-block
.border-bottom.prepend-top-16
%h5
= s_('UserProfile|Recent contributions')
.prepend-top-16
.d-flex.align-items-center.border-bottom
%h4.flex-grow
= s_('UserProfile|Activity')
= link_to s_('UserProfile|View all'), user_activity_path, class: "hide js-view-all"
.overview-content-list{ data: { href: user_path } }
.center.light.loading
%i.fa.fa-spinner.fa-spin
.prepend-top-10
= link_to s_('UserProfile|View all'), user_activity_path, class: "hide js-view-all"
= spinner nil, true
.col-md-12.col-lg-6
.projects-block
.border-bottom.prepend-top-16
%h4
= s_('UserProfile|Personal projects')
.prepend-top-16
.d-flex.align-items-center.border-bottom
%h4.flex-grow
= s_('UserProfile|Personal projects')
= link_to s_('UserProfile|View all'), user_projects_path, class: "hide js-view-all"
.overview-content-list{ data: { href: user_projects_path } }
.center.light.loading
%i.fa.fa-spinner.fa-spin
.prepend-top-10
= link_to s_('UserProfile|View all'), user_projects_path, class: "hide js-view-all"
= spinner nil, true
......@@ -124,12 +124,6 @@
- if profile_tab?(:activity)
#activity.tab-pane
.row-content-block.calendar-block.white.second-block.d-none.d-sm-block
.user-calendar{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } }
%h4.center.light
%i.fa.fa-spinner.fa-spin
.user-calendar-activities
- if can?(current_user, :read_cross_project)
%h4.prepend-top-20
= s_('UserProfile|Most Recent Activity')
......
---
title: Creates /create_merge_request quickaction
merge_request: 22485
author: Jacopo Beschi @jacopo-beschi
type: added
---
title: UI improvements to user's profile
merge_request: 22977
author:
type: other
---
title: Fix default sorting for subgroups and projects list
merge_request: 23058
author: Jacopo Beschi @jacopo-beschi
type: fixed
---
title: Refine cursor positioning in Markdown Editor for wrap tags
merge_request: 23085
author: Johann Hubert Sonntagbauer
type: changed
---
title: Fixes to AWS documentation spelling and grammar
merge_request: 23198
author: Brendan O'Leary
type: other
---
title: Drop default value on status column in deployments table
merge_request: 22971
author:
type: other
---
title: Enable even more frozen string for lib/gitlab
merge_request:
author: gfyoung
type: performance
---
title: Change breadcrumb title for contribution charts
merge_request: 23071
author: George Tsiolis
type: changed
---
title: Fix typo in notebook props
merge_request: 23103
author: George Tsiolis
type: other
---
title: Fix project identicon aligning Harry Kiselev
merge_request: 23166
author: Harry Kiselev
type: other
---
title: 'Rails5: Passing a class as a value in an Active Record query is deprecated'
merge_request: 23164
author: Jasper Maes
type: other
---
title: Escape user fullname while rendering autocomplete template to prevent XSS
merge_request:
author:
type: security
---
title: Bump nokogiri, loofah, and rack gems for security updates
merge_request: 23204
author:
type: security
---
title: Prevent templated services from being imported
merge_request:
author:
type: security
......@@ -15,10 +15,10 @@ docs_paths_to_review = docs_paths_requiring_review(helper.all_changed_files)
unless docs_paths_to_review.empty?
message 'This merge request adds or changes files that require a ' \
'review from the docs team.'
'review from the Docs team.'
markdown(<<~MARKDOWN)
## Docs Review
## Docs review
The following files require a review from the Documentation team:
......@@ -32,24 +32,15 @@ the documentation. GitLabbers are also welcome to use the [#docs](https://gitlab
Who to ping [based on DevOps stages](https://about.gitlab.com/handbook/product/categories/#devops-stages):
| Stage | Tech writer |
| ------------- | ----------- |
| ~Create | `@marcia` |
| ~Configure | `@eread` |
| ~Distribution | `@axil` |
| ~Geo | `@eread` |
| ~Gitaly | `@axil` |
| ~Gitter | `@axil` |
| ~Manage | `@eread` |
| ~Monitoring | `@axil` |
| ~Packaging | `@axil` |
| ~Plan | `@mikelewis`|
| ~Release | `marcia` |
| ~Secure | `@axil` |
| ~Verify | `@eread` |
| Tech writer | Stage(s) |
| ------------ | ------------------------------------------------------------ |
| `@marcia` | ~Create ~Release |
| `@axil` | ~Distribution ~Gitaly ~Gitter ~Monitoring ~Packaging ~Secure |
| `@eread` | ~Manage ~Configure ~Geo ~Verify |
| `@mikelewis` | ~Plan |
If you are not sure which category the change falls within, or the change is not
part of one of these categories, you can mention the whole team with `@gl-docsteam`.
part of one of these categories, you can mention one of the usernames above.
MARKDOWN
unless gitlab.mr_labels.include?('Documentation')
......
# frozen_string_literal: true
class DropDefaultValueOnStatusDeployments < ActiveRecord::Migration
DOWNTIME = false
DEPLOYMENT_STATUS_SUCCESS = 2 # Equivalent to Deployment.state_machine.states['success'].value
def up
change_column_default :deployments, :status, nil
end
def down
change_column_default :deployments, :status, DEPLOYMENT_STATUS_SUCCESS
end
end
......@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20181107054254) do
ActiveRecord::Schema.define(version: 20181112103239) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -818,7 +818,7 @@ ActiveRecord::Schema.define(version: 20181107054254) do
t.datetime "created_at"
t.datetime "updated_at"
t.string "on_stop"
t.integer "status", limit: 2, default: 2, null: false
t.integer "status", limit: 2, null: false
t.datetime_with_timezone "finished_at"
t.index ["created_at"], name: "index_deployments_on_created_at", using: :btree
t.index ["deployable_type", "deployable_id"], name: "index_deployments_on_deployable_type_and_deployable_id", using: :btree
......
......@@ -416,19 +416,18 @@ and/or `production`) you can see this information in the merge request itself.
### Go directly from source files to public pages on the environment
> Introduced in GitLab 8.17.
> Introduced in GitLab 8.17. In GitLab 11.5 the file links
are surfaced to the merge request widget.
To go one step further, we can specify a Route Map to get GitLab to show us "View on [environment URL]" buttons to go directly from a file to that file's representation on the deployed website. It will be exposed in a few places:
| In the diff for a merge request, comparison or commit | In the file view |
| ------ | ------ |
| !["View on env" button in merge request diff](img/view_on_env_mr.png) | !["View on env" button in file view](img/view_on_env_blob.png) |
You can specify a Route Map to get GitLab to show "View on <environment URL>"
buttons to go directly from a file to that file's representation on the
[deployed website via Review Apps](review_apps/index.md).
To get this to work, you need to tell GitLab how the paths of files in your repository map to paths of pages on your website, using a Route Map.
A Route Map is a file inside the repository at `.gitlab/route-map.yml`, which contains a YAML array that maps `source` paths (in the repository) to `public` paths (on the website).
This is an example of a route map for [Middleman](https://middlemanapp.com) static websites like [http://about.gitlab.com](https://gitlab.com/gitlab-com/www-gitlab-com):
Below is an example of a route map for [Middleman](https://middlemanapp.com) static websites
like <https://gitlab.com/gitlab-com/www-gitlab-com>:
```yaml
# Team data
......@@ -467,6 +466,25 @@ In the example above, the fact that mappings are evaluated in order of their def
---
Once you have the route mapping set up, it will be exposed in a few places:
- In the merge request widget. The **View app** button will take you to the
environment URL you have set up in `.gitlab-ci.yml`. The dropdown will render
the first 5 matched items from the route map, but you can filter them if more
than 5 are available.
![View app file list in merge request widget](img/view_on_mr_widget.png)
- In the diff for a merge request, comparison, or commit.
!["View on env" button in merge request diff](img/view_on_env_mr.png)
- In the blob file view.
!["View on env" button in file view](img/view_on_env_blob.png) |
---
We now have a full development cycle, where our app is tested, built, deployed
as a Review app, deployed to a staging server once the merge request is merged,
and finally manually deployed to the production server. What we just described
......
doc/ci/img/view_on_env_blob.png

32.2 KB | W: | H:

doc/ci/img/view_on_env_blob.png

11.6 KB | W: | H:

doc/ci/img/view_on_env_blob.png
doc/ci/img/view_on_env_blob.png
doc/ci/img/view_on_env_blob.png
doc/ci/img/view_on_env_blob.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -1682,6 +1682,11 @@ include:
NOTE: **Note:**
The remote file must be publicly accessible through a simple GET request, as we don't support authentication schemas in the remote URL.
NOTE: **Note:**
In order to include files from another repository inside your local network,
you may need to enable the **Allow requests to the local network from hooks and services** checkbox
located in the **Settings > Network > Outbound requests** section within the **Admin area**.
---
......
......@@ -134,9 +134,9 @@ should be more than enough.
When removing an index make sure to use the method `remove_concurrent_index` instead
of the regular `remove_index` method. The `remove_concurrent_index` method
automatically drops concurrent indexes when using PostgreSQL, removing the
need for downtime. To use this method you must disable transactions by calling
the method `disable_ddl_transaction!` in the body of your migration class like
so:
need for downtime. To use this method you must disable single-transaction mode
by calling the method `disable_ddl_transaction!` in the body of your migration
class like so:
```ruby
class MyMigration < ActiveRecord::Migration
......
......@@ -10,7 +10,7 @@ List of books and resources, that may be worth reading.
1. **The Humble Programmer**
Edsger W. Dijkstra, 1972 ([paper](http://dl.acm.org/citation.cfm?id=361591))
Edsger W. Dijkstra, 1972 ([paper](https://dl.acm.org/citation.cfm?id=361591))
## Programming
......
......@@ -9,7 +9,7 @@ in [significantly degraded performance](https://gitlab.com/gitlab-org/gitlab-ee/
GitLab on AWS can leverage many of the services that are already
configurable with High Availability. These services have a lot of
flexibility and are able to adopt to most companies, best of all is the
flexibility and are able to adapt to most companies, best of all is the
ability to automate both vertical and horizontal scaling.
In this article we'll go through a basic HA setup where we'll start by
......@@ -55,9 +55,9 @@ and from the Actions dropdown choose Edit DNS Hostnames and select Yes.
### Subnet
Now let's create some subnets in different Availability Zones. Make sure
that each subnet is associated the the VPC we just created, that it has
that each subnet is associated to the VPC we just created, that it has
a distinct VPC and lastly that CIDR blocks don't overlap. This will also
allow us to enable multi AZ for redundancy.
allow us to enable multi-AZ for redundancy.
We will create private and public subnets to match load balancers and
RDS instances as well.
......@@ -98,7 +98,7 @@ traffic from any destination.
![Subnet Config](img/ig-rt.png)
Before leaving this screen select the next tab to the rgiht which is
Before leaving this screen select the next tab to the right which is
Subnet Associations and add our public subnets. If you followed our
naming convention they should be easy to find.
......@@ -106,8 +106,8 @@ naming convention they should be easy to find.
## Database with RDS
For our database server we will use Amazon RDS which offers Multi AZ
for redundancy. Lets start by creating a subnet group and then we'll
For our database server we will use Amazon RDS which offers Multi-AZ
for redundancy. Let's start by creating a subnet group and then we'll
create the actual RDS instance.
### Subnet Group
......@@ -122,7 +122,7 @@ the VPC ID dropdown and at the bottom we can add our private subnets.
Select the RDS service from the Database section and create a new
PostgreSQL instance. After choosing between a Production or
Development instance we'll start with the actual configuration. On the
image bellow we have the settings for this article but note the
image below we have the settings for this article but note the
following two options which are of particular interest for HA:
1. Multi-AZ-Deployment is recommended as redundancy. Read more at
......@@ -133,7 +133,7 @@ IOPS (SSD) is best suited for HA. Read more about it at
![RDS Instance Specs](img/instance_specs.png)
The rest of the setting on this page request a DB identifier, username
The rest of the setting on this page request a DB identifier, username,
and a master password. We've chosen to use `gitlab-ha`, `gitlab` and a
very secure password respectively. Keep these in hand for later.
......@@ -152,7 +152,7 @@ EC is an in-memory hosted caching solution. Redis maintains its own
persistence and is used for certain types of application.
Let's choose the ElastiCache service in the Database section from our
AWS console. Now lets create a cache subnet group which will be very
AWS console. Now let's create a cache subnet group which will be very
similar to the RDS subnet group. Make sure to select our VPC and its
private subnets.
......@@ -160,7 +160,7 @@ private subnets.
Now press the Launch a Cache Cluster and choose Redis for our
DB engine. You'll be able to configure details such as replication,
Multi AZ and node types. The second section will allow us to choose our
Multi-AZ and node types. The second section will allow us to choose our
subnet and security group and
![Redis Cluster details](img/redis-cluster-det.png)
......@@ -274,7 +274,7 @@ username, and password.
gitlab_rails['db_password'] = "mypassword"
gitlab_rails['db_host'] = "<rds-endpoint>"
Next we only need to configure the Redis section by adding the host and
Next, we only need to configure the Redis section by adding the host and
uncommenting the port.
......@@ -285,7 +285,7 @@ to make the EFS integration easier to manage.
gitlab_rails['redis_host'] = "<redis-endpoint>"
gitlab_rails['redis_port'] = 6379
Finally run reconfigure, you might find it useful to run a check and
Finally, run reconfigure. You might find it useful to run a check and
a service status to make sure everything has been set up correctly.
sudo gitlab-ctl reconfigure
......@@ -321,10 +321,10 @@ The Load Balancer Health will allow us to indicate where to ping and what
makes up a healthy or unhealthy instance.
We won't add the instance on the next session because we'll destroy it
momentarily as we'll be using the image we where creating. We will keep
momentarily as we'll be using the image we were creating. We will keep
the Enable Cross-Zone and Enable Connection Draining active.
After we finish creating the Load Balancer we can re visit our Security
After we finish creating the Load Balancer we can revisit our Security
Groups to improve access only through the ELB and any other requirement
you might have.
......@@ -363,7 +363,7 @@ After this is launched we are able to start creating our Auto Scaling
Group. Start by giving it a name and assigning it our VPC and private
subnets. We also want to always start with two instances and if you
scroll down to Advanced Details we can choose to receive traffic from ELBs.
Lets enable that option and select our ELB. We also want to use the ELB's
Let's enable that option and select our ELB. We also want to use the ELB's
health check.
![Auto scaling](img/auto-scaling-det.png)
......@@ -388,9 +388,9 @@ we where aiming for.
After you're done with the policies section have some fun trying to break
instances. You should be able to see how the Auto Scaling Group and the
EC2 screen start bringing them up again.
EC2 screen starts bringing them up again.
High Availability is a very big area, we went mostly through scaling and
High Availability is a vast area, we went mostly through scaling and
some redundancy options but it might also imply Geographic replication.
There is a lot of ground yet to cover so have a read through these other
resources and feel free to open an issue to request additional material.
......
......@@ -10,8 +10,8 @@ Historically, runbooks took the form of a decision tree or a detailed
step-by-step guide depending on the condition or system.
Modern implementations have introduced the concept of an "executable
runbooks", where along with a well define process, operators can execute
code blocks or database queries against a given environment.
runbooks", where, along with a well-defined process, operators can execute
pre-written code blocks or database queries against a given environment.
## Nurtch Executable Runbooks
......@@ -45,5 +45,93 @@ To create an executable runbook, you will need:
Nurtch is the company behind the [Rubix library](https://github.com/Nurtch/rubix). Rubix is
an open-source python library that makes it easy to perform common DevOps tasks inside Jupyter Notebooks.
Tasks such as plotting Cloudwatch metrics and rolling your ECS/Kubernetes app are simplified
down to a couple of lines of code. Check the [Nurtch Documentation](http://docs.nurtch.com/en/latest)
down to a couple of lines of code. See the [Nurtch Documentation](http://docs.nurtch.com/en/latest)
for more information.
## Configure an executable runbook with GitLab
Follow this step-by-step guide to configure an executable runbook in GitLab using
the components outlined above and the preloaded demo runbook.
### 1. Add a Kubernetes cluster
Follow the steps outlined in [Adding and creating a new GKE cluster via GitLab](https://docs.gitlab.com/ee/user/project/clusters/#adding-and-creating-a-new-gke-cluster-via-gitlab)
to add a Kubernetes cluster to your project.
### 2. Install Helm Tiller, Ingress, and JupyterHub
Once the cluster has been provisioned in GKE, click the **Install** button next to the **Helm Tiller** app.
![install helm](img/helm-install.png)
Once Tiller has been installed successfully, click the **Install** button next to the **Ingress** app.
![install ingress](img/ingress-install.png)
Once Ingress has been installed successfully, click the **Install** button next to the **JupyterHub** app.
![install jupyterhub](img/jupyterhub-install.png)
### 3. Login to JupyterHub and start the server
Once JupyterHub has been installed successfully, navigate to the displayed **Jupyter Hostname** URL and click
**Sign in with GitLab**. Authentication is automatically enabled for any user of the GitLab instance via OAuth2. This
will redirect to GitLab in order to authorize JupyterHub to use your GitLab account. Click **Authorize**.
![authorize jupyter](img/authorize-jupyter.png)
Once the application has been authorized you will taken back to the JupyterHub application. Click **Start My Server**.
The server will take a couple of seconds to start.
### 4. Configure access
In order for the runbook to access your GitLab project, you will need to enter a
[GitLab Access Token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html)
as well as your Project ID in the **Setup** section of the demo runbook.
Double-click the **DevOps-Runbook-Demo** folder located on the left panel.
![demo runbook](img/demo-runbook.png)
Double-click the "Nurtch-DevOps-Demo.ipynb" runbook.
![sample runbook](img/sample-runbook.png)
The contents on the runbook will be displayed on the right side of the screen. Under the "Setup" section, you will find
entries for both your `PRIVATE_TOKEN` and your `PROJECT_ID`. Enter both these values, conserving the single quotes as follows:
```sql
PRIVATE_TOKEN = 'n671WNGecHugsdEDPsyo'
PROJECT_ID = '1234567'
```
Update the `VARIABLE_NAME` on the last line of this section to match the name of the variable you are using for your
access token. In this example our variable name is `PRIVATE_TOKEN`.
```sql
VARIABLE_VALUE = project.variables.get('PRIVATE_TOKEN').value
```
### 5. Configure an operation
For this example we'll use the "**Run SQL queries in Notebook**" section in the sample runbook to query
a postgres database. The first 4 lines of the section define the variables that are required for this query to function.
```sql
%env DB_USER={project.variables.get('DB_USER').value}
%env DB_PASSWORD={project.variables.get('DB_PASSWORD').value}
%env DB_ENDPOINT={project.variables.get('DB_ENDPOINT').value}
%env DB_NAME={project.variables.get('DB_NAME').value}
```
Create the matching variables in your project's **Settings >> CI/CD >> Variables**
![gitlab variables](img/gitlab-variables.png)
Back in Jupyter, click the "Run SQL queries in Notebook" heading and the click *Run*. The results will be
displayed in-line as follows:
![postgres query](img/postgres-query.png)
You can try other operations such as running shell scripts or interacting with a Kubernetes cluster. Visit the
[Nurtch Documentation](http://docs.nurtch.com/) for more information.
\ No newline at end of file
......@@ -231,9 +231,10 @@ have been marked as a **Work In Progress**.
## Merge request diff file navigation
The diff view has a file tree for file navigation. As you scroll through
diffs with a large number of files, you can easily jump to any changed file
using the file tree.
When reviewing changes in the **Changes** tab the diff can be navigated using
the file tree or file list. As you scroll through large diffs with many
changes, you can quickly jump to any changed file using the file tree or file
list.
![Merge request diff file navigation](img/merge_request_diff_file_navigation.png)
......@@ -258,6 +259,34 @@ all your changes will be available to preview by anyone with the Review Apps lin
[Read more about Review Apps.](../../../ci/review_apps/index.md)
## Pipeline status in merge requests
If you've set up [GitLab CI/CD](../../../ci/README.md) in your project,
you will be able to see:
- Both pre and post-merge pipelines and the environment information if any.
- Which deployments are in progress.
If there's an [environment](../../../ci/environments.md) and the application is
successfully deployed to it, the deployed environment and the link to the
Review App will be shown as well.
### Post-merge pipeline status
When a merge request is merged, you can see the post-merge pipeline status of
the branch the merge request was merged into. For example, when a merge request
is merged into the master branch and then triggers a deployment to the staging
environment.
Deployments that are ongoing will be shown, as well as the deploying/deployed state
for environments. If it's the first time the branch is deployed, the link
will return a `404` error until done. During the deployment, the stop button will
be disabled. If the pipeline fails to deploy, the deployment info will be hidden.
![Merge request pipeline](img/merge_request_pipeline.png)
For more information, [read about pipelines](../../../ci/pipelines.md).
## Bulk editing merge requests
Find out about [bulk editing merge requests](../../project/bulk_editing.md).
......
......@@ -53,6 +53,7 @@ discussions, and descriptions:
| `/target_branch <Local branch Name>` | Set target branch | | ✓ |
| `/wip` | Toggle the Work In Progress status | | ✓ |
| `/merge` | Merge (when pipeline succeeds) | | ✓ |
| `/create_merge_request <branch name>` | Create a new merge request starting from the current issue | ✓ | |
## Quick actions for commit messages
......
......@@ -28,7 +28,7 @@ module Gitlab
end
def extract_authn_context(document)
REXML::XPath.first(document, "//saml:AuthnStatement/saml:AuthnContext/saml:AuthnContextClassRef/text()").to_s
REXML::XPath.first(document, "//*[name()='saml:AuthnStatement' or name()='saml2:AuthnStatement']/*[name()='saml:AuthnContext' or name()='saml2:AuthnContext']/*[name()='saml:AuthnContextClassRef' or name()='saml2:AuthnContextClassRef']/text()").to_s
end
end
end
......
......@@ -42,7 +42,7 @@ module Gitlab
end
def self.cache_key_for_project(project)
"#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:projects/#{project.id}/pipeline_status/#{project.commit&.sha}"
"#{Gitlab::Redis::Cache::CACHE_NAMESPACE}:project:#{project.id}:pipeline_status:#{project.commit&.sha}"
end
def self.update_for_pipeline(pipeline)
......
# frozen_string_literal: true
module Gitlab
module HookData
class BaseBuilder
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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