Merge branch 'master' into fl-mr-widget-1
* master: (38 commits) Added more tests and corrected typos Ban Rugged from Repository Improve doc/development/automatic_ce_ee_merge.md fixed infinite loop crashing tests Converted todos.js to axios Converted usage_ping.js to use axios Converted pager.js to axios Converted notifications_form.js to axios Converted notes.js to axios Converted branch_graph.js to axios Converted mini_pipeline_graph_dropdown.js to axios Include subgroup issuables on the group page Remove grpc and google-protobuf platform-specific version markers in Gemfile.lock Replace $.ajax in find file with axios Replace $.ajax in activity calendar with axios Remove namespaced internationalization import Fix subgroup creation docs Fix a JSON schema that doesn't include enough fields Make user/author use project.creator in most factories Enable RuboCop Style/RegexpLiteral ...
This commit is contained in:
commit
e03c85b981
|
@ -704,7 +704,9 @@ Style/RedundantSelf:
|
|||
# Configuration parameters: EnforcedStyle, AllowInnerSlashes.
|
||||
# SupportedStyles: slashes, percent_r, mixed
|
||||
Style/RegexpLiteral:
|
||||
Enabled: false
|
||||
Enabled: true
|
||||
EnforcedStyle: mixed
|
||||
AllowInnerSlashes: false
|
||||
|
||||
# Offense count: 36
|
||||
# Cop supports --auto-correct.
|
||||
|
|
|
@ -340,7 +340,7 @@ GEM
|
|||
mime-types (~> 3.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
google-protobuf (3.4.1.1)
|
||||
google-protobuf (3.5.1.1)
|
||||
googleapis-common-protos-types (1.0.1)
|
||||
google-protobuf (~> 3.0)
|
||||
googleauth (0.5.3)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
import _ from 'underscore';
|
||||
import Cookies from 'js-cookie';
|
||||
import { s__ } from './locale';
|
||||
import { __ } from './locale';
|
||||
import { isInIssuePage, updateTooltipTitle } from './lib/utils/common_utils';
|
||||
import flash from './flash';
|
||||
import axios from './lib/utils/axios_utils';
|
||||
|
@ -451,7 +451,7 @@ class AwardsHandler {
|
|||
callback();
|
||||
}
|
||||
})
|
||||
.catch(() => flash(s__('Something went wrong on our end.')));
|
||||
.catch(() => flash(__('Something went wrong on our end.')));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { getLocationHash } from './url_utility';
|
||||
import axios from './axios_utils';
|
||||
|
||||
export const getPagePath = (index = 0) => $('body').attr('data-page').split(':')[index];
|
||||
|
||||
|
@ -382,22 +383,16 @@ export const resetFavicon = () => {
|
|||
}
|
||||
};
|
||||
|
||||
export const setCiStatusFavicon = (pageUrl) => {
|
||||
$.ajax({
|
||||
url: pageUrl,
|
||||
dataType: 'json',
|
||||
success: (data) => {
|
||||
export const setCiStatusFavicon = pageUrl =>
|
||||
axios.get(pageUrl)
|
||||
.then(({ data }) => {
|
||||
if (data && data.favicon) {
|
||||
setFavicon(data.favicon);
|
||||
} else {
|
||||
resetFavicon();
|
||||
}
|
||||
},
|
||||
error: () => {
|
||||
resetFavicon();
|
||||
},
|
||||
});
|
||||
};
|
||||
})
|
||||
.catch(resetFavicon);
|
||||
|
||||
export const spriteIcon = (icon, className = '') => {
|
||||
const classAttribute = className.length > 0 ? `class="${className}"` : '';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable no-new */
|
||||
import Flash from './flash';
|
||||
import flash from './flash';
|
||||
import axios from './lib/utils/axios_utils';
|
||||
|
||||
/**
|
||||
* In each pipelines table we have a mini pipeline graph for each pipeline.
|
||||
|
@ -78,26 +79,21 @@ export default class MiniPipelineGraph {
|
|||
const button = e.relatedTarget;
|
||||
const endpoint = button.dataset.stageEndpoint;
|
||||
|
||||
return $.ajax({
|
||||
dataType: 'json',
|
||||
type: 'GET',
|
||||
url: endpoint,
|
||||
beforeSend: () => {
|
||||
this.renderBuildsList(button, '');
|
||||
this.toggleLoading(button);
|
||||
},
|
||||
success: (data) => {
|
||||
|
||||
axios.get(endpoint)
|
||||
.then(({ data }) => {
|
||||
this.toggleLoading(button);
|
||||
this.renderBuildsList(button, data.html);
|
||||
this.stopDropdownClickPropagation();
|
||||
},
|
||||
error: () => {
|
||||
})
|
||||
.catch(() => {
|
||||
this.toggleLoading(button);
|
||||
if ($(button).parent().hasClass('open')) {
|
||||
$(button).dropdown('toggle');
|
||||
}
|
||||
new Flash('An error occurred while fetching the builds.', 'alert');
|
||||
},
|
||||
flash('An error occurred while fetching the builds.', 'alert');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, no-var, wrap-iife, quotes, comma-dangle, one-var, one-var-declaration-per-line, no-mixed-operators, no-loop-func, no-floating-decimal, consistent-return, no-unused-vars, prefer-template, prefer-arrow-callback, camelcase, max-len */
|
||||
|
||||
import { __ } from '../locale';
|
||||
import axios from '../lib/utils/axios_utils';
|
||||
import flash from '../flash';
|
||||
import Raphael from './raphael';
|
||||
|
||||
export default (function() {
|
||||
|
@ -26,16 +29,13 @@ export default (function() {
|
|||
}
|
||||
|
||||
BranchGraph.prototype.load = function() {
|
||||
return $.ajax({
|
||||
url: this.options.url,
|
||||
method: "get",
|
||||
dataType: "json",
|
||||
success: $.proxy(function(data) {
|
||||
axios.get(this.options.url)
|
||||
.then(({ data }) => {
|
||||
$(".loading", this.element).hide();
|
||||
this.prepareData(data.days, data.commits);
|
||||
return this.buildGraph();
|
||||
}, this)
|
||||
});
|
||||
this.buildGraph();
|
||||
})
|
||||
.catch(() => __('Error fetching network graph.'));
|
||||
};
|
||||
|
||||
BranchGraph.prototype.prepareData = function(days, commits) {
|
||||
|
|
|
@ -16,6 +16,7 @@ import Autosize from 'autosize';
|
|||
import 'vendor/jquery.caret'; // required by jquery.atwho
|
||||
import 'vendor/jquery.atwho';
|
||||
import AjaxCache from '~/lib/utils/ajax_cache';
|
||||
import axios from './lib/utils/axios_utils';
|
||||
import { getLocationHash } from './lib/utils/url_utility';
|
||||
import Flash from './flash';
|
||||
import CommentTypeToggle from './comment_type_toggle';
|
||||
|
@ -252,26 +253,20 @@ export default class Notes {
|
|||
return;
|
||||
}
|
||||
this.refreshing = true;
|
||||
return $.ajax({
|
||||
url: this.notes_url,
|
||||
headers: { 'X-Last-Fetched-At': this.last_fetched_at },
|
||||
dataType: 'json',
|
||||
success: (function(_this) {
|
||||
return function(data) {
|
||||
var notes;
|
||||
notes = data.notes;
|
||||
_this.last_fetched_at = data.last_fetched_at;
|
||||
_this.setPollingInterval(data.notes.length);
|
||||
return $.each(notes, function(i, note) {
|
||||
_this.renderNote(note);
|
||||
axios.get(this.notes_url, {
|
||||
headers: {
|
||||
'X-Last-Fetched-At': this.last_fetched_at,
|
||||
},
|
||||
}).then(({ data }) => {
|
||||
const notes = data.notes;
|
||||
this.last_fetched_at = data.last_fetched_at;
|
||||
this.setPollingInterval(data.notes.length);
|
||||
$.each(notes, (i, note) => this.renderNote(note));
|
||||
|
||||
this.refreshing = false;
|
||||
}).catch(() => {
|
||||
this.refreshing = false;
|
||||
});
|
||||
};
|
||||
})(this)
|
||||
}).always((function(_this) {
|
||||
return function() {
|
||||
return _this.refreshing = false;
|
||||
};
|
||||
})(this));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
import { __ } from './locale';
|
||||
import axios from './lib/utils/axios_utils';
|
||||
import flash from './flash';
|
||||
|
||||
export default class NotificationsForm {
|
||||
constructor() {
|
||||
this.toggleCheckbox = this.toggleCheckbox.bind(this);
|
||||
|
@ -27,15 +31,10 @@ export default class NotificationsForm {
|
|||
saveEvent($checkbox, $parent) {
|
||||
const form = $parent.parents('form:first');
|
||||
|
||||
return $.ajax({
|
||||
url: form.attr('action'),
|
||||
method: form.attr('method'),
|
||||
dataType: 'json',
|
||||
data: form.serialize(),
|
||||
beforeSend: () => {
|
||||
this.showCheckboxLoadingSpinner($parent);
|
||||
},
|
||||
}).done((data) => {
|
||||
|
||||
axios[form.attr('method')](form.attr('action'), form.serialize())
|
||||
.then(({ data }) => {
|
||||
$checkbox.enable();
|
||||
if (data.saved) {
|
||||
$parent.find('.custom-notification-event-loading').toggleClass('fa-spin fa-spinner fa-check is-done');
|
||||
|
@ -45,6 +44,7 @@ export default class NotificationsForm {
|
|||
.toggleClass('fa-spin fa-spinner fa-check is-done');
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(() => flash(__('There was an error saving your notification settings.')));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { getParameterByName } from '~/lib/utils/common_utils';
|
||||
import axios from './lib/utils/axios_utils';
|
||||
import { removeParams } from './lib/utils/url_utility';
|
||||
|
||||
const ENDLESS_SCROLL_BOTTOM_PX = 400;
|
||||
|
@ -22,13 +23,12 @@ export default {
|
|||
|
||||
getOld() {
|
||||
this.loading.show();
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: this.url,
|
||||
data: `limit=${this.limit}&offset=${this.offset}`,
|
||||
dataType: 'json',
|
||||
error: () => this.loading.hide(),
|
||||
success: (data) => {
|
||||
axios.get(this.url, {
|
||||
params: {
|
||||
limit: this.limit,
|
||||
offset: this.offset,
|
||||
},
|
||||
}).then(({ data }) => {
|
||||
this.append(data.count, this.prepareData(data.html));
|
||||
this.callback();
|
||||
|
||||
|
@ -38,8 +38,7 @@ export default {
|
|||
} else {
|
||||
this.loading.hide();
|
||||
}
|
||||
},
|
||||
});
|
||||
}).catch(() => this.loading.hide());
|
||||
},
|
||||
|
||||
append(count, html) {
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
export default function UsagePing() {
|
||||
const usageDataUrl = $('.usage-data').data('endpoint');
|
||||
import axios from '../../../lib/utils/axios_utils';
|
||||
import { __ } from '../../../locale';
|
||||
import flash from '../../../flash';
|
||||
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: usageDataUrl,
|
||||
dataType: 'html',
|
||||
success(html) {
|
||||
$('.usage-data').html(html);
|
||||
},
|
||||
});
|
||||
export default function UsagePing() {
|
||||
const el = document.querySelector('.usage-data');
|
||||
|
||||
axios.get(el.dataset.endpoint, {
|
||||
responseType: 'text',
|
||||
}).then(({ data }) => {
|
||||
el.innerHTML = data;
|
||||
}).catch(() => flash(__('Error fetching usage ping data.')));
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
import UsersSelect from '~/users_select';
|
||||
import { isMetaClick } from '~/lib/utils/common_utils';
|
||||
import { __ } from '../../../../locale';
|
||||
import flash from '../../../../flash';
|
||||
import axios from '../../../../lib/utils/axios_utils';
|
||||
|
||||
export default class Todos {
|
||||
constructor() {
|
||||
|
@ -59,18 +62,12 @@ export default class Todos {
|
|||
const target = e.target;
|
||||
target.setAttribute('disabled', true);
|
||||
target.classList.add('disabled');
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: target.dataset.href,
|
||||
dataType: 'json',
|
||||
data: {
|
||||
'_method': target.dataset.method,
|
||||
},
|
||||
success: (data) => {
|
||||
|
||||
axios[target.dataset.method](target.dataset.href)
|
||||
.then(({ data }) => {
|
||||
this.updateRowState(target);
|
||||
return this.updateBadges(data);
|
||||
},
|
||||
});
|
||||
this.updateBadges(data);
|
||||
}).catch(() => flash(__('Error updating todo status.')));
|
||||
}
|
||||
|
||||
updateRowState(target) {
|
||||
|
@ -98,19 +95,15 @@ export default class Todos {
|
|||
e.preventDefault();
|
||||
|
||||
const target = e.currentTarget;
|
||||
const requestData = { '_method': target.dataset.method, ids: this.todo_ids };
|
||||
target.setAttribute('disabled', true);
|
||||
target.classList.add('disabled');
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: target.dataset.href,
|
||||
dataType: 'json',
|
||||
data: requestData,
|
||||
success: (data) => {
|
||||
|
||||
axios[target.dataset.method](target.dataset.href, {
|
||||
ids: this.todo_ids,
|
||||
}).then(({ data }) => {
|
||||
this.updateAllState(target, data);
|
||||
return this.updateBadges(data);
|
||||
},
|
||||
});
|
||||
this.updateBadges(data);
|
||||
}).catch(() => flash(__('Error updating status for all todos.')));
|
||||
}
|
||||
|
||||
updateAllState(target, data) {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import Vue from 'vue';
|
||||
import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
|
||||
import TreeView from '../../../../tree';
|
||||
import ShortcutsNavigation from '../../../../shortcuts_navigation';
|
||||
import BlobViewer from '../../../../blob/viewer';
|
||||
|
@ -11,5 +13,25 @@ export default () => {
|
|||
new NewCommitForm($('.js-create-dir-form')); // eslint-disable-line no-new
|
||||
$('#tree-slider').waitForImages(() =>
|
||||
ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath));
|
||||
|
||||
const commitPipelineStatusEl = document.getElementById('commit-pipeline-status');
|
||||
const statusLink = document.querySelector('.commit-actions .ci-status-link');
|
||||
if (statusLink != null) {
|
||||
statusLink.remove();
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el: commitPipelineStatusEl,
|
||||
components: {
|
||||
commitPipelineStatus,
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement('commit-pipeline-status', {
|
||||
props: {
|
||||
endpoint: commitPipelineStatusEl.dataset.endpoint,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, consistent-return, one-var, one-var-declaration-per-line, no-cond-assign, max-len, object-shorthand, no-param-reassign, comma-dangle, prefer-template, no-unused-vars, no-return-assign */
|
||||
|
||||
import fuzzaldrinPlus from 'fuzzaldrin-plus';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import flash from '~/flash';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
// highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> )
|
||||
const highlighter = function(element, text, matches) {
|
||||
|
@ -72,19 +75,14 @@ export default class ProjectFindFile {
|
|||
|
||||
// files pathes load
|
||||
load(url) {
|
||||
return $.ajax({
|
||||
url: url,
|
||||
method: "get",
|
||||
dataType: "json",
|
||||
success: (function(_this) {
|
||||
return function(data) {
|
||||
_this.element.find(".loading").hide();
|
||||
_this.filePaths = data;
|
||||
_this.findFile();
|
||||
return _this.element.find(".files-slider tr.tree-item").eq(0).addClass("selected").focus();
|
||||
};
|
||||
})(this)
|
||||
});
|
||||
axios.get(url)
|
||||
.then(({ data }) => {
|
||||
this.element.find('.loading').hide();
|
||||
this.filePaths = data;
|
||||
this.findFile();
|
||||
this.element.find('.files-slider tr.tree-item').eq(0).addClass('selected').focus();
|
||||
})
|
||||
.catch(() => flash(__('An error occurred while loading filenames')));
|
||||
}
|
||||
|
||||
// render result
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
<script>
|
||||
import Visibility from 'visibilityjs';
|
||||
import ciIcon from '~/vue_shared/components/ci_icon.vue';
|
||||
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
|
||||
import Poll from '~/lib/utils/poll';
|
||||
import Flash from '~/flash';
|
||||
import { s__, sprintf } from '~/locale';
|
||||
import tooltip from '~/vue_shared/directives/tooltip';
|
||||
import CommitPipelineService from '../services/commit_pipeline_service';
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
components: {
|
||||
ciIcon,
|
||||
loadingIcon,
|
||||
},
|
||||
props: {
|
||||
endpoint: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
/* This prop can be used to replace some of the `render_commit_status`
|
||||
used across GitLab, this way we could use this vue component and add a
|
||||
realtime status where it makes sense
|
||||
realtime: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
}, */
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ciStatus: {},
|
||||
isLoading: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
statusTitle() {
|
||||
return sprintf(s__('Commits|Commit: %{commitText}'), { commitText: this.ciStatus.text });
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.service = new CommitPipelineService(this.endpoint);
|
||||
this.initPolling();
|
||||
},
|
||||
methods: {
|
||||
successCallback(res) {
|
||||
const pipelines = res.data.pipelines;
|
||||
if (pipelines.length > 0) {
|
||||
// The pipeline entity always keeps the latest pipeline info on the `details.status`
|
||||
this.ciStatus = pipelines[0].details.status;
|
||||
}
|
||||
this.isLoading = false;
|
||||
},
|
||||
errorCallback() {
|
||||
this.ciStatus = {
|
||||
text: 'not found',
|
||||
icon: 'status_notfound',
|
||||
group: 'notfound',
|
||||
};
|
||||
this.isLoading = false;
|
||||
Flash(s__('Something went wrong on our end'));
|
||||
},
|
||||
initPolling() {
|
||||
this.poll = new Poll({
|
||||
resource: this.service,
|
||||
method: 'fetchData',
|
||||
successCallback: response => this.successCallback(response),
|
||||
errorCallback: this.errorCallback,
|
||||
});
|
||||
|
||||
if (!Visibility.hidden()) {
|
||||
this.isLoading = true;
|
||||
this.poll.makeRequest();
|
||||
} else {
|
||||
this.fetchPipelineCommitData();
|
||||
}
|
||||
|
||||
Visibility.change(() => {
|
||||
if (!Visibility.hidden()) {
|
||||
this.poll.restart();
|
||||
} else {
|
||||
this.poll.stop();
|
||||
}
|
||||
});
|
||||
},
|
||||
fetchPipelineCommitData() {
|
||||
this.service.fetchData()
|
||||
.then(this.successCallback)
|
||||
.catch(this.errorCallback);
|
||||
},
|
||||
},
|
||||
destroy() {
|
||||
this.poll.stop();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<loading-icon
|
||||
label="Loading pipeline status"
|
||||
size="3"
|
||||
v-if="isLoading"
|
||||
/>
|
||||
<a
|
||||
v-else
|
||||
:href="ciStatus.details_path"
|
||||
>
|
||||
<ci-icon
|
||||
v-tooltip
|
||||
:title="statusTitle"
|
||||
:aria-label="statusTitle"
|
||||
data-container="body"
|
||||
:status="ciStatus"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,11 @@
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
export default class CommitPipelineService {
|
||||
constructor(endpoint) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
fetchData() {
|
||||
return axios.get(this.endpoint);
|
||||
}
|
||||
}
|
|
@ -7,6 +7,11 @@
|
|||
//
|
||||
// <code class="js-render-math"></div>
|
||||
//
|
||||
|
||||
import { __ } from './locale';
|
||||
import axios from './lib/utils/axios_utils';
|
||||
import flash from './flash';
|
||||
|
||||
// Only load once
|
||||
let katexLoaded = false;
|
||||
|
||||
|
@ -33,19 +38,26 @@ export default function renderMath($els) {
|
|||
if (katexLoaded) {
|
||||
renderWithKaTeX($els);
|
||||
} else {
|
||||
$.get(gon.katex_css_url, () => {
|
||||
axios.get(gon.katex_css_url)
|
||||
.then(() => {
|
||||
const css = $('<link>', {
|
||||
rel: 'stylesheet',
|
||||
type: 'text/css',
|
||||
href: gon.katex_css_url,
|
||||
});
|
||||
css.appendTo('head');
|
||||
|
||||
// Load KaTeX js
|
||||
$.getScript(gon.katex_js_url, () => {
|
||||
})
|
||||
.then(() => axios.get(gon.katex_js_url, {
|
||||
responseType: 'text',
|
||||
}))
|
||||
.then(({ data }) => {
|
||||
// Add katex js to our document
|
||||
$.globalEval(data);
|
||||
})
|
||||
.then(() => {
|
||||
katexLoaded = true;
|
||||
renderWithKaTeX($els); // Run KaTeX
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(() => flash(__('An error occurred while rendering KaTeX')));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import _ from 'underscore';
|
||||
import { scaleLinear, scaleThreshold } from 'd3-scale';
|
||||
import { select } from 'd3-selection';
|
||||
import { getDayName, getDayDifference } from '../lib/utils/datetime_utility';
|
||||
import { getDayName, getDayDifference } from '~/lib/utils/datetime_utility';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import flash from '~/flash';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
const d3 = { select, scaleLinear, scaleThreshold };
|
||||
|
||||
|
@ -221,14 +224,16 @@ export default class ActivityCalendar {
|
|||
this.currentSelectedDate.getDate(),
|
||||
].join('-');
|
||||
|
||||
$.ajax({
|
||||
url: this.calendarActivitiesPath,
|
||||
data: { date },
|
||||
cache: false,
|
||||
dataType: 'html',
|
||||
beforeSend: () => $('.user-calendar-activities').html(LOADING_HTML),
|
||||
success: data => $('.user-calendar-activities').html(data),
|
||||
});
|
||||
$('.user-calendar-activities').html(LOADING_HTML);
|
||||
|
||||
axios.get(this.calendarActivitiesPath, {
|
||||
params: {
|
||||
date,
|
||||
},
|
||||
responseType: 'text',
|
||||
})
|
||||
.then(({ data }) => $('.user-calendar-activities').html(data))
|
||||
.catch(() => flash(__('An error occurred while retrieving calendar activity')));
|
||||
} else {
|
||||
this.currentSelectedDate = '';
|
||||
$('.user-calendar-activities').html('');
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
export default {
|
||||
name: 'MRWidgetRelatedLinks',
|
||||
props: {
|
||||
relatedLinks: { type: Object, required: true },
|
||||
state: { type: String, required: false },
|
||||
},
|
||||
computed: {
|
||||
hasLinks() {
|
||||
const { closing, mentioned, assignToMe } = this.relatedLinks;
|
||||
return closing || mentioned || assignToMe;
|
||||
},
|
||||
closesText() {
|
||||
if (this.state === 'merged') {
|
||||
return 'Closed';
|
||||
}
|
||||
if (this.state === 'closed') {
|
||||
return 'Did not close';
|
||||
}
|
||||
return 'Closes';
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<section
|
||||
v-if="hasLinks"
|
||||
class="mr-info-list mr-links">
|
||||
<p v-if="relatedLinks.closing">
|
||||
{{closesText}} <span v-html="relatedLinks.closing"></span>
|
||||
</p>
|
||||
<p v-if="relatedLinks.mentioned">
|
||||
Mentions <span v-html="relatedLinks.mentioned"></span>
|
||||
</p>
|
||||
<p v-if="relatedLinks.assignToMe">
|
||||
<span v-html="relatedLinks.assignToMe"></span>
|
||||
</p>
|
||||
</section>
|
||||
`,
|
||||
};
|
|
@ -0,0 +1,43 @@
|
|||
<script>
|
||||
import { s__ } from '~/locale';
|
||||
|
||||
export default {
|
||||
name: 'MRWidgetRelatedLinks',
|
||||
props: {
|
||||
relatedLinks: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({}),
|
||||
},
|
||||
state: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
closesText() {
|
||||
if (this.state === 'merged') {
|
||||
return s__('mrWidget|Closed');
|
||||
}
|
||||
if (this.state === 'closed') {
|
||||
return s__('mrWidget|Did not close');
|
||||
}
|
||||
return s__('mrWidget|Closes');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<section class="mr-info-list mr-links">
|
||||
<p v-if="relatedLinks.closing">
|
||||
{{ closesText }} <span v-html="relatedLinks.closing"></span>
|
||||
</p>
|
||||
<p v-if="relatedLinks.mentioned">
|
||||
{{ s__("mrWidget|Mentions") }} <span v-html="relatedLinks.mentioned"></span>
|
||||
</p>
|
||||
<p v-if="relatedLinks.assignToMe">
|
||||
<span v-html="relatedLinks.assignToMe"></span>
|
||||
</p>
|
||||
</section>
|
||||
</template>
|
|
@ -15,7 +15,7 @@ export { default as WidgetHeader } from './components/mr_widget_header.vue';
|
|||
export { default as WidgetMergeHelp } from './components/mr_widget_merge_help.vue';
|
||||
export { default as WidgetPipeline } from './components/mr_widget_pipeline.vue';
|
||||
export { default as WidgetDeployment } from './components/mr_widget_deployment';
|
||||
export { default as WidgetRelatedLinks } from './components/mr_widget_related_links';
|
||||
export { default as WidgetRelatedLinks } from './components/mr_widget_related_links.vue';
|
||||
export { default as MergedState } from './components/states/mr_widget_merged.vue';
|
||||
export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge.vue';
|
||||
export { default as ClosedState } from './components/states/mr_widget_closed.vue';
|
||||
|
|
|
@ -257,7 +257,8 @@ export default {
|
|||
<mr-widget-related-links
|
||||
v-if="shouldRenderRelatedLinks"
|
||||
:state="mr.state"
|
||||
:related-links="mr.relatedLinks" />
|
||||
:related-links="mr.relatedLinks"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mr-widget-footer"
|
||||
|
|
|
@ -195,6 +195,18 @@
|
|||
.commit-actions {
|
||||
@media (min-width: $screen-sm-min) {
|
||||
font-size: 0;
|
||||
|
||||
div {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.fa-spinner {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.ci-status-link {
|
||||
|
@ -219,6 +231,11 @@
|
|||
font-size: 14px;
|
||||
font-weight: $gl-font-weight-bold;
|
||||
}
|
||||
|
||||
.ci-status-icon {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.commit,
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class Admin::GitalyServersController < Admin::ApplicationController
|
||||
def index
|
||||
@gitaly_servers = Gitaly::Server.all
|
||||
end
|
||||
end
|
|
@ -94,6 +94,7 @@ module IssuableCollections
|
|||
@filter_params[:project_id] = @project.id
|
||||
elsif @group
|
||||
@filter_params[:group_id] = @group.id
|
||||
@filter_params[:include_subgroups] = true
|
||||
else
|
||||
# TODO: this filter ignore issues/mr created in public or
|
||||
# internal repos where you are not a member. Enable this filter
|
||||
|
|
|
@ -118,10 +118,10 @@ class GroupsController < Groups::ApplicationController
|
|||
end
|
||||
|
||||
def group_params
|
||||
params.require(:group).permit(group_params_ce)
|
||||
params.require(:group).permit(group_params_attributes)
|
||||
end
|
||||
|
||||
def group_params_ce
|
||||
def group_params_attributes
|
||||
[
|
||||
:avatar,
|
||||
:description,
|
||||
|
@ -150,7 +150,6 @@ class GroupsController < Groups::ApplicationController
|
|||
@projects = GroupProjectsFinder.new(params: params, group: group, options: options, current_user: current_user)
|
||||
.execute
|
||||
.includes(:namespace)
|
||||
.page(params[:page])
|
||||
|
||||
@events = EventCollection
|
||||
.new(@projects, offset: params[:offset].to_i, filter: event_filter)
|
||||
|
|
|
@ -5,7 +5,7 @@ class HelpController < ApplicationController
|
|||
|
||||
# Taken from Jekyll
|
||||
# https://github.com/jekyll/jekyll/blob/3.5-stable/lib/jekyll/document.rb#L13
|
||||
YAML_FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m
|
||||
YAML_FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
|
||||
|
||||
def index
|
||||
# Remove YAML frontmatter so that it doesn't look weird
|
||||
|
|
|
@ -403,6 +403,6 @@ class ProjectsController < Projects::ApplicationController
|
|||
# to
|
||||
# localhost/group/project
|
||||
#
|
||||
redirect_to request.original_url.sub(/\.git\/?\Z/, '') if params[:format] == 'git'
|
||||
redirect_to request.original_url.sub(%r{\.git/?\Z}, '') if params[:format] == 'git'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -87,9 +87,18 @@ class GroupProjectsFinder < ProjectsFinder
|
|||
options.fetch(:only_shared, false)
|
||||
end
|
||||
|
||||
# subgroups are supported only for owned projects not for shared
|
||||
def include_subgroups?
|
||||
options.fetch(:include_subgroups, false)
|
||||
end
|
||||
|
||||
def owned_projects
|
||||
if include_subgroups?
|
||||
Project.where(namespace_id: group.self_and_descendants.select(:id))
|
||||
else
|
||||
group.projects
|
||||
end
|
||||
end
|
||||
|
||||
def shared_projects
|
||||
group.shared_projects
|
||||
|
|
|
@ -43,6 +43,7 @@ class IssuableFinder
|
|||
search
|
||||
sort
|
||||
state
|
||||
include_subgroups
|
||||
].freeze
|
||||
ARRAY_PARAMS = { label_name: [], iids: [], assignee_username: [] }.freeze
|
||||
|
||||
|
@ -148,7 +149,8 @@ class IssuableFinder
|
|||
if current_user && params[:authorized_only].presence && !current_user_related?
|
||||
current_user.authorized_projects
|
||||
elsif group
|
||||
GroupProjectsFinder.new(group: group, current_user: current_user).execute
|
||||
finder_options = { include_subgroups: params[:include_subgroups], only_owned: true }
|
||||
GroupProjectsFinder.new(group: group, current_user: current_user, options: finder_options).execute
|
||||
else
|
||||
ProjectsFinder.new(current_user: current_user, project_ids_relation: item_project_ids(items)).execute
|
||||
end
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
module SidekiqHelper
|
||||
SIDEKIQ_PS_REGEXP = /\A
|
||||
SIDEKIQ_PS_REGEXP = %r{\A
|
||||
(?<pid>\d+)\s+
|
||||
(?<cpu>[\d\.,]+)\s+
|
||||
(?<mem>[\d\.,]+)\s+
|
||||
(?<state>[DIEKNRSTVWXZNLpsl\+<>\/\d]+)\s+
|
||||
(?<state>[DIEKNRSTVWXZNLpsl\+<>/\d]+)\s+
|
||||
(?<start>.+?)\s+
|
||||
(?<command>(?:ruby\d+:\s+)?sidekiq.*\].*)
|
||||
\z/x
|
||||
\z}x
|
||||
|
||||
def parse_sidekiq_ps(line)
|
||||
match = line.strip.match(SIDEKIQ_PS_REGEXP)
|
||||
|
|
|
@ -11,7 +11,7 @@ module SubmoduleHelper
|
|||
url = File.join(Gitlab.config.gitlab.url, @project.full_path)
|
||||
end
|
||||
|
||||
if url =~ /([^\/:]+)\/([^\/]+(?:\.git)?)\Z/
|
||||
if url =~ %r{([^/:]+)/([^/]+(?:\.git)?)\Z}
|
||||
namespace, project = $1, $2
|
||||
gitlab_hosts = [Gitlab.config.gitlab.url,
|
||||
Gitlab.config.gitlab_shell.ssh_path_prefix]
|
||||
|
@ -23,7 +23,7 @@ module SubmoduleHelper
|
|||
end
|
||||
end
|
||||
|
||||
namespace.sub!(/\A\//, '')
|
||||
namespace.sub!(%r{\A/}, '')
|
||||
project.rstrip!
|
||||
project.sub!(/\.git\z/, '')
|
||||
|
||||
|
@ -47,11 +47,11 @@ module SubmoduleHelper
|
|||
protected
|
||||
|
||||
def github_dot_com_url?(url)
|
||||
url =~ /github\.com[\/:][^\/]+\/[^\/]+\Z/
|
||||
url =~ %r{github\.com[/:][^/]+/[^/]+\Z}
|
||||
end
|
||||
|
||||
def gitlab_dot_com_url?(url)
|
||||
url =~ /gitlab\.com[\/:][^\/]+\/[^\/]+\Z/
|
||||
url =~ %r{gitlab\.com[/:][^/]+/[^/]+\Z}
|
||||
end
|
||||
|
||||
def self_url?(url, namespace, project)
|
||||
|
@ -65,7 +65,7 @@ module SubmoduleHelper
|
|||
|
||||
def relative_self_url?(url)
|
||||
# (./)?(../repo.git) || (./)?(../../project/repo.git) )
|
||||
url =~ /\A((\.\/)?(\.\.\/))(?!(\.\.)|(.*\/)).*(\.git)?\z/ || url =~ /\A((\.\/)?(\.\.\/){2})(?!(\.\.))([^\/]*)\/(?!(\.\.)|(.*\/)).*(\.git)?\z/
|
||||
url =~ %r{\A((\./)?(\.\./))(?!(\.\.)|(.*/)).*(\.git)?\z} || url =~ %r{\A((\./)?(\.\./){2})(?!(\.\.))([^/]*)/(?!(\.\.)|(.*/)).*(\.git)?\z}
|
||||
end
|
||||
|
||||
def standard_links(host, namespace, project, commit)
|
||||
|
|
|
@ -110,7 +110,7 @@ module TreeHelper
|
|||
|
||||
# returns the relative path of the first subdir that doesn't have only one directory descendant
|
||||
def flatten_tree(root_path, tree)
|
||||
return tree.flat_path.sub(/\A#{root_path}\//, '') if tree.flat_path.present?
|
||||
return tree.flat_path.sub(%r{\A#{root_path}/}, '') if tree.flat_path.present?
|
||||
|
||||
subtree = Gitlab::Git::Tree.where(@repository, @commit.id, tree.path)
|
||||
if subtree.count == 1 && subtree.first.dir?
|
||||
|
|
|
@ -292,7 +292,7 @@ module Ci
|
|||
|
||||
def repo_url
|
||||
auth = "gitlab-ci-token:#{ensure_token!}@"
|
||||
project.http_url_to_repo.sub(/^https?:\/\//) do |prefix|
|
||||
project.http_url_to_repo.sub(%r{^https?://}) do |prefix|
|
||||
prefix + auth
|
||||
end
|
||||
end
|
||||
|
|
|
@ -141,7 +141,7 @@ class CommitStatus < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def group_name
|
||||
name.to_s.gsub(/\d+[\s:\/\\]+\d+\s*/, '').strip
|
||||
name.to_s.gsub(%r{\d+[\s:/\\]+\d+\s*}, '').strip
|
||||
end
|
||||
|
||||
def failed_but_allowed?
|
||||
|
|
|
@ -9,13 +9,13 @@ require 'task_list/filter'
|
|||
module Taskable
|
||||
COMPLETED = 'completed'.freeze
|
||||
INCOMPLETE = 'incomplete'.freeze
|
||||
ITEM_PATTERN = /
|
||||
ITEM_PATTERN = %r{
|
||||
^
|
||||
\s*(?:[-+*]|(?:\d+\.)) # list prefix required - task item has to be always in a list
|
||||
\s+ # whitespace prefix has to be always presented for a list item
|
||||
(\[\s\]|\[[xX]\]) # checkbox
|
||||
(\s.+) # followed by whitespace and some text.
|
||||
/x
|
||||
}x
|
||||
|
||||
def self.get_tasks(content)
|
||||
content.to_s.scan(ITEM_PATTERN).map do |checkbox, label|
|
||||
|
|
|
@ -115,7 +115,7 @@ class Environment < ActiveRecord::Base
|
|||
def formatted_external_url
|
||||
return nil unless external_url
|
||||
|
||||
external_url.gsub(/\A.*?:\/\//, '')
|
||||
external_url.gsub(%r{\A.*?://}, '')
|
||||
end
|
||||
|
||||
def stop_action?
|
||||
|
|
|
@ -234,7 +234,7 @@ class Project < ActiveRecord::Base
|
|||
validates :creator, presence: true, on: :create
|
||||
validates :description, length: { maximum: 2000 }, allow_blank: true
|
||||
validates :ci_config_path,
|
||||
format: { without: /(\.{2}|\A\/)/,
|
||||
format: { without: %r{(\.{2}|\A/)},
|
||||
message: 'cannot include leading slash or directory traversal.' },
|
||||
length: { maximum: 255 },
|
||||
allow_blank: true
|
||||
|
@ -1338,7 +1338,7 @@ class Project < ActiveRecord::Base
|
|||
host = "#{subdomain}.#{Settings.pages.host}".downcase
|
||||
|
||||
# The host in URL always needs to be downcased
|
||||
url = Gitlab.config.pages.url.sub(/^https?:\/\//) do |prefix|
|
||||
url = Gitlab.config.pages.url.sub(%r{^https?://}) do |prefix|
|
||||
"#{prefix}#{subdomain}."
|
||||
end.downcase
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ http://app.asana.com/-/account_api'
|
|||
# - fix/ed/es/ing
|
||||
# - close/s/d
|
||||
# - closing
|
||||
issue_finder = /(fix\w*|clos[ei]\w*+)?\W*(?:https:\/\/app\.asana\.com\/\d+\/\d+\/(\d+)|#(\d+))/i
|
||||
issue_finder = %r{(fix\w*|clos[ei]\w*+)?\W*(?:https://app\.asana\.com/\d+/\d+/(\d+)|#(\d+))}i
|
||||
|
||||
message.scan(issue_finder).each do |tuple|
|
||||
# tuple will be
|
||||
|
|
|
@ -10,9 +10,9 @@ class IssueTrackerService < Service
|
|||
# overriden patterns. See ReferenceRegexes::EXTERNAL_PATTERN
|
||||
def self.reference_pattern(only_long: false)
|
||||
if only_long
|
||||
%r{(\b[A-Z][A-Z0-9_]+-)(?<issue>\d+)}
|
||||
/(\b[A-Z][A-Z0-9_]+-)(?<issue>\d+)/
|
||||
else
|
||||
%r{(\b[A-Z][A-Z0-9_]+-|#{Issue.reference_prefix})(?<issue>\d+)}
|
||||
/(\b[A-Z][A-Z0-9_]+-|#{Issue.reference_prefix})(?<issue>\d+)/
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class JiraService < IssueTrackerService
|
|||
|
||||
# {PROJECT-KEY}-{NUMBER} Examples: JIRA-1, PROJECT-1
|
||||
def self.reference_pattern(only_long: true)
|
||||
@reference_pattern ||= %r{(?<issue>\b([A-Z][A-Z0-9_]+-)\d+)}
|
||||
@reference_pattern ||= /(?<issue>\b([A-Z][A-Z0-9_]+-)\d+)/
|
||||
end
|
||||
|
||||
def initialize_properties
|
||||
|
|
|
@ -852,7 +852,7 @@ class Repository
|
|||
@root_ref_sha ||= commit(root_ref).sha
|
||||
end
|
||||
|
||||
delegate :merged_branch_names, to: :raw_repository
|
||||
delegate :merged_branch_names, :can_be_merged?, to: :raw_repository
|
||||
|
||||
def merge_base(first_commit_id, second_commit_id)
|
||||
first_commit_id = commit(first_commit_id).try(:id) || first_commit_id
|
||||
|
@ -951,7 +951,7 @@ class Repository
|
|||
end
|
||||
|
||||
instance_variable_set(ivar, value)
|
||||
rescue Rugged::ReferenceError, Gitlab::Git::Repository::NoRepository
|
||||
rescue Gitlab::Git::Repository::NoRepository
|
||||
# Even if the above `#exists?` check passes these errors might still
|
||||
# occur (for example because of a non-existing HEAD). We want to
|
||||
# gracefully handle this and not cache anything
|
||||
|
|
|
@ -842,13 +842,13 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def full_website_url
|
||||
return "http://#{website_url}" if website_url !~ /\Ahttps?:\/\//
|
||||
return "http://#{website_url}" if website_url !~ %r{\Ahttps?://}
|
||||
|
||||
website_url
|
||||
end
|
||||
|
||||
def short_website_url
|
||||
website_url.sub(/\Ahttps?:\/\//, '')
|
||||
website_url.sub(%r{\Ahttps?://}, '')
|
||||
end
|
||||
|
||||
def all_ssh_keys
|
||||
|
|
|
@ -138,19 +138,11 @@
|
|||
GitLab API
|
||||
%span.pull-right
|
||||
= API::API::version
|
||||
%p
|
||||
Gitaly
|
||||
%span.pull-right
|
||||
= Gitlab::GitalyClient.expected_server_version
|
||||
- if Gitlab.config.pages.enabled
|
||||
%p
|
||||
GitLab Pages
|
||||
%span.pull-right
|
||||
= Gitlab::Pages::VERSION
|
||||
%p
|
||||
Git
|
||||
%span.pull-right
|
||||
= Gitlab::Git.version
|
||||
%p
|
||||
Ruby
|
||||
%span.pull-right
|
||||
|
@ -163,6 +155,8 @@
|
|||
= Gitlab::Database.adapter_name
|
||||
%span.pull-right
|
||||
= Gitlab::Database.version
|
||||
%p
|
||||
= link_to "Gitaly Servers", admin_gitaly_servers_path
|
||||
.row
|
||||
.col-md-4
|
||||
.info-well
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
- breadcrumb_title _("Gitaly Servers")
|
||||
|
||||
%h3.page-title= _("Gitaly Servers")
|
||||
%hr
|
||||
.gitaly_servers
|
||||
- if @gitaly_servers.any?
|
||||
.table-holder
|
||||
%table.table.responsive-table
|
||||
%thead.hidden-sm.hidden-xs
|
||||
%tr
|
||||
%th= _("Storage")
|
||||
%th= n_("Gitaly|Address")
|
||||
%th= _("Server version")
|
||||
%th= _("Git version")
|
||||
%th= _("Up to date")
|
||||
- @gitaly_servers.each do |server|
|
||||
%tr
|
||||
%td
|
||||
= server.storage
|
||||
%td
|
||||
= server.address
|
||||
%td
|
||||
= server.server_version
|
||||
%td
|
||||
= server.git_binary_version
|
||||
%td
|
||||
= boolean_to_icon(server.up_to_date?)
|
||||
- else
|
||||
.empty-state
|
||||
.text-center
|
||||
%h4= _("No connection could be made to a Gitaly Server, please check your logs!")
|
|
@ -51,6 +51,7 @@
|
|||
- if commit.status(ref)
|
||||
= render_commit_status(commit, ref: ref)
|
||||
|
||||
#commit-pipeline-status{ data: { endpoint: pipelines_project_commit_path(project, commit.id) } }
|
||||
= link_to commit.short_id, link, class: "commit-sha btn btn-transparent btn-link"
|
||||
= clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard"))
|
||||
= link_to_browse_code(project, commit)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Include subgroup issues and merge requests on the group page
|
||||
merge_request:
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add realtime ci status for the repository -> files view
|
||||
merge_request: 16523
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Enable RuboCop Style/RegexpLiteral
|
||||
merge_request: 16752
|
||||
author: Takuya Noguchi
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update minimum git version to 2.9.5
|
||||
merge_request: 16683
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix not all events being shown in group dashboard
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Remove N+1 queries with /projects/:project_id/{access_requests,members} API
|
||||
endpoints
|
||||
merge_request:
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add Gitaly Servers admin dashboard
|
||||
merge_request:
|
||||
author:
|
||||
type: added
|
|
@ -110,7 +110,7 @@ class Settings < Settingslogic
|
|||
url = "http://#{url}" unless url.start_with?('http')
|
||||
|
||||
# Get rid of the path so that we don't even have to encode it
|
||||
url_without_path = url.sub(%r{(https?://[^\/]+)/?.*}, '\1')
|
||||
url_without_path = url.sub(%r{(https?://[^/]+)/?.*}, '\1')
|
||||
|
||||
URI.parse(url_without_path).host
|
||||
end
|
||||
|
@ -469,10 +469,10 @@ end
|
|||
# repository_downloads_path value.
|
||||
#
|
||||
repositories_storages = Settings.repositories.storages.values
|
||||
repository_downloads_path = Settings.gitlab['repository_downloads_path'].to_s.gsub(/\/$/, '')
|
||||
repository_downloads_path = Settings.gitlab['repository_downloads_path'].to_s.gsub(%r{/$}, '')
|
||||
repository_downloads_full_path = File.expand_path(repository_downloads_path, Settings.gitlab['user_home'])
|
||||
|
||||
if repository_downloads_path.blank? || repositories_storages.any? { |rs| [repository_downloads_path, repository_downloads_full_path].include?(rs['path'].gsub(/\/$/, '')) }
|
||||
if repository_downloads_path.blank? || repositories_storages.any? { |rs| [repository_downloads_path, repository_downloads_full_path].include?(rs['path'].gsub(%r{/$}, '')) }
|
||||
Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive')
|
||||
end
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ elsif Gitlab::Database.mysql?
|
|||
def initialize_type_map(mapping)
|
||||
super mapping
|
||||
|
||||
mapping.register_type(%r(timestamp)i) do |sql_type|
|
||||
mapping.register_type(/timestamp/i) do |sql_type|
|
||||
precision = extract_precision(sql_type)
|
||||
ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MysqlDateTimeWithTimeZone.new(precision: precision)
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
namespace :admin do
|
||||
resources :users, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ } do
|
||||
resources :users, constraints: { id: %r{[a-zA-Z./0-9_\-]+} } do
|
||||
resources :keys, only: [:show, :destroy]
|
||||
resources :identities, except: [:show]
|
||||
resources :impersonation_tokens, only: [:index, :create] do
|
||||
|
@ -24,6 +24,8 @@ namespace :admin do
|
|||
resource :impersonation, only: :destroy
|
||||
|
||||
resources :abuse_reports, only: [:index, :destroy]
|
||||
resources :gitaly_servers, only: [:index]
|
||||
|
||||
resources :spam_logs, only: [:index, :destroy] do
|
||||
member do
|
||||
post :mark_as_ham
|
||||
|
|
|
@ -35,7 +35,7 @@ constraints(GroupUrlConstrainer.new) do
|
|||
post :toggle_subscription, on: :member
|
||||
end
|
||||
|
||||
resources :milestones, constraints: { id: /[^\/]+/ }, only: [:index, :show, :edit, :update, :new, :create] do
|
||||
resources :milestones, constraints: { id: %r{[^/]+} }, only: [:index, :show, :edit, :update, :new, :create] do
|
||||
member do
|
||||
get :merge_requests
|
||||
get :participants
|
||||
|
@ -52,7 +52,7 @@ constraints(GroupUrlConstrainer.new) do
|
|||
|
||||
resources :uploads, only: [:create] do
|
||||
collection do
|
||||
get ":secret/:filename", action: :show, as: :show, constraints: { filename: /[^\/]+/ }
|
||||
get ":secret/:filename", action: :show, as: :show, constraints: { filename: %r{[^/]+} }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,7 +40,7 @@ constraints(ProjectUrlConstrainer.new) do
|
|||
#
|
||||
# Templates
|
||||
#
|
||||
get '/templates/:template_type/:key' => 'templates#show', as: :template, constraints: { key: /[^\/]+/ }
|
||||
get '/templates/:template_type/:key' => 'templates#show', as: :template, constraints: { key: %r{[^/]+} }
|
||||
|
||||
resource :avatar, only: [:show, :destroy]
|
||||
resources :commit, only: [:show], constraints: { id: /\h{7,40}/ } do
|
||||
|
@ -55,7 +55,7 @@ constraints(ProjectUrlConstrainer.new) do
|
|||
end
|
||||
|
||||
resource :pages, only: [:show, :destroy] do
|
||||
resources :domains, only: [:show, :new, :create, :destroy], controller: 'pages_domains', constraints: { id: /[^\/]+/ }
|
||||
resources :domains, only: [:show, :new, :create, :destroy], controller: 'pages_domains', constraints: { id: %r{[^/]+} }
|
||||
end
|
||||
|
||||
resources :snippets, concerns: :awardable, constraints: { id: /\d+/ } do
|
||||
|
@ -65,7 +65,7 @@ constraints(ProjectUrlConstrainer.new) do
|
|||
end
|
||||
end
|
||||
|
||||
resources :services, constraints: { id: /[^\/]+/ }, only: [:index, :edit, :update] do
|
||||
resources :services, constraints: { id: %r{[^/]+} }, only: [:index, :edit, :update] do
|
||||
member do
|
||||
put :test
|
||||
end
|
||||
|
@ -346,7 +346,7 @@ constraints(ProjectUrlConstrainer.new) do
|
|||
end
|
||||
end
|
||||
|
||||
resources :project_members, except: [:show, :new, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ }, concerns: :access_requestable do
|
||||
resources :project_members, except: [:show, :new, :edit], constraints: { id: %r{[a-zA-Z./0-9_\-#%+]+} }, concerns: :access_requestable do
|
||||
collection do
|
||||
delete :leave
|
||||
|
||||
|
@ -379,7 +379,7 @@ constraints(ProjectUrlConstrainer.new) do
|
|||
|
||||
resources :uploads, only: [:create] do
|
||||
collection do
|
||||
get ":secret/:filename", action: :show, as: :show, constraints: { filename: /[^\/]+/ }
|
||||
get ":secret/:filename", action: :show, as: :show, constraints: { filename: %r{[^/]+} }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,17 +2,17 @@ scope path: :uploads do
|
|||
# Note attachments and User/Group/Project avatars
|
||||
get "-/system/:model/:mounted_as/:id/:filename",
|
||||
to: "uploads#show",
|
||||
constraints: { model: /note|user|group|project/, mounted_as: /avatar|attachment/, filename: /[^\/]+/ }
|
||||
constraints: { model: /note|user|group|project/, mounted_as: /avatar|attachment/, filename: %r{[^/]+} }
|
||||
|
||||
# show uploads for models, snippets (notes) available for now
|
||||
get '-/system/:model/:id/:secret/:filename',
|
||||
to: 'uploads#show',
|
||||
constraints: { model: /personal_snippet/, id: /\d+/, filename: /[^\/]+/ }
|
||||
constraints: { model: /personal_snippet/, id: /\d+/, filename: %r{[^/]+} }
|
||||
|
||||
# show temporary uploads
|
||||
get '-/system/temp/:secret/:filename',
|
||||
to: 'uploads#show',
|
||||
constraints: { filename: /[^\/]+/ }
|
||||
constraints: { filename: %r{[^/]+} }
|
||||
|
||||
# Appearance
|
||||
get "-/system/:model/:mounted_as/:id/:filename",
|
||||
|
@ -22,7 +22,7 @@ scope path: :uploads do
|
|||
# Project markdown uploads
|
||||
get ":namespace_id/:project_id/:secret/:filename",
|
||||
to: "projects/uploads#show",
|
||||
constraints: { namespace_id: /[a-zA-Z.0-9_\-]+/, project_id: /[a-zA-Z.0-9_\-]+/, filename: /[^\/]+/ }
|
||||
constraints: { namespace_id: /[a-zA-Z.0-9_\-]+/, project_id: /[a-zA-Z.0-9_\-]+/, filename: %r{[^/]+} }
|
||||
|
||||
# create uploads for models, snippets (notes) available for now
|
||||
post ':model',
|
||||
|
@ -34,4 +34,4 @@ end
|
|||
# Redirect old note attachments path to new uploads path.
|
||||
get "files/note/:id/:filename",
|
||||
to: redirect("uploads/note/attachment/%{id}/%{filename}"),
|
||||
constraints: { filename: /[^\/]+/ }
|
||||
constraints: { filename: %r{[^/]+} }
|
||||
|
|
|
@ -5,17 +5,32 @@ Enterprise Edition (look for the [`CE Upstream` merge requests]).
|
|||
|
||||
This merge is done automatically in a
|
||||
[scheduled pipeline](https://gitlab.com/gitlab-org/release-tools/-/jobs/43201679).
|
||||
If a merge is already in progress, the job [doesn't create a new one](https://gitlab.com/gitlab-org/release-tools/-/jobs/43157687).
|
||||
|
||||
**If you are pinged in a `CE Upstream` merge request to resolve a conflict,
|
||||
please resolve the conflict as soon as possible or ask someone else to do it!**
|
||||
## What to do if you are pinged in a `CE Upstream` merge request to resolve a conflict?
|
||||
|
||||
>**Note:**
|
||||
It's ok to resolve more conflicts than the one that you are asked to resolve. In
|
||||
that case, it's a good habit to ask for a double-check on your resolution by
|
||||
someone who is familiar with the code you touched.
|
||||
1. Please resolve the conflict as soon as possible or ask someone else to do it
|
||||
- It's ok to resolve more conflicts than the one that you are asked to resolve.
|
||||
In that case, it's a good habit to ask for a double-check on your resolution
|
||||
by someone who is familiar with the code you touched.
|
||||
1. Once you have resolved your conflicts, push to the branch (no force-push)
|
||||
1. Assign the merge request to the next person that has to resolve a conflict
|
||||
1. If all conflicts are resolved after your resolution is pushed, keep the merge
|
||||
request assigned to you: **you are now responsible for the merge request to be
|
||||
green**
|
||||
1. If you need any help, you can ping the current [release managers], or ask in
|
||||
the `#ce-to-ee` Slack channel
|
||||
|
||||
A few notes about the automatic CE->EE merge job:
|
||||
|
||||
- If a merge is already in progress, the job
|
||||
[doesn't create a new one](https://gitlab.com/gitlab-org/release-tools/-/jobs/43157687).
|
||||
- If there is nothing to merge (i.e. EE is up-to-date with CE), the job doesn't
|
||||
create a new one
|
||||
- The job posts messages to the `#ce-to-ee` Slack channel to inform what's the
|
||||
current CE->EE merge status (e.g. "A new MR has been created", "A MR is still pending")
|
||||
|
||||
[`CE Upstream` merge requests]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests?label_name%5B%5D=CE+upstream
|
||||
[release managers]: https://about.gitlab.com/release-managers/
|
||||
|
||||
## Always merge EE merge requests before their CE counterparts
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ Make sure you have the right version of Git installed
|
|||
# Install Git
|
||||
sudo apt-get install -y git-core
|
||||
|
||||
# Make sure Git is version 2.14.3 or higher
|
||||
# Make sure Git is version 2.9.5 or higher
|
||||
git --version
|
||||
|
||||
Is the system packaged Git too old? Remove it and compile from source.
|
||||
|
@ -93,9 +93,9 @@ Is the system packaged Git too old? Remove it and compile from source.
|
|||
|
||||
# Download and compile from source
|
||||
cd /tmp
|
||||
curl --remote-name --progress https://www.kernel.org/pub/software/scm/git/git-2.8.4.tar.gz
|
||||
echo '626e319f8a24fc0866167ea5f6bf3e2f38f69d6cb2e59e150f13709ca3ebf301 git-2.8.4.tar.gz' | shasum -a256 -c - && tar -xzf git-2.8.4.tar.gz
|
||||
cd git-2.8.4/
|
||||
curl --remote-name --progress https://www.kernel.org/pub/software/scm/git/git-2.14.3.tar.gz
|
||||
echo '023ffff6d3ba8a1bea779dfecc0ed0bb4ad68ab8601d14435dd8c08416f78d7f git-2.14.3.tar.gz' | shasum -a256 -c - && tar -xzf git-2.14.3.tar.gz
|
||||
cd git-2.14.3/
|
||||
./configure
|
||||
make prefix=/usr/local all
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 11 KiB |
|
@ -90,7 +90,8 @@ structure.
|
|||
|
||||
To create a subgroup:
|
||||
|
||||
1. In the group's dashboard go to the **Subgroups** page and click **New subgroup**.
|
||||
1. In the group's dashboard expand the **New project** split button, select
|
||||
**New subgroup** and click the **New subgroup** button.
|
||||
|
||||
![Subgroups page](img/create_subgroup_button.png)
|
||||
|
||||
|
|
|
@ -193,7 +193,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
|
|||
end
|
||||
|
||||
step 'The link with text "/ID" should have url "tree/markdownID"' do
|
||||
find('a', text: /^\/#id$/)['href'] == current_host + project_tree_path(@project, "markdown") + '#id'
|
||||
find('a', text: %r{^/#id$})['href'] == current_host + project_tree_path(@project, "markdown") + '#id'
|
||||
end
|
||||
|
||||
step 'The link with text "README.mdID" '\
|
||||
|
@ -203,7 +203,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
|
|||
|
||||
step 'The link with text "d/README.mdID" should have '\
|
||||
'url "blob/markdown/d/README.mdID"' do
|
||||
find('a', text: /^d\/README.md#id$/)['href'] == current_host + project_blob_path(@project, "d/markdown/README.md") + '#id'
|
||||
find('a', text: %r{^d/README.md#id$})['href'] == current_host + project_blob_path(@project, "d/markdown/README.md") + '#id'
|
||||
end
|
||||
|
||||
step 'The link with text "ID" should have url "blob/markdown/README.mdID"' do
|
||||
|
@ -212,7 +212,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
|
|||
end
|
||||
|
||||
step 'The link with text "/ID" should have url "blob/markdown/README.mdID"' do
|
||||
find('a', text: /^\/#id$/)['href'] == current_host + project_blob_path(@project, "markdown/README.md") + '#id'
|
||||
find('a', text: %r{^/#id$})['href'] == current_host + project_blob_path(@project, "markdown/README.md") + '#id'
|
||||
end
|
||||
|
||||
# Wiki
|
||||
|
|
|
@ -24,7 +24,7 @@ module API
|
|||
access_requesters = AccessRequestsFinder.new(source).execute!(current_user)
|
||||
access_requesters = paginate(access_requesters.includes(:user))
|
||||
|
||||
present access_requesters.map(&:user), with: Entities::AccessRequester, source: source
|
||||
present access_requesters, with: Entities::AccessRequester
|
||||
end
|
||||
|
||||
desc "Requests access for the authenticated user to a #{source_type}." do
|
||||
|
@ -36,7 +36,7 @@ module API
|
|||
access_requester = source.request_access(current_user)
|
||||
|
||||
if access_requester.persisted?
|
||||
present access_requester.user, with: Entities::AccessRequester, access_requester: access_requester
|
||||
present access_requester, with: Entities::AccessRequester
|
||||
else
|
||||
render_validation_error!(access_requester)
|
||||
end
|
||||
|
@ -56,7 +56,7 @@ module API
|
|||
member = ::Members::ApproveAccessRequestService.new(source, current_user, declared_params).execute
|
||||
|
||||
status :created
|
||||
present member.user, with: Entities::Member, member: member
|
||||
present member, with: Entities::Member
|
||||
end
|
||||
|
||||
desc 'Denies an access request for the given user.' do
|
||||
|
|
|
@ -205,22 +205,15 @@ module API
|
|||
expose :build_artifacts_size, as: :job_artifacts_size
|
||||
end
|
||||
|
||||
class Member < UserBasic
|
||||
expose :access_level do |user, options|
|
||||
member = options[:member] || options[:source].members.find_by(user_id: user.id)
|
||||
member.access_level
|
||||
end
|
||||
expose :expires_at do |user, options|
|
||||
member = options[:member] || options[:source].members.find_by(user_id: user.id)
|
||||
member.expires_at
|
||||
end
|
||||
class Member < Grape::Entity
|
||||
expose :user, merge: true, using: UserBasic
|
||||
expose :access_level
|
||||
expose :expires_at
|
||||
end
|
||||
|
||||
class AccessRequester < UserBasic
|
||||
expose :requested_at do |user, options|
|
||||
access_requester = options[:access_requester] || options[:source].requesters.find_by(user_id: user.id)
|
||||
access_requester.requested_at
|
||||
end
|
||||
class AccessRequester < Grape::Entity
|
||||
expose :user, merge: true, using: UserBasic
|
||||
expose :requested_at
|
||||
end
|
||||
|
||||
class Group < Grape::Entity
|
||||
|
|
|
@ -21,10 +21,11 @@ module API
|
|||
get ":id/members" do
|
||||
source = find_source(source_type, params[:id])
|
||||
|
||||
users = source.users
|
||||
users = users.merge(User.search(params[:query])) if params[:query].present?
|
||||
members = source.members.where.not(user_id: nil).includes(:user)
|
||||
members = members.joins(:user).merge(User.search(params[:query])) if params[:query].present?
|
||||
members = paginate(members)
|
||||
|
||||
present paginate(users), with: Entities::Member, source: source
|
||||
present members, with: Entities::Member
|
||||
end
|
||||
|
||||
desc 'Gets a member of a group or project.' do
|
||||
|
@ -39,7 +40,7 @@ module API
|
|||
members = source.members
|
||||
member = members.find_by!(user_id: params[:user_id])
|
||||
|
||||
present member.user, with: Entities::Member, member: member
|
||||
present member, with: Entities::Member
|
||||
end
|
||||
|
||||
desc 'Adds a member to a group or project.' do
|
||||
|
@ -62,7 +63,7 @@ module API
|
|||
if !member
|
||||
not_allowed! # This currently can only be reached in EE
|
||||
elsif member.persisted? && member.valid?
|
||||
present member.user, with: Entities::Member, member: member
|
||||
present member, with: Entities::Member
|
||||
else
|
||||
render_validation_error!(member)
|
||||
end
|
||||
|
@ -83,7 +84,7 @@ module API
|
|||
member = source.members.find_by!(user_id: params.delete(:user_id))
|
||||
|
||||
if member.update_attributes(declared_params(include_missing: false))
|
||||
present member.user, with: Entities::Member, member: member
|
||||
present member, with: Entities::Member
|
||||
else
|
||||
render_validation_error!(member)
|
||||
end
|
||||
|
|
|
@ -17,15 +17,15 @@ module API
|
|||
}
|
||||
}.freeze
|
||||
PROJECT_TEMPLATE_REGEX =
|
||||
/[\<\{\[]
|
||||
%r{[\<\{\[]
|
||||
(project|description|
|
||||
one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here
|
||||
[\>\}\]]/xi.freeze
|
||||
[\>\}\]]}xi.freeze
|
||||
YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i.freeze
|
||||
FULLNAME_TEMPLATE_REGEX =
|
||||
/[\<\{\[]
|
||||
%r{[\<\{\[]
|
||||
(fullname|name\sof\s(author|copyright\sowner))
|
||||
[\>\}\]]/xi.freeze
|
||||
[\>\}\]]}xi.freeze
|
||||
|
||||
helpers do
|
||||
def parsed_license_template
|
||||
|
|
|
@ -22,10 +22,11 @@ module API
|
|||
get ":id/members" do
|
||||
source = find_source(source_type, params[:id])
|
||||
|
||||
users = source.users
|
||||
users = users.merge(User.search(params[:query])) if params[:query].present?
|
||||
members = source.members.where.not(user_id: nil).includes(:user)
|
||||
members = members.joins(:user).merge(User.search(params[:query])) if params[:query].present?
|
||||
members = paginate(members)
|
||||
|
||||
present paginate(users), with: ::API::Entities::Member, source: source
|
||||
present members, with: ::API::Entities::Member
|
||||
end
|
||||
|
||||
desc 'Gets a member of a group or project.' do
|
||||
|
@ -40,7 +41,7 @@ module API
|
|||
members = source.members
|
||||
member = members.find_by!(user_id: params[:user_id])
|
||||
|
||||
present member.user, with: ::API::Entities::Member, member: member
|
||||
present member, with: ::API::Entities::Member
|
||||
end
|
||||
|
||||
desc 'Adds a member to a group or project.' do
|
||||
|
@ -69,7 +70,7 @@ module API
|
|||
end
|
||||
|
||||
if member.persisted? && member.valid?
|
||||
present member.user, with: ::API::Entities::Member, member: member
|
||||
present member, with: ::API::Entities::Member
|
||||
else
|
||||
# This is to ensure back-compatibility but 400 behavior should be used
|
||||
# for all validation errors in 9.0!
|
||||
|
@ -93,7 +94,7 @@ module API
|
|||
member = source.members.find_by!(user_id: params.delete(:user_id))
|
||||
|
||||
if member.update_attributes(declared_params(include_missing: false))
|
||||
present member.user, with: ::API::Entities::Member, member: member
|
||||
present member, with: ::API::Entities::Member
|
||||
else
|
||||
# This is to ensure back-compatibility but 400 behavior should be used
|
||||
# for all validation errors in 9.0!
|
||||
|
@ -125,7 +126,7 @@ module API
|
|||
else
|
||||
::Members::DestroyService.new(source, current_user, declared_params).execute
|
||||
|
||||
present member.user, with: ::API::Entities::Member, member: member
|
||||
present member, with: ::API::Entities::Member
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -173,7 +173,7 @@ module API
|
|||
use :sort_params
|
||||
use :pagination
|
||||
end
|
||||
get "/search/:query", requirements: { query: /[^\/]+/ } do
|
||||
get "/search/:query", requirements: { query: %r{[^/]+} } do
|
||||
search_service = Search::GlobalService.new(current_user, search: params[:query]).execute
|
||||
projects = search_service.objects('projects', params[:page], false)
|
||||
projects = projects.reorder(params[:order_by] => params[:sort])
|
||||
|
|
|
@ -16,15 +16,15 @@ module API
|
|||
}
|
||||
}.freeze
|
||||
PROJECT_TEMPLATE_REGEX =
|
||||
/[\<\{\[]
|
||||
%r{[\<\{\[]
|
||||
(project|description|
|
||||
one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here
|
||||
[\>\}\]]/xi.freeze
|
||||
[\>\}\]]}xi.freeze
|
||||
YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i.freeze
|
||||
FULLNAME_TEMPLATE_REGEX =
|
||||
/[\<\{\[]
|
||||
%r{[\<\{\[]
|
||||
(fullname|name\sof\s(author|copyright\sowner))
|
||||
[\>\}\]]/xi.freeze
|
||||
[\>\}\]]}xi.freeze
|
||||
DEPRECATION_MESSAGE = ' This endpoint is deprecated and has been removed in V4.'.freeze
|
||||
|
||||
helpers do
|
||||
|
|
|
@ -54,9 +54,9 @@ module Banzai
|
|||
# Build a regexp that matches all valid :emoji: names.
|
||||
def self.emoji_pattern
|
||||
@emoji_pattern ||=
|
||||
/(?<=[^[:alnum:]:]|\n|^)
|
||||
%r{(?<=[^[:alnum:]:]|\n|^)
|
||||
:(#{Gitlab::Emoji.emojis_names.map { |name| Regexp.escape(name) }.join('|')}):
|
||||
(?=[^[:alnum:]:]|$)/x
|
||||
(?=[^[:alnum:]:]|$)}x
|
||||
end
|
||||
|
||||
# Build a regexp that matches all valid unicode emojis names.
|
||||
|
|
|
@ -51,10 +51,10 @@ module Banzai
|
|||
# See https://github.com/gollum/gollum/wiki
|
||||
#
|
||||
# Rubular: http://rubular.com/r/7dQnE5CUCH
|
||||
TAGS_PATTERN = %r{\[\[(.+?)\]\]}.freeze
|
||||
TAGS_PATTERN = /\[\[(.+?)\]\]/.freeze
|
||||
|
||||
# Pattern to match allowed image extensions
|
||||
ALLOWED_IMAGE_EXTENSIONS = %r{.+(jpg|png|gif|svg|bmp)\z}i.freeze
|
||||
ALLOWED_IMAGE_EXTENSIONS = /.+(jpg|png|gif|svg|bmp)\z/i.freeze
|
||||
|
||||
def call
|
||||
search_text_nodes(doc).each do |node|
|
||||
|
|
|
@ -11,7 +11,7 @@ module ContainerRegistry
|
|||
private
|
||||
|
||||
def default_path
|
||||
@uri.sub(/^https?:\/\//, '')
|
||||
@uri.sub(%r{^https?://}, '')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -56,7 +56,7 @@ module ExtractsPath
|
|||
|
||||
if valid_refs.length == 0
|
||||
# No exact ref match, so just try our best
|
||||
pair = id.match(/([^\/]+)(.*)/).captures
|
||||
pair = id.match(%r{([^/]+)(.*)}).captures
|
||||
else
|
||||
# There is a distinct possibility that multiple refs prefix the ID.
|
||||
# Use the longest match to maximize the chance that we have the
|
||||
|
@ -68,7 +68,7 @@ module ExtractsPath
|
|||
end
|
||||
|
||||
# Remove ending slashes from path
|
||||
pair[1].gsub!(/^\/|\/$/, '')
|
||||
pair[1].gsub!(%r{^/|/$}, '')
|
||||
|
||||
pair
|
||||
end
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
module Gitaly
|
||||
class Server
|
||||
def self.all
|
||||
Gitlab.config.repositories.storages.keys.map { |s| Gitaly::Server.new(s) }
|
||||
end
|
||||
|
||||
attr_reader :storage
|
||||
|
||||
def initialize(storage)
|
||||
@storage = storage
|
||||
end
|
||||
|
||||
def server_version
|
||||
info.server_version
|
||||
end
|
||||
|
||||
def git_binary_version
|
||||
info.git_version
|
||||
end
|
||||
|
||||
def up_to_date?
|
||||
server_version == Gitlab::GitalyClient.expected_server_version
|
||||
end
|
||||
|
||||
def address
|
||||
Gitlab::GitalyClient.address(@storage)
|
||||
rescue RuntimeError => e
|
||||
"Error getting the address: #{e.message}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def info
|
||||
@info ||=
|
||||
begin
|
||||
Gitlab::GitalyClient::ServerService.new(@storage).info
|
||||
rescue GRPC::Unavailable, GRPC::GRPC::DeadlineExceeded
|
||||
# This will show the server as being out of date
|
||||
Gitaly::ServerInfoResponse.new(git_version: '', server_version: '')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -12,7 +12,7 @@ module Gitlab
|
|||
|
||||
# Ends with /:random_hex/:filename
|
||||
FILE_UPLOADER_PATH = %r{/\h+/[^/]+\z}
|
||||
FULL_PATH_CAPTURE = %r{\A(.+)#{FILE_UPLOADER_PATH}}
|
||||
FULL_PATH_CAPTURE = /\A(.+)#{FILE_UPLOADER_PATH}/
|
||||
|
||||
# These regex patterns are tested against a relative path, relative to
|
||||
# the upload directory.
|
||||
|
|
|
@ -97,7 +97,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def total_size
|
||||
descendant_pattern = %r{^#{Regexp.escape(@path.to_s)}}
|
||||
descendant_pattern = /^#{Regexp.escape(@path.to_s)}/
|
||||
entries.sum do |path, entry|
|
||||
(entry[:size] if path =~ descendant_pattern).to_i
|
||||
end
|
||||
|
|
|
@ -11,7 +11,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def package_url(name)
|
||||
"https://packagist.org/packages/#{name}" if name =~ %r{\A#{REPO_REGEX}\z}
|
||||
"https://packagist.org/packages/#{name}" if name =~ /\A#{REPO_REGEX}\z/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,7 +15,7 @@ module Gitlab
|
|||
link_regex(/(github:|:github\s*=>)\s*['"](?<name>[^'"]+)['"]/, &method(:github_url))
|
||||
|
||||
# Link `git: "https://gitlab.example.com/user/repo"` to https://gitlab.example.com/user/repo
|
||||
link_regex(%r{(git:|:git\s*=>)\s*['"](?<name>#{URL_REGEX})['"]}, &:itself)
|
||||
link_regex(/(git:|:git\s*=>)\s*['"](?<name>#{URL_REGEX})['"]/, &:itself)
|
||||
|
||||
# Link `source "https://rubygems.org"` to https://rubygems.org
|
||||
link_method_call('source', URL_REGEX, &:itself)
|
||||
|
|
|
@ -12,7 +12,7 @@ module Gitlab
|
|||
def link_dependencies
|
||||
link_method_call('homepage', URL_REGEX, &:itself)
|
||||
|
||||
link_regex(%r{(git:|:git\s*=>)\s*['"](?<name>#{URL_REGEX})['"]}, &:itself)
|
||||
link_regex(/(git:|:git\s*=>)\s*['"](?<name>#{URL_REGEX})['"]/, &:itself)
|
||||
|
||||
link_method_call('license', &method(:license_url))
|
||||
link_regex(/license\s*=\s*\{\s*(type:|:type\s*=>)\s*#{STRING_REGEX}/, &method(:license_url))
|
||||
|
|
|
@ -43,7 +43,7 @@ module Gitlab
|
|||
return "" unless decoded
|
||||
|
||||
# Certain trigger phrases that means we didn't parse correctly
|
||||
if decoded =~ /(Content\-Type\:|multipart\/alternative|text\/plain)/
|
||||
if decoded =~ %r{(Content\-Type\:|multipart/alternative|text/plain)}
|
||||
return ""
|
||||
end
|
||||
|
||||
|
|
|
@ -6,14 +6,14 @@ module Gitlab
|
|||
module FileDetector
|
||||
PATTERNS = {
|
||||
# Project files
|
||||
readme: /\Areadme[^\/]*\z/i,
|
||||
changelog: /\A(changelog|history|changes|news)[^\/]*\z/i,
|
||||
license: /\A(licen[sc]e|copying)(\.[^\/]+)?\z/i,
|
||||
contributing: /\Acontributing[^\/]*\z/i,
|
||||
readme: %r{\Areadme[^/]*\z}i,
|
||||
changelog: %r{\A(changelog|history|changes|news)[^/]*\z}i,
|
||||
license: %r{\A(licen[sc]e|copying)(\.[^/]+)?\z}i,
|
||||
contributing: %r{\Acontributing[^/]*\z}i,
|
||||
version: 'version',
|
||||
avatar: /\Alogo\.(png|jpg|gif)\z/,
|
||||
issue_template: /\A\.gitlab\/issue_templates\/[^\/]+\.md\z/,
|
||||
merge_request_template: /\A\.gitlab\/merge_request_templates\/[^\/]+\.md\z/,
|
||||
issue_template: %r{\A\.gitlab/issue_templates/[^/]+\.md\z},
|
||||
merge_request_template: %r{\A\.gitlab/merge_request_templates/[^/]+\.md\z},
|
||||
|
||||
# Configuration files
|
||||
gitignore: '.gitignore',
|
||||
|
@ -22,17 +22,17 @@ module Gitlab
|
|||
route_map: '.gitlab/route-map.yml',
|
||||
|
||||
# Dependency files
|
||||
cartfile: /\ACartfile[^\/]*\z/,
|
||||
cartfile: %r{\ACartfile[^/]*\z},
|
||||
composer_json: 'composer.json',
|
||||
gemfile: /\A(Gemfile|gems\.rb)\z/,
|
||||
gemfile_lock: 'Gemfile.lock',
|
||||
gemspec: /\A[^\/]*\.gemspec\z/,
|
||||
gemspec: %r{\A[^/]*\.gemspec\z},
|
||||
godeps_json: 'Godeps.json',
|
||||
package_json: 'package.json',
|
||||
podfile: 'Podfile',
|
||||
podspec_json: /\A[^\/]*\.podspec\.json\z/,
|
||||
podspec: /\A[^\/]*\.podspec\z/,
|
||||
requirements_txt: /\A[^\/]*requirements\.txt\z/,
|
||||
podspec_json: %r{\A[^/]*\.podspec\.json\z},
|
||||
podspec: %r{\A[^/]*\.podspec\z},
|
||||
requirements_txt: %r{\A[^/]*requirements\.txt\z},
|
||||
yarn_lock: 'yarn.lock'
|
||||
}.freeze
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ module Gitlab
|
|||
include Gitlab::EncodingHelper
|
||||
|
||||
def ref_name(ref)
|
||||
encode!(ref).sub(/\Arefs\/(tags|heads|remotes)\//, '')
|
||||
encode!(ref).sub(%r{\Arefs/(tags|heads|remotes)/}, '')
|
||||
end
|
||||
|
||||
def branch_name(ref)
|
||||
|
|
|
@ -107,7 +107,7 @@ module Gitlab
|
|||
def find_entry_by_path(repository, root_id, path)
|
||||
root_tree = repository.lookup(root_id)
|
||||
# Strip leading slashes
|
||||
path[/^\/*/] = ''
|
||||
path[%r{^/*}] = ''
|
||||
path_arr = path.split('/')
|
||||
|
||||
entry = root_tree.find do |entry|
|
||||
|
@ -140,7 +140,7 @@ module Gitlab
|
|||
def find_by_gitaly(repository, sha, path, limit: MAX_DATA_DISPLAY_SIZE)
|
||||
return unless path
|
||||
|
||||
path = path.sub(/\A\/*/, '')
|
||||
path = path.sub(%r{\A/*}, '')
|
||||
path = '/' if path.empty?
|
||||
name = File.basename(path)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ module Gitlab
|
|||
class << self
|
||||
def normalize_path(filename)
|
||||
# Strip all leading slashes so that //foo -> foo
|
||||
filename[/^\/*/] = ''
|
||||
filename[%r{^/*}] = ''
|
||||
|
||||
# Expand relative paths (e.g. foo/../bar)
|
||||
filename = Pathname.new(filename)
|
||||
|
|
|
@ -23,7 +23,7 @@ module Gitlab
|
|||
# Ex.
|
||||
# Ref.extract_branch_name('refs/heads/master') #=> 'master'
|
||||
def self.extract_branch_name(str)
|
||||
str.gsub(/\Arefs\/heads\//, '')
|
||||
str.gsub(%r{\Arefs/heads/}, '')
|
||||
end
|
||||
|
||||
# Gitaly: this method will probably be migrated indirectly via its call sites.
|
||||
|
|
|
@ -462,7 +462,6 @@ module Gitlab
|
|||
path: nil,
|
||||
follow: false,
|
||||
skip_merges: false,
|
||||
disable_walk: false,
|
||||
after: nil,
|
||||
before: nil
|
||||
}
|
||||
|
@ -494,11 +493,7 @@ module Gitlab
|
|||
return []
|
||||
end
|
||||
|
||||
if log_using_shell?(options)
|
||||
log_by_shell(sha, options)
|
||||
else
|
||||
log_by_walk(sha, options)
|
||||
end
|
||||
end
|
||||
|
||||
def count_commits(options)
|
||||
|
@ -1386,8 +1381,18 @@ module Gitlab
|
|||
run_git(args).first.scrub.split(/^--$/)
|
||||
end
|
||||
|
||||
def can_be_merged?(source_sha, target_branch)
|
||||
gitaly_migrate(:can_be_merged) do |is_enabled|
|
||||
if is_enabled
|
||||
gitaly_can_be_merged?(source_sha, find_branch(target_branch, true).target)
|
||||
else
|
||||
rugged_can_be_merged?(source_sha, target_branch)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def search_files_by_name(query, ref)
|
||||
safe_query = Regexp.escape(query.sub(/^\/*/, ""))
|
||||
safe_query = Regexp.escape(query.sub(%r{^/*}, ""))
|
||||
|
||||
return [] if empty? || safe_query.blank?
|
||||
|
||||
|
@ -1635,24 +1640,6 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def log_using_shell?(options)
|
||||
options[:path].present? ||
|
||||
options[:disable_walk] ||
|
||||
options[:skip_merges] ||
|
||||
options[:after] ||
|
||||
options[:before]
|
||||
end
|
||||
|
||||
def log_by_walk(sha, options)
|
||||
walk_options = {
|
||||
show: sha,
|
||||
sort: Rugged::SORT_NONE,
|
||||
limit: options[:limit],
|
||||
offset: options[:offset]
|
||||
}
|
||||
Rugged::Walker.walk(rugged, walk_options).to_a
|
||||
end
|
||||
|
||||
# Gitaly note: JV: although #log_by_shell shells out to Git I think the
|
||||
# complexity is such that we should migrate it as Ruby before trying to
|
||||
# do it in Go.
|
||||
|
@ -2015,7 +2002,7 @@ module Gitlab
|
|||
target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target)
|
||||
Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target, target_commit)
|
||||
rescue Rugged::ReferenceError => e
|
||||
raise InvalidRef.new("Branch #{ref} already exists") if e.to_s =~ /'refs\/heads\/#{ref}'/
|
||||
raise InvalidRef.new("Branch #{ref} already exists") if e.to_s =~ %r{'refs/heads/#{ref}'}
|
||||
|
||||
raise InvalidRef.new("Invalid reference #{start_point}")
|
||||
end
|
||||
|
@ -2280,6 +2267,14 @@ module Gitlab
|
|||
run_git(['fetch', remote_name], env: env).last.zero?
|
||||
end
|
||||
|
||||
def gitaly_can_be_merged?(their_commit, our_commit)
|
||||
!gitaly_conflicts_client(our_commit, their_commit).conflicts?
|
||||
end
|
||||
|
||||
def rugged_can_be_merged?(their_commit, our_commit)
|
||||
!rugged.merge_commits(our_commit, their_commit).conflicts?
|
||||
end
|
||||
|
||||
def gitlab_projects_error
|
||||
raise CommandError, @gitlab_projects.output
|
||||
end
|
||||
|
|
|
@ -43,7 +43,7 @@ module Gitlab
|
|||
branches = []
|
||||
|
||||
rugged.references.each("refs/remotes/#{remote_name}/*").map do |ref|
|
||||
name = ref.name.sub(/\Arefs\/remotes\/#{remote_name}\//, '')
|
||||
name = ref.name.sub(%r{\Arefs/remotes/#{remote_name}/}, '')
|
||||
|
||||
begin
|
||||
target_commit = Gitlab::Git::Commit.find(self, ref.target)
|
||||
|
|
|
@ -83,6 +83,8 @@ module Gitlab
|
|||
commit_id: sha
|
||||
)
|
||||
end
|
||||
rescue Rugged::ReferenceError
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -257,7 +257,7 @@ module Gitlab
|
|||
offset: options[:offset],
|
||||
follow: options[:follow],
|
||||
skip_merges: options[:skip_merges],
|
||||
disable_walk: options[:disable_walk]
|
||||
disable_walk: true # This option is deprecated. The 'walk' implementation is being removed.
|
||||
)
|
||||
request.after = GitalyClient.timestamp(options[:after]) if options[:after]
|
||||
request.before = GitalyClient.timestamp(options[:before]) if options[:before]
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
module Gitlab
|
||||
module GitalyClient
|
||||
# Meant for extraction of server data, and later maybe to perform misc task
|
||||
#
|
||||
# Not meant for connection logic, look in Gitlab::GitalyClient
|
||||
class ServerService
|
||||
def initialize(storage)
|
||||
@storage = storage
|
||||
end
|
||||
|
||||
def info
|
||||
GitalyClient.call(@storage, :server_service, :server_info, Gitaly::ServerInfoRequest.new)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -13,7 +13,7 @@ module Gitlab
|
|||
:diff_hunk, :author, :note, :created_at, :updated_at,
|
||||
:github_id
|
||||
|
||||
NOTEABLE_ID_REGEX = /\/pull\/(?<iid>\d+)/i
|
||||
NOTEABLE_ID_REGEX = %r{/pull/(?<iid>\d+)}i
|
||||
|
||||
# Builds a diff note from a GitHub API response.
|
||||
#
|
||||
|
|
|
@ -12,7 +12,7 @@ module Gitlab
|
|||
expose_attribute :noteable_id, :noteable_type, :author, :note,
|
||||
:created_at, :updated_at, :github_id
|
||||
|
||||
NOTEABLE_TYPE_REGEX = /\/(?<type>(pull|issues))\/(?<iid>\d+)/i
|
||||
NOTEABLE_TYPE_REGEX = %r{/(?<type>(pull|issues))/(?<iid>\d+)}i
|
||||
|
||||
# Builds a note from a GitHub API response.
|
||||
#
|
||||
|
|
|
@ -59,7 +59,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def extracted_files
|
||||
Dir.glob("#{@shared.export_path}/**/*", File::FNM_DOTMATCH).reject { |f| f =~ /.*\/\.{1,2}$/ }
|
||||
Dir.glob("#{@shared.export_path}/**/*", File::FNM_DOTMATCH).reject { |f| f =~ %r{.*/\.{1,2}$} }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -34,7 +34,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def relative_path(path)
|
||||
path.gsub(/^#{Rails.root.to_s}\/?/, '')
|
||||
path.gsub(%r{^#{Rails.root.to_s}/?}, '')
|
||||
end
|
||||
|
||||
def values_for(event)
|
||||
|
|
|
@ -56,12 +56,12 @@ module Gitlab
|
|||
end
|
||||
|
||||
def strip_url(url)
|
||||
url.gsub(/\Ahttps?:\/\//, '')
|
||||
url.gsub(%r{\Ahttps?://}, '')
|
||||
end
|
||||
|
||||
def project_path(request)
|
||||
path_info = request.env["PATH_INFO"]
|
||||
path_info.sub!(/^\//, '')
|
||||
path_info.sub!(%r{^/}, '')
|
||||
|
||||
project_path_match = "#{path_info}/".match(PROJECT_PATH_REGEX)
|
||||
return unless project_path_match
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module Gitlab
|
||||
module Middleware
|
||||
class Static < ActionDispatch::Static
|
||||
UPLOADS_REGEX = /\A\/uploads(\/|\z)/.freeze
|
||||
UPLOADS_REGEX = %r{\A/uploads(/|\z)}.freeze
|
||||
|
||||
def call(env)
|
||||
return @app.call(env) if env['PATH_INFO'] =~ UPLOADS_REGEX
|
||||
|
|
|
@ -9,7 +9,7 @@ module Gitlab
|
|||
# if date doesn't present return time with current date
|
||||
# in other cases return nil
|
||||
class SpendTimeAndDateSeparator
|
||||
DATE_REGEX = /(\d{2,4}[\/\-.]\d{1,2}[\/\-.]\d{1,2})/
|
||||
DATE_REGEX = %r{(\d{2,4}[/\-.]\d{1,2}[/\-.]\d{1,2})}
|
||||
|
||||
def initialize(spend_command_arg)
|
||||
@spend_arg = spend_command_arg
|
||||
|
|
|
@ -30,7 +30,7 @@ module Gitlab
|
|||
raise NotFoundError.new("No known storage path matches #{repo_path.inspect}")
|
||||
end
|
||||
|
||||
result.sub(/\A\/*/, '')
|
||||
result.sub(%r{\A/*}, '')
|
||||
end
|
||||
|
||||
def self.find_project(project_path)
|
||||
|
|
|
@ -31,7 +31,7 @@ module Gitlab
|
|||
storages << { name: 'test_second_storage', path: Rails.root.join('tmp', 'tests', 'second_storage').to_s }
|
||||
end
|
||||
|
||||
config = { socket_path: address.sub(%r{\Aunix:}, ''), storage: storages }
|
||||
config = { socket_path: address.sub(/\Aunix:/, ''), storage: storages }
|
||||
config[:auth] = { token: 'secret' } if Rails.env.test?
|
||||
config[:'gitaly-ruby'] = { dir: File.join(gitaly_dir, 'ruby') } if gitaly_ruby
|
||||
config[:'gitlab-shell'] = { dir: Gitlab.config.gitlab_shell.path }
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue