Commit 6b824cc2 authored by Clement Ho's avatar Clement Ho

Merge branch 'master' into 'refactor-jobs-show-haml'

# Conflicts:
#   app/assets/javascripts/dispatcher.js
parents 27a6d65c b2363483
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
"import/no-commonjs": "error", "import/no-commonjs": "error",
"no-multiple-empty-lines": ["error", { "max": 1 }], "no-multiple-empty-lines": ["error", { "max": 1 }],
"promise/catch-or-return": "error", "promise/catch-or-return": "error",
"no-underscore-dangle": ["error", { "allow": ["__"]}], "no-underscore-dangle": ["error", { "allow": ["__", "_links"]}],
"vue/html-self-closing": ["error", { "vue/html-self-closing": ["error", {
"html": { "html": {
"void": "always", "void": "always",
......
...@@ -2,6 +2,25 @@ ...@@ -2,6 +2,25 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 10.4.4 (2018-02-16)
### Security (1 change)
- Update nokogiri to 1.8.2. !16807
### Fixed (9 changes)
- Fix 500 error when loading a merge request with an invalid comment. !16795
- Cleanup new branch/merge request form in issues. !16854
- Fix GitLab import leaving group_id on ProjectLabel. !16877
- Fix forking projects when no restricted visibility levels are defined applicationwide. !16881
- Resolve PrepareUntrackedUploads PostgreSQL syntax error. !17019
- Fixed error 500 when removing an identity with synced attributes and visiting the profile page. !17054
- Validate user namespace before saving so that errors persist on model.
- LDAP Person no longer throws exception on invalid entry.
- Fix JIRA not working when a trailing slash is included.
## 10.4.3 (2018-02-05) ## 10.4.3 (2018-02-05)
### Security (4 changes) ### Security (4 changes)
......
...@@ -75,6 +75,7 @@ export default class AjaxVariableList { ...@@ -75,6 +75,7 @@ export default class AjaxVariableList {
if (res.status === statusCodes.OK && res.data) { if (res.status === statusCodes.OK && res.data) {
this.updateRowsWithPersistedVariables(res.data.variables); this.updateRowsWithPersistedVariables(res.data.variables);
this.variableList.hideValues();
} else if (res.status === statusCodes.BAD_REQUEST) { } else if (res.status === statusCodes.BAD_REQUEST) {
// Validation failed // Validation failed
this.errorBox.innerHTML = generateErrorBoxContent(res.data); this.errorBox.innerHTML = generateErrorBoxContent(res.data);
......
...@@ -178,6 +178,10 @@ export default class VariableList { ...@@ -178,6 +178,10 @@ export default class VariableList {
this.$container.find('.js-row-remove-button').attr('disabled', !isEnabled); this.$container.find('.js-row-remove-button').attr('disabled', !isEnabled);
} }
hideValues() {
this.secretValues.updateDom(false);
}
getAllData() { getAllData() {
// Ignore the last empty row because we don't want to try persist // Ignore the last empty row because we don't want to try persist
// a blank variable and run into validation problems. // a blank variable and run into validation problems.
......
...@@ -43,174 +43,14 @@ var Dispatcher; ...@@ -43,174 +43,14 @@ var Dispatcher;
}); });
switch (page) { switch (page) {
case 'projects:environments:metrics':
import('./pages/projects/environments/metrics')
.then(callDefault)
.catch(fail);
break;
case 'projects:merge_requests:index': case 'projects:merge_requests:index':
case 'projects:issues:index': case 'projects:issues:index':
case 'projects:issues:show': case 'projects:issues:show':
shortcut_handler = true;
break;
case 'projects:milestones:index':
import('./pages/projects/milestones/index')
.then(callDefault)
.catch(fail);
break;
case 'projects:milestones:show':
import('./pages/projects/milestones/show')
.then(callDefault)
.catch(fail);
break;
case 'groups:milestones:show':
import('./pages/groups/milestones/show')
.then(callDefault)
.catch(fail);
break;
case 'dashboard:milestones:show':
import('./pages/dashboard/milestones/show')
.then(callDefault)
.catch(fail);
break;
case 'dashboard:issues':
import('./pages/dashboard/issues')
.then(callDefault)
.catch(fail);
break;
case 'dashboard:merge_requests':
import('./pages/dashboard/merge_requests')
.then(callDefault)
.catch(fail);
break;
case 'groups:issues':
import('./pages/groups/issues')
.then(callDefault)
.catch(fail);
break;
case 'groups:merge_requests':
import('./pages/groups/merge_requests')
.then(callDefault)
.catch(fail);
break;
case 'dashboard:todos:index':
import('./pages/dashboard/todos/index')
.then(callDefault)
.catch(fail);
break;
case 'admin:jobs:index':
import('./pages/admin/jobs/index')
.then(callDefault)
.catch(fail);
break;
case 'admin:projects:index':
import('./pages/admin/projects/index/index')
.then(callDefault)
.catch(fail);
break;
case 'admin:users:index':
import('./pages/admin/users/shared')
.then(callDefault)
.catch(fail);
break;
case 'admin:users:show':
import('./pages/admin/users/shared')
.then(callDefault)
.catch(fail);
break;
case 'dashboard:projects:index':
case 'dashboard:projects:starred':
import('./pages/dashboard/projects')
.then(callDefault)
.catch(fail);
break;
case 'explore:projects:index':
case 'explore:projects:trending':
case 'explore:projects:starred':
import('./pages/explore/projects')
.then(callDefault)
.catch(fail);
break;
case 'explore:groups:index':
import('./pages/explore/groups')
.then(callDefault)
.catch(fail);
break;
case 'projects:milestones:new':
case 'projects:milestones:create':
import('./pages/projects/milestones/new')
.then(callDefault)
.catch(fail);
break;
case 'projects:milestones:edit':
case 'projects:milestones:update':
import('./pages/projects/milestones/edit')
.then(callDefault)
.catch(fail);
break;
case 'groups:milestones:new':
case 'groups:milestones:create':
import('./pages/groups/milestones/new')
.then(callDefault)
.catch(fail);
break;
case 'groups:milestones:edit':
case 'groups:milestones:update':
import('./pages/groups/milestones/edit')
.then(callDefault)
.catch(fail);
break;
case 'projects:compare:show':
import('./pages/projects/compare/show')
.then(callDefault)
.catch(fail);
break;
case 'projects:branches:new':
import('./pages/projects/branches/new')
.then(callDefault)
.catch(fail);
break;
case 'projects:branches:create':
import('./pages/projects/branches/new')
.then(callDefault)
.catch(fail);
break;
case 'projects:branches:index':
import('./pages/projects/branches/index')
.then(callDefault)
.catch(fail);
break;
case 'projects:issues:new': case 'projects:issues:new':
import('./pages/projects/issues/new')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:issues:edit': case 'projects:issues:edit':
import('./pages/projects/issues/edit')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:jobs:show':
import('./pages/projects/jobs/show')
.then(callDefault)
.catch(fail);
break;
case 'projects:merge_requests:creations:new': case 'projects:merge_requests:creations:new':
import('./pages/projects/merge_requests/creations/new')
.then(callDefault)
.catch(fail);
case 'projects:merge_requests:creations:diffs': case 'projects:merge_requests:creations:diffs':
import('./pages/projects/merge_requests/creations/diffs')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:merge_requests:edit': case 'projects:merge_requests:edit':
import('./pages/projects/merge_requests/edit')
.then(callDefault)
.catch(fail);
shortcut_handler = true; shortcut_handler = true;
break; break;
case 'projects:tags:new': case 'projects:tags:new':
...@@ -472,11 +312,6 @@ var Dispatcher; ...@@ -472,11 +312,6 @@ var Dispatcher;
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
break; break;
case 'users:show':
import('./pages/users/show')
.then(callDefault)
.catch(fail);
break;
case 'admin:conversational_development_index:show': case 'admin:conversational_development_index:show':
import('./pages/admin/conversational_development_index/show') import('./pages/admin/conversational_development_index/show')
.then(callDefault) .then(callDefault)
......
...@@ -6,6 +6,11 @@ monacoContext.require.config({ ...@@ -6,6 +6,11 @@ monacoContext.require.config({
}, },
}); });
// ignore CDN config and use local assets path for service worker which cannot be cross-domain
const relativeRootPath = (gon && gon.relative_url_root) || '';
const monacoPath = `${relativeRootPath}/assets/webpack/monaco-editor/vs`;
window.MonacoEnvironment = { getWorkerUrl: () => `${monacoPath}/base/worker/workerMain.js` };
// eslint-disable-next-line no-underscore-dangle // eslint-disable-next-line no-underscore-dangle
window.__monaco_context__ = monacoContext; window.__monaco_context__ = monacoContext;
export default monacoContext.require; export default monacoContext.require;
...@@ -59,8 +59,10 @@ class ImporterStatus { ...@@ -59,8 +59,10 @@ class ImporterStatus {
.catch(() => flash(__('An error occurred while importing project'))); .catch(() => flash(__('An error occurred while importing project')));
} }
setAutoUpdate() { autoUpdate() {
return setInterval(() => $.get(this.jobsUrl, data => $.each(data, (i, job) => { return axios.get(this.jobsUrl)
.then(({ data = [] }) => {
data.forEach((job) => {
const jobItem = $(`#project_${job.id}`); const jobItem = $(`#project_${job.id}`);
const statusField = jobItem.find('.job-status'); const statusField = jobItem.find('.job-status');
...@@ -81,7 +83,12 @@ class ImporterStatus { ...@@ -81,7 +83,12 @@ class ImporterStatus {
statusField.html(job.import_status); statusField.html(job.import_status);
break; break;
} }
})), 4000); });
});
}
setAutoUpdate() {
setInterval(this.autoUpdate.bind(this), 4000);
} }
} }
......
<script> <script>
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import Flash from '~/flash'; import createFlash from '~/flash';
import modal from '~/vue_shared/components/modal.vue'; import GlModal from '~/vue_shared/components/gl_modal.vue';
import { s__ } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility'; import { redirectTo } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
export default { export default {
components: { components: {
modal, GlModal,
}, },
props: { props: {
url: { url: {
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
}, },
computed: { computed: {
text() { text() {
return s__('AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running.'); return s__('AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running.');
}, },
}, },
methods: { methods: {
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
redirectTo(response.request.responseURL); redirectTo(response.request.responseURL);
}) })
.catch((error) => { .catch((error) => {
Flash(s__('AdminArea|Stopping jobs failed')); createFlash(s__('AdminArea|Stopping jobs failed'));
throw error; throw error;
}); });
}, },
...@@ -37,11 +37,13 @@ ...@@ -37,11 +37,13 @@
</script> </script>
<template> <template>
<modal <gl-modal
id="stop-jobs-modal" id="stop-jobs-modal"
:title="s__('AdminArea|Stop all jobs?')" :header-title-text="s__('AdminArea|Stop all jobs?')"
:text="text" footer-primary-button-variant="danger"
kind="danger" :footer-primary-button-text="s__('AdminArea|Stop jobs')"
:primary-button-label="s__('AdminArea|Stop jobs')" @submit="onSubmit"
@submit="onSubmit" /> >
{{ text }}
</gl-modal>
</template> </template>
import Vue from 'vue'; import Vue from 'vue';
import Translate from '~/vue_shared/translate'; import Translate from '~/vue_shared/translate';
import stopJobsModal from './components/stop_jobs_modal.vue'; import stopJobsModal from './components/stop_jobs_modal.vue';
Vue.use(Translate); Vue.use(Translate);
export default () => { document.addEventListener('DOMContentLoaded', () => {
const stopJobsButton = document.getElementById('stop-jobs-button'); const stopJobsButton = document.getElementById('stop-jobs-button');
if (stopJobsButton) {
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new Vue({ new Vue({
el: '#stop-jobs-modal', el: '#stop-jobs-modal',
...@@ -26,4 +24,5 @@ export default () => { ...@@ -26,4 +24,5 @@ export default () => {
}); });
}, },
}); });
}; }
});
...@@ -5,7 +5,7 @@ import csrf from '~/lib/utils/csrf'; ...@@ -5,7 +5,7 @@ import csrf from '~/lib/utils/csrf';
import deleteProjectModal from './components/delete_project_modal.vue'; import deleteProjectModal from './components/delete_project_modal.vue';
export default () => { document.addEventListener('DOMContentLoaded', () => {
Vue.use(Translate); Vue.use(Translate);
const deleteProjectModalEl = document.getElementById('delete-project-modal'); const deleteProjectModalEl = document.getElementById('delete-project-modal');
...@@ -34,4 +34,4 @@ export default () => { ...@@ -34,4 +34,4 @@ export default () => {
deleteModal.projectName = buttonProps.projectName; deleteModal.projectName = buttonProps.projectName;
} }
}); });
}; });
...@@ -5,7 +5,7 @@ import csrf from '~/lib/utils/csrf'; ...@@ -5,7 +5,7 @@ import csrf from '~/lib/utils/csrf';
import deleteUserModal from './components/delete_user_modal.vue'; import deleteUserModal from './components/delete_user_modal.vue';
export default () => { document.addEventListener('DOMContentLoaded', () => {
Vue.use(Translate); Vue.use(Translate);
const deleteUserModalEl = document.getElementById('delete-user-modal'); const deleteUserModalEl = document.getElementById('delete-user-modal');
...@@ -40,4 +40,4 @@ export default () => { ...@@ -40,4 +40,4 @@ export default () => {
deleteModal.username = buttonProps.username; deleteModal.username = buttonProps.username;
} }
}); });
}; });
import projectSelect from '~/project_select'; import projectSelect from '~/project_select';
import initLegacyFilters from '~/init_legacy_filters'; import initLegacyFilters from '~/init_legacy_filters';
export default () => { document.addEventListener('DOMContentLoaded', () => {
projectSelect(); projectSelect();
initLegacyFilters(); initLegacyFilters();
}; });
import projectSelect from '~/project_select'; import projectSelect from '~/project_select';
import initLegacyFilters from '~/init_legacy_filters'; import initLegacyFilters from '~/init_legacy_filters';
export default () => { document.addEventListener('DOMContentLoaded', () => {
projectSelect(); projectSelect();
initLegacyFilters(); initLegacyFilters();
}; });
import Milestone from '~/milestone'; import Milestone from '~/milestone';
import Sidebar from '~/right_sidebar'; import Sidebar from '~/right_sidebar';
export default () => { document.addEventListener('DOMContentLoaded', () => {
new Milestone(); // eslint-disable-line no-new new Milestone(); // eslint-disable-line no-new
new Sidebar(); // eslint-disable-line no-new new Sidebar(); // eslint-disable-line no-new
}; });
import ProjectsList from '~/projects_list'; import ProjectsList from '~/projects_list';
export default () => new ProjectsList(); document.addEventListener('DOMContentLoaded', () => new ProjectsList());
import Todos from './todos'; import Todos from './todos';
export default () => new Todos(); document.addEventListener('DOMContentLoaded', () => new Todos());
...@@ -2,7 +2,7 @@ import GroupsList from '~/groups_list'; ...@@ -2,7 +2,7 @@ import GroupsList from '~/groups_list';
import Landing from '~/landing'; import Landing from '~/landing';
import initGroupsList from '../../../groups'; import initGroupsList from '../../../groups';
export default function () { document.addEventListener('DOMContentLoaded', () => {
new GroupsList(); // eslint-disable-line no-new new GroupsList(); // eslint-disable-line no-new
initGroupsList(); initGroupsList();
const landingElement = document.querySelector('.js-explore-groups-landing'); const landingElement = document.querySelector('.js-explore-groups-landing');
...@@ -13,4 +13,4 @@ export default function () { ...@@ -13,4 +13,4 @@ export default function () {
'explore_groups_landing_dismissed', 'explore_groups_landing_dismissed',
); );
exploreGroupsLanding.toggle(); exploreGroupsLanding.toggle();
} });
import ProjectsList from '~/projects_list'; import ProjectsList from '~/projects_list';
export default () => new ProjectsList(); document.addEventListener('DOMContentLoaded', () => new ProjectsList());
...@@ -2,9 +2,9 @@ import projectSelect from '~/project_select'; ...@@ -2,9 +2,9 @@ import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search'; import initFilteredSearch from '~/pages/search/init_filtered_search';
import { FILTERED_SEARCH } from '~/pages/constants'; import { FILTERED_SEARCH } from '~/pages/constants';
export default () => { document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({ initFilteredSearch({
page: FILTERED_SEARCH.ISSUES, page: FILTERED_SEARCH.ISSUES,
}); });
projectSelect(); projectSelect();
}; });
...@@ -2,9 +2,9 @@ import projectSelect from '~/project_select'; ...@@ -2,9 +2,9 @@ import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search'; import initFilteredSearch from '~/pages/search/init_filtered_search';
import { FILTERED_SEARCH } from '~/pages/constants'; import { FILTERED_SEARCH } from '~/pages/constants';
export default () => { document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({ initFilteredSearch({
page: FILTERED_SEARCH.MERGE_REQUESTS, page: FILTERED_SEARCH.MERGE_REQUESTS,
}); });
projectSelect(); projectSelect();
}; });
import initForm from '../../../../shared/milestones/form'; import initForm from '../../../../shared/milestones/form';
export default () => initForm(false); document.addEventListener('DOMContentLoaded', () => initForm(false));
import initForm from '../../../../shared/milestones/form'; import initForm from '../../../../shared/milestones/form';
export default () => initForm(false); document.addEventListener('DOMContentLoaded', () => initForm(false));
import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show'; import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show';
export default initMilestonesShow; document.addEventListener('DOMContentLoaded', initMilestonesShow);
import AjaxLoadingSpinner from '~/ajax_loading_spinner'; import AjaxLoadingSpinner from '~/ajax_loading_spinner';
import DeleteModal from '~/branches/branches_delete_modal'; import DeleteModal from '~/branches/branches_delete_modal';
export default () => { document.addEventListener('DOMContentLoaded', () => {
AjaxLoadingSpinner.init(); AjaxLoadingSpinner.init();
new DeleteModal(); // eslint-disable-line no-new new DeleteModal(); // eslint-disable-line no-new
}; });
import NewBranchForm from '~/new_branch_form'; import NewBranchForm from '~/new_branch_form';
export default () => new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML)); document.addEventListener('DOMContentLoaded', () => (
new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML))
));
import Diff from '~/diff'; import Diff from '~/diff';
import initChangesDropdown from '~/init_changes_dropdown'; import initChangesDropdown from '~/init_changes_dropdown';
export default () => { document.addEventListener('DOMContentLoaded', () => {
new Diff(); // eslint-disable-line no-new new Diff(); // eslint-disable-line no-new
const paddingTop = 16; const paddingTop = 16;
initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - paddingTop); initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - paddingTop);
}; });
import monitoringBundle from '~/monitoring/monitoring_bundle'; import monitoringBundle from '~/monitoring/monitoring_bundle';
export default monitoringBundle; document.addEventListener('DOMContentLoaded', monitoringBundle);
import initForm from '../form'; import initForm from '../form';
export default () => { document.addEventListener('DOMContentLoaded', initForm);
initForm();
};
import initForm from '../form'; import initForm from '../form';
export default () => { document.addEventListener('DOMContentLoaded', initForm);
initForm();
};
import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request'; import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request';
export default initMergeRequest; document.addEventListener('DOMContentLoaded', initMergeRequest);
import Compare from '~/compare'; import Compare from '~/compare';
import MergeRequest from '~/merge_request'; import MergeRequest from '~/merge_request';
export default () => { document.addEventListener('DOMContentLoaded', () => {
const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare'); const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare');
if (mrNewCompareNode) { if (mrNewCompareNode) {
new Compare({ // eslint-disable-line no-new new Compare({ // eslint-disable-line no-new
...@@ -15,4 +15,4 @@ export default () => { ...@@ -15,4 +15,4 @@ export default () => {
action: mrNewSubmitNode.dataset.mrSubmitAction, action: mrNewSubmitNode.dataset.mrSubmitAction,
}); });
} }
}; });
import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request'; import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request';
export default initMergeRequest; document.addEventListener('DOMContentLoaded', initMergeRequest);
import initForm from '../../../../shared/milestones/form'; import initForm from '../../../../shared/milestones/form';
export default () => initForm(); document.addEventListener('DOMContentLoaded', () => initForm());
import milestones from '~/pages/milestones/shared'; import milestones from '~/pages/milestones/shared';
export default milestones; document.addEventListener('DOMContentLoaded', milestones);
import initForm from '../../../../shared/milestones/form'; import initForm from '../../../../shared/milestones/form';
export default () => initForm(); document.addEventListener('DOMContentLoaded', () => initForm());
import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show'; import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show';
import milestones from '~/pages/milestones/shared'; import milestones from '~/pages/milestones/shared';
export default () => { document.addEventListener('DOMContentLoaded', () => {
initMilestonesShow(); initMilestonesShow();
milestones(); milestones();
}; });
import Chart from 'vendor/Chart';
const options = {
scaleOverlay: true,
responsive: true,
maintainAspectRatio: false,
};
const buildChart = (chartScope) => {
const data = {
labels: chartScope.labels,
datasets: [{
fillColor: '#707070',
strokeColor: '#707070',
pointColor: '#707070',
pointStrokeColor: '#EEE',
data: chartScope.totalValues,
},
{
fillColor: '#1aaa55',
strokeColor: '#1aaa55',
pointColor: '#1aaa55',
pointStrokeColor: '#fff',
data: chartScope.successValues,
},
],
};
const ctx = $(`#${chartScope.scope}Chart`).get(0).getContext('2d');
new Chart(ctx).Line(data, options);
};
document.addEventListener('DOMContentLoaded', () => {
const chartTimesData = JSON.parse(document.getElementById('pipelinesTimesChartsData').innerHTML);
const chartsData = JSON.parse(document.getElementById('pipelinesChartsData').innerHTML);
const data = {
labels: chartTimesData.labels,
datasets: [{
fillColor: 'rgba(220,220,220,0.5)',
strokeColor: 'rgba(220,220,220,1)',
barStrokeWidth: 1,
barValueSpacing: 1,
barDatasetSpacing: 1,
data: chartTimesData.values,
}],
};
if (window.innerWidth < 768) {
// Scale fonts if window width lower than 768px (iPad portrait)
options.scaleFontSize = 8;
}
new Chart($('#build_timesChart').get(0).getContext('2d')).Bar(data, options);
chartsData.forEach(scope => buildChart(scope));
});
import IntegrationSettingsForm from '~/integrations/integration_settings_form'; import IntegrationSettingsForm from '~/integrations/integration_settings_form';
import PrometheusMetrics from '~/prometheus_metrics/prometheus_metrics';
export default () => { export default () => {
const prometheusSettingsWrapper = document.querySelector('.js-prometheus-metrics-monitoring');
const integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form'); const integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form');
integrationSettingsForm.init(); integrationSettingsForm.init();
if (prometheusSettingsWrapper) {
const prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
prometheusMetrics.loadActiveMetrics();
}
}; };
import UserCallout from '~/user_callout';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import UserTabs from './user_tabs'; import UserTabs from './user_tabs';
...@@ -22,4 +23,5 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -22,4 +23,5 @@ document.addEventListener('DOMContentLoaded', () => {
const page = $('body').attr('data-page'); const page = $('body').attr('data-page');
const action = page.split(':')[1]; const action = page.split(':')[1];
initUserProfile(action); initUserProfile(action);
new UserCallout(); // eslint-disable-line no-new
}); });
import UserCallout from '~/user_callout';
export default () => new UserCallout();
import axios from '../lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import Activities from '../activities'; import Activities from '~/activities';
import { localTimeAgo } from '~/lib/utils/datetime_utility';
import { __ } from '~/locale';
import flash from '~/flash';
import ActivityCalendar from './activity_calendar'; import ActivityCalendar from './activity_calendar';
import { localTimeAgo } from '../lib/utils/datetime_utility';
import { __ } from '../locale';
import flash from '../flash';
/** /**
* UserTabs * UserTabs
......
import Chart from 'vendor/Chart';
document.addEventListener('DOMContentLoaded', () => {
const chartData = JSON.parse(document.getElementById('pipelinesChartsData').innerHTML);
const buildChart = (chartScope) => {
const data = {
labels: chartScope.labels,
datasets: [{
fillColor: '#707070',
strokeColor: '#707070',
pointColor: '#707070',
pointStrokeColor: '#EEE',
data: chartScope.totalValues,
},
{
fillColor: '#1aaa55',
strokeColor: '#1aaa55',
pointColor: '#1aaa55',
pointStrokeColor: '#fff',
data: chartScope.successValues,
},
],
};
const ctx = $(`#${chartScope.scope}Chart`).get(0).getContext('2d');
const options = {
scaleOverlay: true,
responsive: true,
maintainAspectRatio: false,
};
if (window.innerWidth < 768) {
// Scale fonts if window width lower than 768px (iPad portrait)
options.scaleFontSize = 8;
}
new Chart(ctx).Line(data, options);
};
chartData.forEach(scope => buildChart(scope));
});
import Chart from 'vendor/Chart';
document.addEventListener('DOMContentLoaded', () => {
const chartData = JSON.parse(document.getElementById('pipelinesTimesChartsData').innerHTML);
const data = {
labels: chartData.labels,
datasets: [{
fillColor: 'rgba(220,220,220,0.5)',
strokeColor: 'rgba(220,220,220,1)',
barStrokeWidth: 1,
barValueSpacing: 1,
barDatasetSpacing: 1,
data: chartData.values,
}],
};
const ctx = $('#build_timesChart').get(0).getContext('2d');
const options = {
scaleOverlay: true,
responsive: true,
maintainAspectRatio: false,
};
if (window.innerWidth < 768) {
// Scale fonts if window width lower than 768px (iPad portrait)
options.scaleFontSize = 8;
}
new Chart(ctx).Bar(data, options);
});
import PrometheusMetrics from './prometheus_metrics';
$(() => {
const prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
prometheusMetrics.loadActiveMetrics();
});
...@@ -44,7 +44,10 @@ ...@@ -44,7 +44,10 @@
type="button" type="button"
class="btn btn-xs btn-default" class="btn btn-xs btn-default"
> >
<loading-icon v-if="isRefreshing" /> <loading-icon
v-if="isRefreshing"
:inline="true"
/>
{{ s__("mrWidget|Refresh") }} {{ s__("mrWidget|Refresh") }}
</button> </button>
</div> </div>
......
<script>
const buttonVariants = [
'danger',
'primary',
'success',
'warning',
];
export default {
name: 'GlModal',
props: {
id: {
type: String,
required: false,
default: null,
},
headerTitleText: {
type: String,
required: false,
default: '',
},
footerPrimaryButtonVariant: {
type: String,
required: false,
default: 'primary',
validator: value => buttonVariants.indexOf(value) !== -1,
},
footerPrimaryButtonText: {
type: String,
required: false,
default: '',
},
},
methods: {
emitCancel(event) {
this.$emit('cancel', event);
},
emitSubmit(event) {
this.$emit('submit', event);
},
},
};
</script>
<template>
<div
:id="id"
class="modal fade"
tabindex="-1"
role="dialog"
>
<div
class="modal-dialog"
role="document"
>
<div class="modal-content">
<div class="modal-header">
<slot name="header">
<button
type="button"
class="close"
data-dismiss="modal"
:aria-label="s__('Modal|Close')"
@click="emitCancel($event)"
>
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">
<slot name="title">
{{ headerTitleText }}
</slot>
</h4>
</slot>
</div>
<div class="modal-body">
<slot></slot>
</div>
<div class="modal-footer">
<slot name="footer">
<button
type="button"
class="btn"
data-dismiss="modal"
@click="emitCancel($event)"
>
{{ s__('Modal|Cancel') }}
</button>
<button
type="button"
class="btn"
:class="`btn-${footerPrimaryButtonVariant}`"
data-dismiss="modal"
@click="emitSubmit($event)"
>
{{ footerPrimaryButtonText }}
</button>
</slot>
</div>
</div>
</div>
</div>
</template>
...@@ -7,17 +7,24 @@ module WebpackHelper ...@@ -7,17 +7,24 @@ module WebpackHelper
def webpack_controller_bundle_tags def webpack_controller_bundle_tags
bundles = [] bundles = []
segments = [*controller.controller_path.split('/'), controller.action_name].compact
until segments.empty? action = case controller.action_name
when 'create' then 'new'
when 'update' then 'edit'
else controller.action_name
end
route = [*controller.controller_path.split('/'), action].compact
until route.empty?
begin begin
asset_paths = gitlab_webpack_asset_paths("pages.#{segments.join('.')}", extension: 'js') asset_paths = gitlab_webpack_asset_paths("pages.#{route.join('.')}", extension: 'js')
bundles.unshift(*asset_paths) bundles.unshift(*asset_paths)
rescue Webpack::Rails::Manifest::EntryPointMissingError rescue Webpack::Rails::Manifest::EntryPointMissingError
# no bundle exists for this path # no bundle exists for this path
end end
segments.pop route.pop
end end
javascript_include_tag(*bundles) javascript_include_tag(*bundles)
......
...@@ -19,19 +19,10 @@ module Issues ...@@ -19,19 +19,10 @@ module Issues
# on rewriting notes (unfolding references) # on rewriting notes (unfolding references)
# #
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
# New issue tasks
#
@new_issue = create_new_issue @new_issue = create_new_issue
rewrite_notes update_new_issue
rewrite_issue_award_emoji update_old_issue
add_note_moved_from
# Old issue tasks
#
add_note_moved_to
close_issue
mark_as_moved
end end
notify_participants notify_participants
...@@ -41,6 +32,18 @@ module Issues ...@@ -41,6 +32,18 @@ module Issues
private private
def update_new_issue
rewrite_notes
rewrite_issue_award_emoji
add_note_moved_from
end
def update_old_issue
add_note_moved_to
close_issue
mark_as_moved
end
def create_new_issue def create_new_issue
new_params = { id: nil, iid: nil, label_ids: cloneable_label_ids, new_params = { id: nil, iid: nil, label_ids: cloneable_label_ids,
milestone_id: cloneable_milestone_id, milestone_id: cloneable_milestone_id,
......
...@@ -7,10 +7,9 @@ ...@@ -7,10 +7,9 @@
- build_path_proc = ->(scope) { admin_jobs_path(scope: scope) } - build_path_proc = ->(scope) { admin_jobs_path(scope: scope) }
= render "shared/builds/tabs", build_path_proc: build_path_proc, all_builds: @all_builds, scope: @scope = render "shared/builds/tabs", build_path_proc: build_path_proc, all_builds: @all_builds, scope: @scope
.nav-controls
- if @all_builds.running_or_pending.any? - if @all_builds.running_or_pending.any?
#stop-jobs-modal #stop-jobs-modal
.nav-controls
%button#stop-jobs-button.btn.btn-danger{ data: { toggle: 'modal', %button#stop-jobs-button.btn.btn-danger{ data: { toggle: 'modal',
target: '#stop-jobs-modal', target: '#stop-jobs-modal',
url: cancel_all_admin_jobs_path } } url: cancel_all_admin_jobs_path } }
......
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
- if @project.avatar? - if @project.avatar?
%hr %hr
= link_to _('Remove avatar'), project_avatar_path(@project), data: { confirm: _("Avatar will be removed. Are you sure?") }, method: :delete, class: "btn btn-danger btn-inverted" = link_to _('Remove avatar'), project_avatar_path(@project), data: { confirm: _("Avatar will be removed. Are you sure?") }, method: :delete, class: "btn btn-danger btn-inverted"
= f.submit 'Save changes', class: "btn btn-success" = f.submit 'Save changes', class: "btn btn-success js-btn-save-general-project-settings"
%section.settings.sharing-permissions.no-animate{ class: ('expanded' if expanded) } %section.settings.sharing-permissions.no-animate{ class: ('expanded' if expanded) }
.settings-header .settings-header
......
- @no_container = true - @no_container = true
- breadcrumb_title "CI / CD Charts" - breadcrumb_title "CI / CD Charts"
- page_title _("Charts"), _("Pipelines") - page_title _("Charts"), _("Pipelines")
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_d3')
= page_specific_javascript_bundle_tag('graphs')
%div{ class: container_class } %div{ class: container_class }
.sub-header-block .sub-header-block
......
- content_for :page_specific_javascripts do
= webpack_bundle_tag('pipelines_times')
%div %div
%p.light %p.light
= _("Commit duration in minutes for last 30 commits") = _("Commit duration in minutes for last 30 commits")
......
- content_for :page_specific_javascripts do
= webpack_bundle_tag('pipelines_charts')
%h4= _("Pipelines charts") %h4= _("Pipelines charts")
%p %p
&nbsp; &nbsp;
......
- content_for :page_specific_javascripts do
= webpack_bundle_tag('prometheus_metrics')
.row.prepend-top-default.append-bottom-default.prometheus-metrics-monitoring.js-prometheus-metrics-monitoring .row.prepend-top-default.append-bottom-default.prometheus-metrics-monitoring.js-prometheus-metrics-monitoring
.col-lg-3 .col-lg-3
%h4.prepend-top-0 %h4.prepend-top-0
......
...@@ -6,4 +6,4 @@ ...@@ -6,4 +6,4 @@
$(".project-edit-errors").html("#{escape_javascript(render('errors'))}"); $(".project-edit-errors").html("#{escape_javascript(render('errors'))}");
$('.save-project-loader').hide(); $('.save-project-loader').hide();
$('.project-edit-container').show(); $('.project-edit-container').show();
$('.edit-project .btn-save').enable(); $('.edit-project .js-btn-save-general-project-settings').enable();
...@@ -4,9 +4,6 @@ ...@@ -4,9 +4,6 @@
- page_description @user.bio - page_description @user.bio
- header_title @user.name, user_path(@user) - header_title @user.name, user_path(@user)
- @no_container = true - @no_container = true
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_d3'
= webpack_bundle_tag 'users'
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity") = auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity")
......
---
title: API endpoint for importing a project export
merge_request: 17025
author:
type: added
---
title: Fix 500 error when loading a merge request with an invalid comment
merge_request: 16795
author:
type: fixed
---
title: Update nokogiri to 1.8.2
merge_request: 16807
author:
type: security
---
title: Fix monaco editor features which were incompatable with GitLab CDN settings
merge_request: 17021
author:
type: fixed
---
title: Fix GitLab import leaving group_id on ProjectLabel
merge_request: 16877
author:
type: fixed
---
title: Hide CI secret variable values after saving
merge_request: 17044
author:
type: changed
---
title: Allows project rename after validation error
merge_request: 17150
author:
type: fixed
---
title: Asciidoc now support inter-document cross references between files in repository
merge_request: 17125
author: Turo Soisenniemi
type: changed
---
title: Fix forking projects when no restricted visibility levels are defined applicationwide
merge_request: 16881
author:
type: fixed
--- ---
title: Fix JIRA not working when a trailing slash is included title: Escape HTML entities in commit messages
merge_request: merge_request:
author: author:
type: fixed type: fixed
---
title: Validate user namespace before saving so that errors persist on model
merge_request:
author:
type: fixed
---
title: Fixed error 500 when removing an identity with synced attributes and visiting
the profile page
merge_request: 17054
author:
type: fixed
--- ---
title: LDAP Person no longer throws exception on invalid entry title: Show loading button inline in refresh button in MR widget
merge_request: merge_request:
author: author:
type: fixed type: fixed
---
title: Resolve PrepareUntrackedUploads PostgreSQL syntax error
merge_request: 17019
author:
type: fixed
---
title: Cleanup new branch/merge request form in issues
merge_request: 16854
author:
type: fixed
---
title: Add new modal Vue component
merge_request: 17108
author:
type: changed
...@@ -69,6 +69,7 @@ module Gitlab ...@@ -69,6 +69,7 @@ module Gitlab
# - Webhook URLs (:hook) # - Webhook URLs (:hook)
# - Sentry DSN (:sentry_dsn) # - Sentry DSN (:sentry_dsn)
# - Deploy keys (:key) # - Deploy keys (:key)
# - Secret variable values (:value)
config.filter_parameters += [/token$/, /password/, /secret/] config.filter_parameters += [/token$/, /password/, /secret/]
config.filter_parameters += %i( config.filter_parameters += %i(
certificate certificate
...@@ -80,6 +81,7 @@ module Gitlab ...@@ -80,6 +81,7 @@ module Gitlab
sentry_dsn sentry_dsn
trace trace
variables variables
value
) )
# Enable escaping HTML in JSON. # Enable escaping HTML in JSON.
......
...@@ -77,12 +77,9 @@ var config = { ...@@ -77,12 +77,9 @@ var config = {
notes: './notes/index.js', notes: './notes/index.js',
pdf_viewer: './blob/pdf_viewer.js', pdf_viewer: './blob/pdf_viewer.js',
pipelines: './pipelines/pipelines_bundle.js', pipelines: './pipelines/pipelines_bundle.js',
pipelines_charts: './pipelines/pipelines_charts.js',
pipelines_details: './pipelines/pipeline_details_bundle.js', pipelines_details: './pipelines/pipeline_details_bundle.js',
pipelines_times: './pipelines/pipelines_times.js',
profile: './profile/profile_bundle.js', profile: './profile/profile_bundle.js',
project_import_gl: './projects/project_import_gitlab_project.js', project_import_gl: './projects/project_import_gitlab_project.js',
prometheus_metrics: './prometheus_metrics',
protected_branches: './protected_branches', protected_branches: './protected_branches',
protected_tags: './protected_tags', protected_tags: './protected_tags',
registry_list: './registry/index.js', registry_list: './registry/index.js',
...@@ -98,7 +95,6 @@ var config = { ...@@ -98,7 +95,6 @@ var config = {
vue_merge_request_widget: './vue_merge_request_widget/index.js', vue_merge_request_widget: './vue_merge_request_widget/index.js',
test: './test.js', test: './test.js',
two_factor_auth: './two_factor_auth.js', two_factor_auth: './two_factor_auth.js',
users: './users/index.js',
webpack_runtime: './webpack.js', webpack_runtime: './webpack.js',
}, },
......
...@@ -43,6 +43,7 @@ following locations: ...@@ -43,6 +43,7 @@ following locations:
- [Pipeline Schedules](pipeline_schedules.md) - [Pipeline Schedules](pipeline_schedules.md)
- [Projects](projects.md) including setting Webhooks - [Projects](projects.md) including setting Webhooks
- [Project Access Requests](access_requests.md) - [Project Access Requests](access_requests.md)
- [Project import/export](project_import_export.md)
- [Project Members](members.md) - [Project Members](members.md)
- [Project Snippets](project_snippets.md) - [Project Snippets](project_snippets.md)
- [Protected Branches](protected_branches.md) - [Protected Branches](protected_branches.md)
......
# Project import API
[Introduced][ce-41899] in GitLab 10.6
[See also the project import/export documentation](../user/project/settings/import_export.md)
## Import a file
```http
POST /projects/import
```
| Attribute | Type | Required | Description |
| --------- | -------------- | -------- | ---------------------------------------- |
| `namespace` | integer/string | no | The ID or path of the namespace that the project will be imported to. Defaults to the current user's namespace |
| `file` | string | yes | The file to be uploaded |
| `path` | string | yes | Name and path for new project |
To upload a file from your filesystem, use the `--form` argument. This causes
cURL to post data using the header `Content-Type: multipart/form-data`.
The `file=` parameter must point to a file on your filesystem and be preceded
by `@`. For example:
```console
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "path=api-project" --form "file=@/path/to/file" https://gitlab.example.com/api/v4/projects/import
```
```json
{
"id": 1,
"description": null,
"name": "api-project",
"name_with_namespace": "Administrator / api-project",
"path": "api-project",
"path_with_namespace": "root/api-project",
"created_at": "2018-02-13T09:05:58.023Z",
"import_status": "scheduled"
}
```
## Import status
Get the status of an import.
```http
GET /projects/:id/import
```
| Attribute | Type | Required | Description |
| --------- | -------------- | -------- | ---------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
```console
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/import
```
Status can be one of `none`, `scheduled`, `failed`, `started`, or `finished`.
If the status is `failed`, it will include the import error message under `import_error`.
```json
{
"id": 1,
"description": "Itaque perspiciatis minima aspernatur corporis consequatur.",
"name": "Gitlab Test",
"name_with_namespace": "Gitlab Org / Gitlab Test",
"path": "gitlab-test",
"path_with_namespace": "gitlab-org/gitlab-test",
"created_at": "2017-08-29T04:36:44.383Z",
"import_status": "started"
}
```
[ce-41899]: https://gitlab.com/gitlab-org/gitlab-ce/issues/41899
...@@ -1330,6 +1330,10 @@ POST /projects/:id/housekeeping ...@@ -1330,6 +1330,10 @@ POST /projects/:id/housekeeping
Read more in the [Branches](branches.md) documentation. Read more in the [Branches](branches.md) documentation.
## Project Import/Export
Read more in the [Project import/export](project_import_export.md) documentation.
## Project members ## Project members
Read more in the [Project members](members.md) documentation. Read more in the [Project members](members.md) documentation.
...@@ -70,6 +70,8 @@ learn how to leverage its potential even more. ...@@ -70,6 +70,8 @@ learn how to leverage its potential even more.
- [Use SSH keys in your build environment](ssh_keys/README.md) - [Use SSH keys in your build environment](ssh_keys/README.md)
- [Trigger pipelines through the GitLab API](triggers/README.md) - [Trigger pipelines through the GitLab API](triggers/README.md)
- [Trigger pipelines on a schedule](../user/project/pipelines/schedules.md) - [Trigger pipelines on a schedule](../user/project/pipelines/schedules.md)
- [Kubernetes clusters](../user/project/clusters/index.md) - Integrate one or
more Kubernetes clusters to your project
## GitLab CI/CD for Docker ## GitLab CI/CD for Docker
......
# Components
## Contents
* [Dropdowns](#dropdowns)
* [Modals](#modals)
## Dropdowns
See also the [corresponding UX guide](../ux_guide/components.md#dropdowns).
### How to style a bootstrap dropdown
1. Use the HTML structure provided by the [docs][bootstrap-dropdowns]
1. Add a specific class to the top level `.dropdown` element
```Haml
.dropdown.my-dropdown
%button{ type: 'button', data: { toggle: 'dropdown' }, 'aria-haspopup': true, 'aria-expanded': false }
%span.dropdown-toggle-text
Toggle Dropdown
= icon('chevron-down')
%ul.dropdown-menu
%li
%a
item!
```
Or use the helpers
```Haml
.dropdown.my-dropdown
= dropdown_toggle('Toogle!', { toggle: 'dropdown' })
= dropdown_content
%li
%a
item!
```
[bootstrap-dropdowns]: https://getbootstrap.com/docs/3.3/javascript/#dropdowns
## Modals
See also the [corresponding UX guide](../ux_guide/components.md#modals).
We have a reusable Vue component for modals: [vue_shared/components/gl-modal.vue](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/vue_shared/components/gl-modal.vue)
Here is an example of how to use it:
```html
<gl-modal
id="dogs-out-modal"
:header-title-text="s__('ModalExample|Let the dogs out?')"
footer-primary-button-variant="danger"
:footer-primary-button-text="s__('ModalExample|Let them out')"
@submit="letOut(theDogs)"
>
{{ s__('ModalExample|You’re about to let the dogs out.') }}
</gl-modal>
```
![example modal](img/gl-modal.png)
# Dropdowns This page has moved [here](components.md#dropdowns).
## How to style a bootstrap dropdown
1. Use the HTML structure provided by the [docs][bootstrap-dropdowns]
1. Add a specific class to the top level `.dropdown` element
```Haml
.dropdown.my-dropdown
%button{ type: 'button', data: { toggle: 'dropdown' }, 'aria-haspopup': true, 'aria-expanded': false }
%span.dropdown-toggle-text
Toggle Dropdown
= icon('chevron-down')
%ul.dropdown-menu
%li
%a
item!
```
Or use the helpers
```Haml
.dropdown.my-dropdown
= dropdown_toggle('Toogle!', { toggle: 'dropdown' })
= dropdown_content
%li
%a
item!
```
[bootstrap-dropdowns]: https://getbootstrap.com/docs/3.3/javascript/#dropdowns
...@@ -21,6 +21,8 @@ Working with our frontend assets requires Node (v4.3 or greater) and Yarn ...@@ -21,6 +21,8 @@ Working with our frontend assets requires Node (v4.3 or greater) and Yarn
[jQuery][jquery] is used throughout the application's JavaScript, with [jQuery][jquery] is used throughout the application's JavaScript, with
[Vue.js][vue] for particularly advanced, dynamic elements. [Vue.js][vue] for particularly advanced, dynamic elements.
We also use [Axios][axios] to handle all of our network requests.
### Browser Support ### Browser Support
For our currently-supported browsers, see our [requirements][requirements]. For our currently-supported browsers, see our [requirements][requirements].
...@@ -77,8 +79,10 @@ Axios specific practices and gotchas. ...@@ -77,8 +79,10 @@ Axios specific practices and gotchas.
## [Icons](icons.md) ## [Icons](icons.md)
How we use SVG for our Icons. How we use SVG for our Icons.
## [Dropdowns](dropdowns.md) ## [Components](components.md)
How we use dropdowns.
How we use UI components.
--- ---
## Style Guides ## Style Guides
...@@ -122,6 +126,7 @@ The [externalization part of the guide](../i18n/externalization.md) explains the ...@@ -122,6 +126,7 @@ The [externalization part of the guide](../i18n/externalization.md) explains the
[webpack]: https://webpack.js.org/ [webpack]: https://webpack.js.org/
[jquery]: https://jquery.com/ [jquery]: https://jquery.com/
[vue]: http://vuejs.org/ [vue]: http://vuejs.org/
[axios]: https://github.com/axios/axios
[airbnb-js-style-guide]: https://github.com/airbnb/javascript [airbnb-js-style-guide]: https://github.com/airbnb/javascript
[scss-lint]: https://github.com/brigade/scss-lint [scss-lint]: https://github.com/brigade/scss-lint
[install]: ../../install/installation.md#4-node [install]: ../../install/installation.md#4-node
......
...@@ -27,6 +27,17 @@ Gitlab::Profiler.profile('/my-user') ...@@ -27,6 +27,17 @@ Gitlab::Profiler.profile('/my-user')
# Returns a RubyProf::Profile where 100 seconds is spent in UsersController#show # Returns a RubyProf::Profile where 100 seconds is spent in UsersController#show
``` ```
For routes that require authorization you will need to provide a user to
`Gitlab::Profiler`. You can do this like so:
```ruby
Gitlab::Profiler.profile('/gitlab-org/gitlab-test', user: User.first)
```
The user you provide will need to have a [personal access
token](https://docs.gitlab.com/ce/user/profile/personal_access_tokens.html) in
the GitLab instance.
Passing a `logger:` keyword argument to `Gitlab::Profiler.profile` will send Passing a `logger:` keyword argument to `Gitlab::Profiler.profile` will send
ActiveRecord and ActionController log output to that logger. Further options are ActiveRecord and ActionController log output to that logger. Further options are
documented with the method source. documented with the method source.
......
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