Commit 75206afb authored by Matija Čupić's avatar Matija Čupić

Merge branch 'master' into mc/fix/project-variables-scope-ee

parents c5727d53 02c15350
...@@ -146,7 +146,7 @@ stages: ...@@ -146,7 +146,7 @@ stages:
- export KNAPSACK_TEST_FILE_PATTERN="ee/spec/**{,/*/**}/*_spec.rb" KNAPSACK_GENERATE_REPORT=true CACHE_CLASSES=true - export KNAPSACK_TEST_FILE_PATTERN="ee/spec/**{,/*/**}/*_spec.rb" KNAPSACK_GENERATE_REPORT=true CACHE_CLASSES=true
- JOB_NAME=( $CI_JOB_NAME ) - JOB_NAME=( $CI_JOB_NAME )
- export CI_NODE_INDEX=${JOB_NAME[-2]} CI_NODE_TOTAL=${JOB_NAME[-1]} - export CI_NODE_INDEX=${JOB_NAME[-2]} CI_NODE_TOTAL=${JOB_NAME[-1]}
- export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report-ee.json - export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- cp ${EE_KNAPSACK_RSPEC_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH} - cp ${EE_KNAPSACK_RSPEC_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH}
- scripts/gitaly-test-spawn - scripts/gitaly-test-spawn
- knapsack rspec "-Ispec --color --format documentation --tag ~geo" - knapsack rspec "-Ispec --color --format documentation --tag ~geo"
...@@ -303,7 +303,7 @@ update-tests-metadata: ...@@ -303,7 +303,7 @@ update-tests-metadata:
- retry gem install fog-aws mime-types - retry gem install fog-aws mime-types
- scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json - scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json
- scripts/merge-reports ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/spinach-pg_node_*.json - scripts/merge-reports ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/spinach-pg_node_*.json
- scripts/merge-reports ${EE_KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*-ee.json - scripts/merge-reports ${EE_KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg-ee_node_*.json
- scripts/merge-reports ${FLAKY_RSPEC_SUITE_REPORT_PATH} rspec_flaky/all_*_*.json - scripts/merge-reports ${FLAKY_RSPEC_SUITE_REPORT_PATH} rspec_flaky/all_*_*.json
- '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH $KNAPSACK_SPINACH_SUITE_REPORT_PATH' - '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH $KNAPSACK_SPINACH_SUITE_REPORT_PATH'
- '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $EE_KNAPSACK_RSPEC_SUITE_REPORT_PATH' - '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $EE_KNAPSACK_RSPEC_SUITE_REPORT_PATH'
......
...@@ -10,7 +10,6 @@ import SearchAutocomplete from './search_autocomplete'; ...@@ -10,7 +10,6 @@ import SearchAutocomplete from './search_autocomplete';
import UsersSelect from './users_select'; import UsersSelect from './users_select';
import UserCallout from './user_callout'; import UserCallout from './user_callout';
import ZenMode from './zen_mode'; import ZenMode from './zen_mode';
import initCompareAutocomplete from './compare_autocomplete';
import initGeoInfoModal from 'ee/init_geo_info_modal'; // eslint-disable-line import/first import initGeoInfoModal from 'ee/init_geo_info_modal'; // eslint-disable-line import/first
import initGroupAnalytics from 'ee/init_group_analytics'; // eslint-disable-line import/first import initGroupAnalytics from 'ee/init_group_analytics'; // eslint-disable-line import/first
import initPathLocks from 'ee/path_locks'; // eslint-disable-line import/first import initPathLocks from 'ee/path_locks'; // eslint-disable-line import/first
...@@ -53,19 +52,6 @@ var Dispatcher; ...@@ -53,19 +52,6 @@ var Dispatcher;
}); });
}); });
function initBlobEE() {
const dataEl = document.getElementById('js-file-lock');
if (dataEl) {
const {
toggle_path,
path,
} = JSON.parse(dataEl.innerHTML);
initPathLocks(toggle_path, path);
}
}
switch (page) { switch (page) {
case 'projects:environments:metrics': case 'projects:environments:metrics':
import('./pages/projects/environments/metrics') import('./pages/projects/environments/metrics')
...@@ -86,7 +72,6 @@ var Dispatcher; ...@@ -86,7 +72,6 @@ var Dispatcher;
import('./pages/projects/milestones/show') import('./pages/projects/milestones/show')
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
new UserCallout();
break; break;
case 'groups:milestones:show': case 'groups:milestones:show':
import('./pages/groups/milestones/show') import('./pages/groups/milestones/show')
...@@ -185,14 +170,6 @@ var Dispatcher; ...@@ -185,14 +170,6 @@ var Dispatcher;
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
break; break;
case 'groups:epics:show':
new ZenMode();
break;
case 'groups:epics:index':
import(/* webpackChunkName: "ee_epics_show" */ 'ee/pages/epics')
.then(callDefault)
.catch(fail);
break;
case 'projects:compare:show': case 'projects:compare:show':
import('./pages/projects/compare/show') import('./pages/projects/compare/show')
.then(callDefault) .then(callDefault)
...@@ -229,24 +206,17 @@ var Dispatcher; ...@@ -229,24 +206,17 @@ var Dispatcher;
import('./pages/projects/merge_requests/creations/new') import('./pages/projects/merge_requests/creations/new')
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
new UserCallout();
case 'projects:merge_requests:creations:diffs': case 'projects:merge_requests:creations:diffs':
import('./pages/projects/merge_requests/creations/diffs') import('./pages/projects/merge_requests/creations/diffs')
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
shortcut_handler = true; shortcut_handler = true;
// ee-start
initApprovals();
// ee-end
break; break;
case 'projects:merge_requests:edit': case 'projects:merge_requests:edit':
import('./pages/projects/merge_requests/edit') import('./pages/projects/merge_requests/edit')
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
shortcut_handler = true; shortcut_handler = true;
// ee-start
initApprovals();
// ee-end
break; break;
case 'projects:tags:new': case 'projects:tags:new':
import('./pages/projects/tags/new') import('./pages/projects/tags/new')
...@@ -331,9 +301,6 @@ var Dispatcher; ...@@ -331,9 +301,6 @@ var Dispatcher;
break; break;
case 'projects:show': case 'projects:show':
shortcut_handler = true; shortcut_handler = true;
// ee-start
initGeoInfoModal();
// ee-end
break; break;
case 'projects:edit': case 'projects:edit':
import(/* webpackChunkName: "ee_projects_edit" */ 'ee/pages/projects/edit') import(/* webpackChunkName: "ee_projects_edit" */ 'ee/pages/projects/edit')
...@@ -415,14 +382,12 @@ var Dispatcher; ...@@ -415,14 +382,12 @@ var Dispatcher;
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
shortcut_handler = true; shortcut_handler = true;
initBlobEE();
break; break;
case 'projects:blame:show': case 'projects:blame:show':
import('./pages/projects/blame/show') import('./pages/projects/blame/show')
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
shortcut_handler = true; shortcut_handler = true;
initBlobEE();
break; break;
case 'groups:labels:new': case 'groups:labels:new':
import('./pages/groups/labels/new') import('./pages/groups/labels/new')
...@@ -485,26 +450,11 @@ var Dispatcher; ...@@ -485,26 +450,11 @@ var Dispatcher;
import('./pages/search/show') import('./pages/search/show')
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
new UserCallout();
break;
case 'projects:mirrors:show':
case 'projects:mirrors:update':
new UsersSelect();
break;
case 'admin:emails:show':
import(/* webpackChunkName: "ee_admin_emails_show" */ 'ee/pages/admin/emails/show').then(m => m.default()).catch(fail);
break;
case 'admin:audit_logs:index':
import(/* webpackChunkName: "ee_audit_logs" */ 'ee/pages/admin/audit_logs').then(m => m.default()).catch(fail);
break; break;
case 'projects:settings:repository:show': case 'projects:settings:repository:show':
import('./pages/projects/settings/repository/show') import('./pages/projects/settings/repository/show')
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
// ee-start
new UsersSelect();
new UserCallout();
// ee-end
break; break;
case 'projects:settings:ci_cd:show': case 'projects:settings:ci_cd:show':
import('./pages/projects/settings/ci_cd/show') import('./pages/projects/settings/ci_cd/show')
...@@ -552,11 +502,6 @@ var Dispatcher; ...@@ -552,11 +502,6 @@ var Dispatcher;
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
break; break;
case 'profiles:personal_access_tokens:index':
import('./pages/profiles/personal_access_tokens')
.then(callDefault)
.catch(fail);
break;
case 'projects:clusters:show': case 'projects:clusters:show':
case 'projects:clusters:update': case 'projects:clusters:update':
case 'projects:clusters:destroy': case 'projects:clusters:destroy':
...@@ -573,14 +518,6 @@ var Dispatcher; ...@@ -573,14 +518,6 @@ var Dispatcher;
import('./pages/dashboard/groups/index') import('./pages/dashboard/groups/index')
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
case 'admin:licenses:new':
import(/* webpackChunkName: "admin_licenses" */ 'ee/pages/admin/licenses/new').then(m => m.default()).catch(fail);
break;
case 'groups:analytics:show':
initGroupAnalytics();
break;
case 'groups:ldap_group_links:index':
initLDAPGroupsSelect();
break; break;
} }
switch (path[0]) { switch (path[0]) {
...@@ -616,11 +553,7 @@ var Dispatcher; ...@@ -616,11 +553,7 @@ var Dispatcher;
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
break; break;
case 'edit':
import(/* webpackChunkName: "ee_admin_groups_edit" */ 'ee/pages/admin/groups/edit').then(m => m.default()).catch(fail);
break;
} }
break; break;
case 'projects': case 'projects':
import('./pages/admin/projects') import('./pages/admin/projects')
...@@ -645,11 +578,6 @@ var Dispatcher; ...@@ -645,11 +578,6 @@ var Dispatcher;
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
break; break;
case 'geo_nodes':
import(/* webpackChunkName: 'geo_node_form' */ './geo/geo_node_form')
.then(geoNodeForm => geoNodeForm.default($('.js-geo-node-form')))
.catch(() => {});
break;
} }
break; break;
case 'profiles': case 'profiles':
...@@ -664,7 +592,9 @@ var Dispatcher; ...@@ -664,7 +592,9 @@ var Dispatcher;
shortcut_handler = true; shortcut_handler = true;
switch (path[1]) { switch (path[1]) {
case 'compare': case 'compare':
initCompareAutocomplete(); import('./pages/projects/compare')
.then(callDefault)
.catch(fail);
break; break;
case 'create': case 'create':
case 'new': case 'new':
...@@ -686,6 +616,81 @@ var Dispatcher; ...@@ -686,6 +616,81 @@ var Dispatcher;
new Shortcuts(); new Shortcuts();
} }
// EE-only route-based code
function initBlobEE() {
const dataEl = document.getElementById('js-file-lock');
if (dataEl) {
const {
toggle_path,
path,
} = JSON.parse(dataEl.innerHTML);
initPathLocks(toggle_path, path);
}
}
switch (page) {
case 'groups:epics:show':
new ZenMode();
break;
case 'groups:epics:index':
import(/* webpackChunkName: "ee_epics_index" */ 'ee/pages/epics')
.then(callDefault)
.catch(fail);
break;
case 'projects:milestones:show':
case 'search:show':
new UserCallout();
break;
case 'projects:merge_requests:creations:new':
new UserCallout();
initApprovals();
break;
case 'projects:merge_requests:creations:diffs':
case 'projects:merge_requests:edit':
initApprovals();
break;
case 'projects:show':
initGeoInfoModal();
break;
case 'projects:blob:show':
case 'projects:blame:show':
initBlobEE();
break;
case 'projects:mirrors:show':
case 'projects:mirrors:update':
new UsersSelect();
break;
case 'admin:emails:show':
import(/* webpackChunkName: "ee_admin_emails_show" */ 'ee/pages/admin/emails/show').then(m => m.default()).catch(fail);
break;
case 'admin:audit_logs:index':
import(/* webpackChunkName: "ee_audit_logs" */ 'ee/pages/admin/audit_logs').then(m => m.default()).catch(fail);
break;
case 'projects:settings:repository:show':
new UsersSelect();
new UserCallout();
break;
case 'admin:licenses:new':
import(/* webpackChunkName: "admin_licenses" */ 'ee/pages/admin/licenses/new').then(m => m.default()).catch(fail);
break;
case 'groups:analytics:show':
initGroupAnalytics();
break;
case 'groups:ldap_group_links:index':
initLDAPGroupsSelect();
break;
case 'admin:groups:edit':
import(/* webpackChunkName: "ee_admin_groups_edit" */ 'ee/pages/admin/groups/edit').then(m => m.default()).catch(fail);
break;
case 'admin:geo_nodes':
import(/* webpackChunkName: 'geo_node_form' */ './geo/geo_node_form')
.then(geoNodeForm => geoNodeForm.default($('.js-geo-node-form')))
.catch(() => {});
break;
}
if (document.querySelector('#peek')) { if (document.querySelector('#peek')) {
import('./performance_bar') import('./performance_bar')
.then(m => new m.default({ container: '#peek' })) // eslint-disable-line new-cap .then(m => new m.default({ container: '#peek' })) // eslint-disable-line new-cap
......
...@@ -5,13 +5,10 @@ import UsersSelect from './users_select'; ...@@ -5,13 +5,10 @@ import UsersSelect from './users_select';
import issueStatusSelect from './issue_status_select'; import issueStatusSelect from './issue_status_select';
import MilestoneSelect from './milestone_select'; import MilestoneSelect from './milestone_select';
import WeightSelect from 'ee/weight_select'; // eslint-disable-line import/first
export default () => { export default () => {
new UsersSelect(); new UsersSelect();
new LabelsSelect(); new LabelsSelect();
new MilestoneSelect(); new MilestoneSelect();
issueStatusSelect(); issueStatusSelect();
subscriptionSelect(); subscriptionSelect();
new WeightSelect();
}; };
import _ from 'underscore'; import _ from 'underscore';
import { __ } from '~/locale';
import axios from '~/lib/utils/axios_utils';
import Flash from '../flash'; import Flash from '../flash';
import AUTH_METHOD from './constants'; import AUTH_METHOD from './constants';
import { backOff } from '../lib/utils/common_utils'; import { backOff } from '../lib/utils/common_utils';
...@@ -145,22 +147,24 @@ export default class MirrorPull { ...@@ -145,22 +147,24 @@ export default class MirrorPull {
if (selectedAuthType === AUTH_METHOD.SSH && if (selectedAuthType === AUTH_METHOD.SSH &&
!$sshPublicKey.text().trim()) { !$sshPublicKey.text().trim()) {
this.$dropdownAuthType.disable(); this.$dropdownAuthType.disable();
$.ajax({
type: 'PUT', axios.put(projectMirrorAuthTypeEndpoint, JSON.stringify(authTypeData), {
url: projectMirrorAuthTypeEndpoint, headers: {
contentType: 'application/json; charset=utf-8', 'Content-Type': 'application/json; charset=utf-8',
data: JSON.stringify(authTypeData), },
}) })
.done((res) => { .then(({ data }) => {
// Show SSH public key container and fill in public key // Show SSH public key container and fill in public key
this.toggleAuthWell(selectedAuthType); this.toggleAuthWell(selectedAuthType);
this.toggleSSHAuthWellMessage(true); this.toggleSSHAuthWellMessage(true);
this.setSSHPublicKey(res.import_data_attributes.ssh_public_key); this.setSSHPublicKey(data.import_data_attributes.ssh_public_key);
})
.fail(() => { this.$wellAuthTypeChanging.addClass('hidden');
Flash('Something went wrong on our end.'); this.$dropdownAuthType.enable();
}) })
.always(() => { .catch(() => {
Flash(__('Something went wrong on our end.'));
this.$wellAuthTypeChanging.addClass('hidden'); this.$wellAuthTypeChanging.addClass('hidden');
this.$dropdownAuthType.enable(); this.$dropdownAuthType.enable();
}); });
......
...@@ -33,9 +33,6 @@ import flash from '../flash'; ...@@ -33,9 +33,6 @@ import flash from '../flash';
$('input[name="user[multi_file]"]').on('change', this.setNewRepoCookie); $('input[name="user[multi_file]"]').on('change', this.setNewRepoCookie);
$('#user_notification_email').on('change', this.submitForm); $('#user_notification_email').on('change', this.submitForm);
$('#user_notified_of_own_activity').on('change', this.submitForm); $('#user_notified_of_own_activity').on('change', this.submitForm);
$('.update-username').on('ajax:before', this.beforeUpdateUsername);
$('.update-username').on('ajax:complete', this.afterUpdateUsername);
$('.update-notifications').on('ajax:success', this.onUpdateNotifs);
this.form.on('submit', this.onSubmitForm); this.form.on('submit', this.onSubmitForm);
} }
...@@ -48,21 +45,6 @@ import flash from '../flash'; ...@@ -48,21 +45,6 @@ import flash from '../flash';
return this.saveForm(); return this.saveForm();
} }
beforeUpdateUsername() {
$('.loading-username', this).removeClass('hidden');
}
afterUpdateUsername() {
$('.loading-username', this).addClass('hidden');
$('button[type=submit]', this).enable();
}
onUpdateNotifs(e, data) {
return data.saved ?
flash(__('Notification settings saved'), 'notice') :
flash(__('Failed to save new settings'));
}
saveForm() { saveForm() {
const self = this; const self = this;
const formData = new FormData(this.form[0]); const formData = new FormData(this.form[0]);
......
...@@ -42,7 +42,7 @@ export default function initSettingsPanels() { ...@@ -42,7 +42,7 @@ export default function initSettingsPanels() {
if (location.hash) { if (location.hash) {
const $target = $(location.hash); const $target = $(location.hash);
if ($target.length && $target.hasClass('.settings')) { if ($target.length && $target.hasClass('settings')) {
expandSection($target); expandSection($target);
} }
} }
......
...@@ -72,7 +72,7 @@ ...@@ -72,7 +72,7 @@
{{ sprintf(__('Lock %{issuableDisplayName}'), { issuableDisplayName: issuableDisplayName }) }} {{ sprintf(__('Lock %{issuableDisplayName}'), { issuableDisplayName: issuableDisplayName }) }}
<button <button
v-if="isEditable" v-if="isEditable"
class="pull-right lock-edit btn btn-blank" class="pull-right lock-edit"
type="button" type="button"
@click.prevent="toggleForm" @click.prevent="toggleForm"
> >
......
import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetNotAllowed',
components: {
statusIcon,
},
template: `
<div class="mr-widget-body media">
<status-icon status="success" :show-disabled-button="true" />
<div class="media-body space-children">
<span class="bold">
Ready to be merged automatically.
Ask someone with write access to this repository to merge this request
</span>
</div>
</div>
`,
};
<script>
import StatusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetNotAllowed',
components: {
StatusIcon,
},
};
</script>
<template>
<div class="mr-widget-body media">
<status-icon
status="success"
:show-disabled-button="true"
/>
<div class="media-body space-children">
<span class="bold">
{{ s__(`mrWidget|Ready to be merged automatically.
Ask someone with write access to this repository to merge this request`) }}
</span>
</div>
</div>
</template>
import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetPipelineBlocked',
components: {
statusIcon,
},
template: `
<div class="mr-widget-body media">
<status-icon status="warning" :show-disabled-button="true" />
<div class="media-body space-children">
<span class="bold">
Pipeline blocked. The pipeline for this merge request requires a manual action to proceed
</span>
</div>
</div>
`,
};
<script>
import StatusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetPipelineBlocked',
components: {
StatusIcon,
},
};
</script>
<template>
<div class="mr-widget-body media">
<status-icon
status="warning"
:show-disabled-button="true"
/>
<div class="media-body space-children">
<span class="bold">
{{ s__(`mrWidget|Pipeline blocked.
The pipeline for this merge request requires a manual action to proceed`) }}
</span>
</div>
</div>
</template>
...@@ -25,11 +25,11 @@ export { default as ArchivedState } from './components/states/mr_widget_archived ...@@ -25,11 +25,11 @@ export { default as ArchivedState } from './components/states/mr_widget_archived
export { default as ConflictsState } from './components/states/mr_widget_conflicts.vue'; export { default as ConflictsState } from './components/states/mr_widget_conflicts.vue';
export { default as NothingToMergeState } from './components/states/mr_widget_nothing_to_merge'; export { default as NothingToMergeState } from './components/states/mr_widget_nothing_to_merge';
export { default as MissingBranchState } from './components/states/mr_widget_missing_branch.vue'; export { default as MissingBranchState } from './components/states/mr_widget_missing_branch.vue';
export { default as NotAllowedState } from './components/states/mr_widget_not_allowed'; export { default as NotAllowedState } from './components/states/mr_widget_not_allowed.vue';
export { default as ReadyToMergeState } from 'ee/vue_merge_request_widget/components/states/mr_widget_ready_to_merge'; export { default as ReadyToMergeState } from 'ee/vue_merge_request_widget/components/states/mr_widget_ready_to_merge';
export { default as SHAMismatchState } from './components/states/mr_widget_sha_mismatch'; export { default as SHAMismatchState } from './components/states/mr_widget_sha_mismatch';
export { default as UnresolvedDiscussionsState } from './components/states/mr_widget_unresolved_discussions'; export { default as UnresolvedDiscussionsState } from './components/states/mr_widget_unresolved_discussions';
export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked'; export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked.vue';
export { default as PipelineFailedState } from './components/states/mr_widget_pipeline_failed'; export { default as PipelineFailedState } from './components/states/mr_widget_pipeline_failed';
export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds.vue'; export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds.vue';
export { default as RebaseState } from './components/states/mr_widget_rebase.vue'; export { default as RebaseState } from './components/states/mr_widget_rebase.vue';
......
...@@ -197,11 +197,18 @@ ...@@ -197,11 +197,18 @@
margin-left: 0; margin-left: 0;
} }
a.edit-link:not([href]):hover {
color: rgba($avatar-border, .2);
}
.lock-edit, // uses same style, different js behaviour
.edit-link { .edit-link {
@extend .btn-blank;
color: $gl-text-color; color: $gl-text-color;
&:not([href]):hover { &:hover {
color: rgba($avatar-border, .2); text-decoration: underline;
color: $md-link-color;
} }
} }
} }
......
...@@ -126,8 +126,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -126,8 +126,7 @@ class Projects::IssuesController < Projects::ApplicationController
end end
def referenced_merge_requests def referenced_merge_requests
@merge_requests = @issue.referenced_merge_requests(current_user) @merge_requests, @closed_by_merge_requests = ::Issues::FetchReferencedMergeRequestsService.new(project, current_user).execute(issue)
@closed_by_merge_requests = @issue.closed_by_merge_requests(current_user)
respond_to do |format| respond_to do |format|
format.json do format.json do
......
...@@ -160,7 +160,7 @@ module DiffHelper ...@@ -160,7 +160,7 @@ module DiffHelper
end end
def diff_file_changed_icon(diff_file) def diff_file_changed_icon(diff_file)
if diff_file.deleted_file? || diff_file.renamed_file? if diff_file.deleted_file?
"file-deletion" "file-deletion"
elsif diff_file.new_file? elsif diff_file.new_file?
"file-addition" "file-addition"
......
...@@ -298,6 +298,10 @@ module ProjectsHelper ...@@ -298,6 +298,10 @@ module ProjectsHelper
nav_tabs << :pipelines nav_tabs << :pipelines
end end
if project.external_issue_tracker
nav_tabs << :external_issue_tracker
end
tab_ability_map.each do |tab, ability| tab_ability_map.each do |tab, ability|
if can?(current_user, ability, project) if can?(current_user, ability, project)
nav_tabs << tab nav_tabs << tab
......
...@@ -116,6 +116,10 @@ class Commit ...@@ -116,6 +116,10 @@ class Commit
raw.id raw.id
end end
def project_id
project.id
end
def ==(other) def ==(other)
other.is_a?(self.class) && raw == other.raw other.is_a?(self.class) && raw == other.raw
end end
......
...@@ -317,7 +317,7 @@ class Member < ActiveRecord::Base ...@@ -317,7 +317,7 @@ class Member < ActiveRecord::Base
end end
def notification_setting def notification_setting
@notification_setting ||= user.notification_settings_for(source) @notification_setting ||= user&.notification_settings_for(source)
end end
def notifiable?(type, opts = {}) def notifiable?(type, opts = {})
......
...@@ -61,11 +61,8 @@ module Network ...@@ -61,11 +61,8 @@ module Network
@reserved[i] = [] @reserved[i] = []
end end
# n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37436 commits_sort_by_ref.each do |commit|
Gitlab::GitalyClient.allow_n_plus_1_calls do place_chain(commit)
commits_sort_by_ref.each do |commit|
place_chain(commit)
end
end end
# find parent spaces for not overlap lines # find parent spaces for not overlap lines
......
...@@ -10,6 +10,8 @@ class JiraService < IssueTrackerService ...@@ -10,6 +10,8 @@ class JiraService < IssueTrackerService
before_update :reset_password before_update :reset_password
alias_method :project_url, :url
# This is confusing, but JiraService does not really support these events. # This is confusing, but JiraService does not really support these events.
# The values here are required to display correct options in the service # The values here are required to display correct options in the service
# configuration screen. # configuration screen.
......
...@@ -6,18 +6,14 @@ class DeleteMergedBranchesService < BaseService ...@@ -6,18 +6,14 @@ class DeleteMergedBranchesService < BaseService
def execute def execute
raise Gitlab::Access::AccessDeniedError unless can?(current_user, :push_code, project) raise Gitlab::Access::AccessDeniedError unless can?(current_user, :push_code, project)
# n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37438 branches = project.repository.merged_branch_names
Gitlab::GitalyClient.allow_n_plus_1_calls do # Prevent deletion of branches relevant to open merge requests
branches = project.repository.branch_names branches -= merge_request_branch_names
branches = branches.select { |branch| project.repository.merged_to_root_ref?(branch) } # Prevent deletion of protected branches
# Prevent deletion of branches relevant to open merge requests branches = branches.reject { |branch| ProtectedBranch.protected?(project, branch) }
branches -= merge_request_branch_names
# Prevent deletion of protected branches
branches = branches.reject { |branch| ProtectedBranch.protected?(project, branch) }
branches.each do |branch| branches.each do |branch|
DeleteBranchService.new(project, current_user).execute(branch) DeleteBranchService.new(project, current_user).execute(branch)
end
end end
end end
......
module Issues
class FetchReferencedMergeRequestsService < Issues::BaseService
def execute(issue)
referenced_merge_requests = issue.referenced_merge_requests(current_user)
referenced_merge_requests = Gitlab::IssuableSorter.sort(project, referenced_merge_requests) { |i| i.iid.to_s }
closed_by_merge_requests = issue.closed_by_merge_requests(current_user)
closed_by_merge_requests = Gitlab::IssuableSorter.sort(project, closed_by_merge_requests) { |i| i.iid.to_s }
[referenced_merge_requests, closed_by_merge_requests]
end
end
end
...@@ -11,6 +11,7 @@ module Members ...@@ -11,6 +11,7 @@ module Members
Member.transaction do Member.transaction do
unassign_issues_and_merge_requests(member) unless member.invite? unassign_issues_and_merge_requests(member) unless member.invite?
member.notification_setting&.destroy
member.destroy member.destroy
end end
......
...@@ -141,6 +141,19 @@ ...@@ -141,6 +141,19 @@
= link_to project_milestones_path(@project), title: 'Milestones' do = link_to project_milestones_path(@project), title: 'Milestones' do
%span %span
Milestones Milestones
- if project_nav_tab? :external_issue_tracker
= nav_link do
- issue_tracker = @project.external_issue_tracker
= link_to issue_tracker.issue_tracker_path, class: 'shortcuts-external_tracker' do
.nav-icon-container
= sprite_icon('issue-external')
%span.nav-item-name
= issue_tracker.title
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(html_options: { class: "fly-out-top-item" } ) do
= link_to issue_tracker.issue_tracker_path do
%strong.fly-out-top-item-name
= issue_tracker.title
- if project_nav_tab? :merge_requests - if project_nav_tab? :merge_requests
= nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :labels, :milestones]) do = nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :labels, :milestones]) do
......
---
title: 'Geo: Reset force_redownload flag after successful sync'
merge_request:
author:
type: fixed
---
title: "[Geo] Fix redownload repository recovery when there is not local repo at all"
merge_request:
author:
type: fixed
---
title: Fix the background_upload configuration being ignored.
merge_request: 4507
author:
type: fixed
---
title: Bump Geo JWT timeout from 1 minute to 10 minutes
merge_request:
author:
type: performance
---
title: Group MRs on issue page by project and namespace.
merge_request: 8494
author: Jeff Stubler
---
title: Display a link to external issue tracker when enabled
merge_request:
author:
type: changed
---
title: Render modified icon for moved file in changes dropdown
merge_request:
author:
type: fixed
---
title: Remove user notification settings for groups and projects when user leaves
merge_request: 16906
author: Jacopo Beschi @jacopo-beschi
type: fixed
---
title: Fix settings panels not expanding when fragment hash linked
merge_request: 17074
author:
type: fixed
---
title: 'API: Get references a commit is pushed to'
merge_request: 15026
author: Robert Schilling
type: added
---
title: Allow including custom attributes in API responses
merge_request: 16526
author: Markus Koller
type: changed
---
title: Resolve PrepareUntrackedUploads PostgreSQL syntax error
merge_request: 17019
author:
type: fixed
---
title: LDAP Person no longer throws exception on invalid entry
merge_request:
author:
type: fixed
...@@ -348,9 +348,9 @@ Settings.artifacts['storage_path'] = Settings.absolute(Settings.artifacts.values ...@@ -348,9 +348,9 @@ Settings.artifacts['storage_path'] = Settings.absolute(Settings.artifacts.values
Settings.artifacts['path'] = Settings.artifacts['storage_path'] Settings.artifacts['path'] = Settings.artifacts['storage_path']
Settings.artifacts['max_size'] ||= 100 # in megabytes Settings.artifacts['max_size'] ||= 100 # in megabytes
Settings.artifacts['object_store'] ||= Settingslogic.new({}) Settings.artifacts['object_store'] ||= Settingslogic.new({})
Settings.artifacts['object_store']['enabled'] ||= false Settings.artifacts['object_store']['enabled'] = false if Settings.artifacts['object_store']['enabled'].nil?
Settings.artifacts['object_store']['remote_directory'] ||= nil Settings.artifacts['object_store']['remote_directory'] ||= nil
Settings.artifacts['object_store']['background_upload'] ||= true Settings.artifacts['object_store']['background_upload'] = true if Settings.artifacts['object_store']['background_upload'].nil?
# Convert upload connection settings to use string keys, to make Fog happy # Convert upload connection settings to use string keys, to make Fog happy
Settings.artifacts['object_store']['connection']&.deep_stringify_keys! Settings.artifacts['object_store']['connection']&.deep_stringify_keys!
...@@ -394,9 +394,9 @@ Settings['lfs'] ||= Settingslogic.new({}) ...@@ -394,9 +394,9 @@ Settings['lfs'] ||= Settingslogic.new({})
Settings.lfs['enabled'] = true if Settings.lfs['enabled'].nil? Settings.lfs['enabled'] = true if Settings.lfs['enabled'].nil?
Settings.lfs['storage_path'] = Settings.absolute(Settings.lfs['storage_path'] || File.join(Settings.shared['path'], "lfs-objects")) Settings.lfs['storage_path'] = Settings.absolute(Settings.lfs['storage_path'] || File.join(Settings.shared['path'], "lfs-objects"))
Settings.lfs['object_store'] ||= Settingslogic.new({}) Settings.lfs['object_store'] ||= Settingslogic.new({})
Settings.lfs['object_store']['enabled'] ||= false Settings.lfs['object_store']['enabled'] = false if Settings.lfs['object_store']['enabled'].nil?
Settings.lfs['object_store']['remote_directory'] ||= nil Settings.lfs['object_store']['remote_directory'] ||= nil
Settings.lfs['object_store']['background_upload'] ||= true Settings.lfs['object_store']['background_upload'] = true if Settings.lfs['object_store']['background_upload'].nil?
# Convert upload connection settings to use string keys, to make Fog happy # Convert upload connection settings to use string keys, to make Fog happy
Settings.lfs['object_store']['connection']&.deep_stringify_keys! Settings.lfs['object_store']['connection']&.deep_stringify_keys!
...@@ -407,19 +407,12 @@ Settings['uploads'] ||= Settingslogic.new({}) ...@@ -407,19 +407,12 @@ Settings['uploads'] ||= Settingslogic.new({})
Settings.uploads['storage_path'] = Settings.absolute(Settings.uploads['storage_path'] || 'public') Settings.uploads['storage_path'] = Settings.absolute(Settings.uploads['storage_path'] || 'public')
Settings.uploads['base_dir'] = Settings.uploads['base_dir'] || 'uploads/-/system' Settings.uploads['base_dir'] = Settings.uploads['base_dir'] || 'uploads/-/system'
Settings.uploads['object_store'] ||= Settingslogic.new({}) Settings.uploads['object_store'] ||= Settingslogic.new({})
Settings.uploads['object_store']['enabled'] ||= false Settings.uploads['object_store']['enabled'] = false if Settings.uploads['object_store']['enabled'].nil?
Settings.uploads['object_store']['remote_directory'] ||= 'uploads' Settings.uploads['object_store']['remote_directory'] ||= 'uploads'
Settings.uploads['object_store']['background_upload'] ||= true Settings.uploads['object_store']['background_upload'] = true if Settings.uploads['object_store']['background_upload'].nil?
# Convert upload connection settings to use string keys, to make Fog happy # Convert upload connection settings to use string keys, to make Fog happy
Settings.uploads['object_store']['connection']&.deep_stringify_keys! Settings.uploads['object_store']['connection']&.deep_stringify_keys!
#
# Uploads
#
Settings['uploads'] ||= Settingslogic.new({})
Settings.uploads['storage_path'] = Settings.absolute(Settings.uploads['storage_path'] || 'public')
Settings.uploads['base_dir'] = Settings.uploads['base_dir'] || 'uploads/-/system'
# #
# Mattermost # Mattermost
# #
......
#
# Monkey patching the https support for private urls
# See https://gitlab.com/gitlab-org/gitlab-ee/issues/4879
#
module Fog
module Storage
class GoogleXML
class File < Fog::Model
module MonkeyPatch
def url(expires)
requires :key
collection.get_https_url(key, expires)
end
end
prepend MonkeyPatch
end
end
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class SchedulePopulateUntrackedUploadsIfNeeded < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
FOLLOW_UP_MIGRATION = 'PopulateUntrackedUploads'.freeze
class UntrackedFile < ActiveRecord::Base
include EachBatch
self.table_name = 'untracked_files_for_uploads'
end
def up
if table_exists?(:untracked_files_for_uploads)
process_or_remove_table
end
end
def down
# nothing
end
private
def process_or_remove_table
if UntrackedFile.all.empty?
drop_temp_table
else
schedule_populate_untracked_uploads_jobs
end
end
def drop_temp_table
drop_table(:untracked_files_for_uploads, if_exists: true)
end
def schedule_populate_untracked_uploads_jobs
say "Scheduling #{FOLLOW_UP_MIGRATION} background migration jobs since there are rows in untracked_files_for_uploads."
bulk_queue_background_migration_jobs_by_range(
UntrackedFile, FOLLOW_UP_MIGRATION)
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20180206200543) do ActiveRecord::Schema.define(version: 20180208183958) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
......
...@@ -54,6 +54,7 @@ following locations: ...@@ -54,6 +54,7 @@ following locations:
- [Repositories](repositories.md) - [Repositories](repositories.md)
- [Repository Files](repository_files.md) - [Repository Files](repository_files.md)
- [Runners](runners.md) - [Runners](runners.md)
- [Search](search.md)
- [Services](services.md) - [Services](services.md)
- [Settings](settings.md) - [Settings](settings.md)
- [Sidekiq metrics](sidekiq_metrics.md) - [Sidekiq metrics](sidekiq_metrics.md)
......
...@@ -198,6 +198,41 @@ Example response: ...@@ -198,6 +198,41 @@ Example response:
} }
``` ```
## Get references a commit is pushed to
> [Introduced][ce-15026] in GitLab 10.6
Get all references (from branches or tags) a commit is pushed to.
The pagination parameters `page` and `per_page` can be used to restrict the list of references.
```
GET /projects/:id/repository/commits/:sha/refs
```
Parameters:
| 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
| `sha` | string | yes | The commit hash |
| `type` | string | no | The scope of commits. Possible values `branch`, `tag`, `all`. Default is `all`. |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/repository/commits/5937ac0a7beb003549fc5fd26fc247adbce4a52e/refs?type=all"
```
Example response:
```json
[
{"type": "branch", "name": "'test'"},
{"type": "branch", "name": "add-balsamiq-file"},
{"type": "branch", "name": "wip"},
{"type": "tag", "name": "v1.1.0"}
]
```
## Cherry pick a commit ## Cherry pick a commit
> [Introduced][ce-8047] in GitLab 8.15. > [Introduced][ce-8047] in GitLab 8.15.
...@@ -500,3 +535,4 @@ Example response: ...@@ -500,3 +535,4 @@ Example response:
[ce-6096]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6096 "Multi-file commit" [ce-6096]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6096 "Multi-file commit"
[ce-8047]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8047 [ce-8047]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8047
[ce-15026]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15026
...@@ -15,6 +15,7 @@ Parameters: ...@@ -15,6 +15,7 @@ Parameters:
| `order_by` | string | no | Order groups by `name` or `path`. Default is `name` | | `order_by` | string | no | Order groups by `name` or `path`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` | | `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
| `statistics` | boolean | no | Include group statistics (admins only) | | `statistics` | boolean | no | Include group statistics (admins only) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `owned` | boolean | no | Limit to groups owned by the current user | | `owned` | boolean | no | Limit to groups owned by the current user |
``` ```
...@@ -98,6 +99,7 @@ Parameters: ...@@ -98,6 +99,7 @@ Parameters:
| `order_by` | string | no | Order groups by `name` or `path`. Default is `name` | | `order_by` | string | no | Order groups by `name` or `path`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` | | `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
| `statistics` | boolean | no | Include group statistics (admins only) | | `statistics` | boolean | no | Include group statistics (admins only) |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `owned` | boolean | no | Limit to groups owned by the current user | | `owned` | boolean | no | Limit to groups owned by the current user |
``` ```
...@@ -145,6 +147,7 @@ Parameters: ...@@ -145,6 +147,7 @@ Parameters:
| `simple` | boolean | no | Return only the ID, URL, name, and path of each project | | `simple` | boolean | no | Return only the ID, URL, name, and path of each project |
| `owned` | boolean | no | Limit by projects owned by the current user | | `owned` | boolean | no | Limit by projects owned by the current user |
| `starred` | boolean | no | Limit by projects starred by the current user | | `starred` | boolean | no | Limit by projects starred by the current user |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
Example response: Example response:
...@@ -204,6 +207,7 @@ Parameters: ...@@ -204,6 +207,7 @@ Parameters:
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user | | `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
```bash ```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/4 curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/4
......
...@@ -39,7 +39,7 @@ Example response: ...@@ -39,7 +39,7 @@ Example response:
"path": "group1", "path": "group1",
"kind": "group" "kind": "group"
"full_path": "group1", "full_path": "group1",
"parent_id": "null", "parent_id": null,
"members_count_with_descendants": 2, "members_count_with_descendants": 2,
"plan": "bronze" "plan": "bronze"
}, },
...@@ -49,7 +49,7 @@ Example response: ...@@ -49,7 +49,7 @@ Example response:
"path": "bar", "path": "bar",
"kind": "group", "kind": "group",
"full_path": "foo/bar", "full_path": "foo/bar",
"parent_id": "9", "parent_id": 9,
"members_count_with_descendants": 5 "members_count_with_descendants": 5
} }
] ]
...@@ -85,7 +85,7 @@ Example response: ...@@ -85,7 +85,7 @@ Example response:
"path": "twitter", "path": "twitter",
"kind": "group", "kind": "group",
"full_path": "twitter", "full_path": "twitter",
"parent_id": "null", "parent_id": null,
"members_count_with_descendants": 2 "members_count_with_descendants": 2
} }
] ]
...@@ -118,7 +118,7 @@ Example response: ...@@ -118,7 +118,7 @@ Example response:
"path": "group1", "path": "group1",
"kind": "group", "kind": "group",
"full_path": "group1", "full_path": "group1",
"parent_id": "null", "parent_id": null,
"members_count_with_descendants": 2 "members_count_with_descendants": 2
} }
``` ```
...@@ -138,7 +138,7 @@ Example response: ...@@ -138,7 +138,7 @@ Example response:
"path": "group1", "path": "group1",
"kind": "group", "kind": "group",
"full_path": "group1", "full_path": "group1",
"parent_id": "null", "parent_id": null,
"members_count_with_descendants": 2 "members_count_with_descendants": 2
} }
``` ```
...@@ -37,6 +37,7 @@ GET /projects ...@@ -37,6 +37,7 @@ GET /projects
| `membership` | boolean | no | Limit by projects that the current user is a member of | | `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user | | `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics | | `statistics` | boolean | no | Include project statistics |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature | | `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature | | `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
...@@ -222,6 +223,7 @@ GET /users/:user_id/projects ...@@ -222,6 +223,7 @@ GET /users/:user_id/projects
| `membership` | boolean | no | Limit by projects that the current user is a member of | | `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user | | `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics | | `statistics` | boolean | no | Include project statistics |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature | | `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature | | `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
...@@ -390,6 +392,7 @@ GET /projects/:id ...@@ -390,6 +392,7 @@ GET /projects/:id
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `statistics` | boolean | no | Include project statistics | | `statistics` | boolean | no | Include project statistics |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
```json ```json
{ {
...@@ -674,6 +677,7 @@ GET /projects/:id/forks ...@@ -674,6 +677,7 @@ GET /projects/:id/forks
| `membership` | boolean | no | Limit by projects that the current user is a member of | | `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user | | `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics | | `statistics` | boolean | no | Include project statistics |
| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature | | `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature | | `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
......
...@@ -302,7 +302,8 @@ Example response: ...@@ -302,7 +302,8 @@ Example response:
"filename": "home.md", "filename": "home.md",
"id": null, "id": null,
"ref": "master", "ref": "master",
"startline": 5 "startline": 5,
"project_id": 6
} }
] ]
``` ```
...@@ -334,7 +335,8 @@ Example response: ...@@ -334,7 +335,8 @@ Example response:
"authored_date": "2013-02-18T22:02:54.000Z", "authored_date": "2013-02-18T22:02:54.000Z",
"committer_name": "angus croll", "committer_name": "angus croll",
"committer_email": "anguscroll@gmail.com", "committer_email": "anguscroll@gmail.com",
"committed_date": "2013-02-18T22:02:54.000Z" "committed_date": "2013-02-18T22:02:54.000Z",
"project_id": 6
} }
] ]
``` ```
...@@ -358,7 +360,8 @@ Example response: ...@@ -358,7 +360,8 @@ Example response:
"filename": "README.md", "filename": "README.md",
"id": null, "id": null,
"ref": "master", "ref": "master",
"startline": 46 "startline": 46,
"project_id": 6
} }
] ]
``` ```
...@@ -603,7 +606,8 @@ Example response: ...@@ -603,7 +606,8 @@ Example response:
"filename": "home.md", "filename": "home.md",
"id": null, "id": null,
"ref": "master", "ref": "master",
"startline": 5 "startline": 5,
"project_id": 6
} }
] ]
``` ```
...@@ -635,7 +639,8 @@ Example response: ...@@ -635,7 +639,8 @@ Example response:
"authored_date": "2013-02-18T22:02:54.000Z", "authored_date": "2013-02-18T22:02:54.000Z",
"committer_name": "angus croll", "committer_name": "angus croll",
"committer_email": "anguscroll@gmail.com", "committer_email": "anguscroll@gmail.com",
"committed_date": "2013-02-18T22:02:54.000Z" "committed_date": "2013-02-18T22:02:54.000Z",
"project_id": 6
} }
] ]
``` ```
...@@ -659,7 +664,8 @@ Example response: ...@@ -659,7 +664,8 @@ Example response:
"filename": "README.md", "filename": "README.md",
"id": null, "id": null,
"ref": "master", "ref": "master",
"startline": 46 "startline": 46,
"project_id": 6
} }
] ]
``` ```
...@@ -901,7 +907,8 @@ Example response: ...@@ -901,7 +907,8 @@ Example response:
"filename": "home.md", "filename": "home.md",
"id": null, "id": null,
"ref": "master", "ref": "master",
"startline": 5 "startline": 5,
"project_id": 6
} }
] ]
``` ```
...@@ -931,7 +938,8 @@ Example response: ...@@ -931,7 +938,8 @@ Example response:
"authored_date": "2013-02-18T22:02:54.000Z", "authored_date": "2013-02-18T22:02:54.000Z",
"committer_name": "angus croll", "committer_name": "angus croll",
"committer_email": "anguscroll@gmail.com", "committer_email": "anguscroll@gmail.com",
"committed_date": "2013-02-18T22:02:54.000Z" "committed_date": "2013-02-18T22:02:54.000Z",
"project_id": 6
} }
] ]
``` ```
...@@ -953,7 +961,8 @@ Example response: ...@@ -953,7 +961,8 @@ Example response:
"filename": "README.md", "filename": "README.md",
"id": null, "id": null,
"ref": "master", "ref": "master",
"startline": 46 "startline": 46,
"project_id": 6
} }
] ]
``` ```
......
...@@ -167,6 +167,12 @@ You can filter by [custom attributes](custom_attributes.md) with: ...@@ -167,6 +167,12 @@ You can filter by [custom attributes](custom_attributes.md) with:
GET /users?custom_attributes[key]=value&custom_attributes[other_key]=other_value GET /users?custom_attributes[key]=value&custom_attributes[other_key]=other_value
``` ```
You can include the users' [custom attributes](custom_attributes.md) in the response with:
```
GET /users?with_custom_attributes=true
```
## Single user ## Single user
Get a single user. Get a single user.
...@@ -248,6 +254,12 @@ Parameters: ...@@ -248,6 +254,12 @@ Parameters:
} }
``` ```
You can include the user's [custom attributes](custom_attributes.md) in the response with:
```
GET /users/:id?with_custom_attributes=true
```
## User creation ## User creation
Creates a new user. Note only administrators can create new users. Either `password` or `reset_password` should be specified (`reset_password` takes priority). Creates a new user. Note only administrators can create new users. Either `password` or `reset_password` should be specified (`reset_password` takes priority).
......
# Query Count Limits # Query Count Limits
Each controller or API endpoint is allowed to execute up to 100 SQL queries. In Each controller or API endpoint is allowed to execute up to 100 SQL queries and
a production environment we'll only log an error in case this threshold is in test environments we'll raise an error when this threshold is exceeded.
exceeded, but in a test environment we'll raise an error instead.
## Solving Failing Tests ## Solving Failing Tests
......
...@@ -17,6 +17,9 @@ would be `process_something`. If you're not sure what queue a worker uses, ...@@ -17,6 +17,9 @@ would be `process_something`. If you're not sure what queue a worker uses,
you can find it using `SomeWorker.queue`. There is almost never a reason to you can find it using `SomeWorker.queue`. There is almost never a reason to
manually override the queue name using `sidekiq_options queue: :some_queue`. manually override the queue name using `sidekiq_options queue: :some_queue`.
You must always add any new queues to `app/workers/all_queues.yml` otherwise
your worker will not run.
## Queue Namespaces ## Queue Namespaces
While different workers cannot share a queue, they can share a queue namespace. While different workers cannot share a queue, they can share a queue namespace.
......
...@@ -395,27 +395,29 @@ data before running `pg_basebackup`. ...@@ -395,27 +395,29 @@ data before running `pg_basebackup`.
gitlab-ctl replicate-geo-database --slot-name=secondary_example --host=1.2.3.4 gitlab-ctl replicate-geo-database --slot-name=secondary_example --host=1.2.3.4
``` ```
When prompted, enter the password you set up for the `gitlab_replicator` When prompted, enter the _plaintext_ password you set up for the `gitlab_replicator`
user in the first step. user in the first step.
This command also takes a number of additional options. You can use `--help` This command also takes a number of additional options. You can use `--help`
to list them all, but here are a couple of tips: to list them all, but here are a couple of tips:
- If PostgreSQL is listening on a non-standard port, add `--port=` as well. - If PostgreSQL is listening on a non-standard port, add `--port=` as well.
- If your database is too large to be transferred in 30 minutes, you will need - If your database is too large to be transferred in 30 minutes, you will need
to increase the timeout, e.g., `--backup-timeout=3600` if you expect the to increase the timeout, e.g., `--backup-timeout=3600` if you expect the
initial replication to take under an hour. initial replication to take under an hour.
- Pass `--sslmode=disable` to skip PostgreSQL TLS authentication altogether - Pass `--sslmode=disable` to skip PostgreSQL TLS authentication altogether
(e.g., you know the network path is secure, or you are using a site-to-site (e.g., you know the network path is secure, or you are using a site-to-site
VPN). This is **not** safe over the public Internet! VPN). This is **not** safe over the public Internet!
- You can read more details about each `sslmode` in the - You can read more details about each `sslmode` in the
[PostgreSQL documentation](https://www.postgresql.org/docs/9.6/static/libpq-ssl.html#LIBPQ-SSL-PROTECTION); [PostgreSQL documentation](https://www.postgresql.org/docs/9.6/static/libpq-ssl.html#LIBPQ-SSL-PROTECTION);
the instructions above are carefully written to ensure protection against the instructions above are carefully written to ensure protection against
both passive eavesdroppers and active "man-in-the-middle" attackers. both passive eavesdroppers and active "man-in-the-middle" attackers.
- Change the `--slot-name` to the name of the replication slot - Change the `--slot-name` to the name of the replication slot
to be used on the primary database. The script will attempt to create the to be used on the primary database. The script will attempt to create the
replication slot automatically if it does not exist. replication slot automatically if it does not exist.
- If you're repurposing an old server into a Geo secondary, you'll need to - If you're repurposing an old server into a Geo secondary, you'll need to
add `--force` to the command line. add `--force` to the command line.
- When not in a production machine you can disable backup step if you
really sure this is what you want by adding `--skip-backup`
1. Verify that the secondary is configured correctly and that the primary is 1. Verify that the secondary is configured correctly and that the primary is
reachable: reachable:
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
and [problems](https://bugs.mysql.com/bug.php?id=65830) that and [problems](https://bugs.mysql.com/bug.php?id=65830) that
[suggested](https://bugs.mysql.com/bug.php?id=50909) [suggested](https://bugs.mysql.com/bug.php?id=50909)
[fixes](https://bugs.mysql.com/bug.php?id=65830) [have](https://bugs.mysql.com/bug.php?id=63164). [fixes](https://bugs.mysql.com/bug.php?id=65830) [have](https://bugs.mysql.com/bug.php?id=63164).
- We recommend using MySQL version 5.6 or later. Please see the following [issue][ce-38152].
## Initial database setup ## Initial database setup
......
...@@ -11,11 +11,7 @@ in the table below. ...@@ -11,11 +11,7 @@ in the table below.
| `issues_url` | The URL to the issue in Bugzilla project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. | | `issues_url` | The URL to the issue in Bugzilla project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. |
| `new_issue_url` | This is the URL to create a new issue in Bugzilla for the project linked to this GitLab project. Note that the `new_issue_url` requires PRODUCT_NAME to be updated with the product/project name in Bugzilla. | | `new_issue_url` | This is the URL to create a new issue in Bugzilla for the project linked to this GitLab project. Note that the `new_issue_url` requires PRODUCT_NAME to be updated with the product/project name in Bugzilla. |
Once you have configured and enabled Bugzilla: Once you have configured and enabled Bugzilla you'll see the Bugzilla link on the GitLab project pages that takes you to the appropriate Bugzilla project.
- the **Issues** link on the GitLab project pages takes you to the appropriate
Bugzilla product page
- clicking **New issue** on the project dashboard takes you to Bugzilla for entering a new issue
## Referencing issues in Bugzilla ## Referencing issues in Bugzilla
......
...@@ -118,7 +118,7 @@ in the table below. ...@@ -118,7 +118,7 @@ in the table below.
| `Transition ID` | This is the ID of a transition that moves issues to a closed state. You can find this number under JIRA workflow administration ([see screenshot](img/jira_workflow_screenshot.png)). **Closing JIRA issues via commits or Merge Requests won't work if you don't set the ID correctly.** | | `Transition ID` | This is the ID of a transition that moves issues to a closed state. You can find this number under JIRA workflow administration ([see screenshot](img/jira_workflow_screenshot.png)). **Closing JIRA issues via commits or Merge Requests won't work if you don't set the ID correctly.** |
After saving the configuration, your GitLab project will be able to interact After saving the configuration, your GitLab project will be able to interact
with all JIRA projects in your JIRA instance. with all JIRA projects in your JIRA instance and you'll see the JIRA link on the GitLab project pages that takes you to the appropriate JIRA project.
![JIRA service page](img/jira_service_page.png) ![JIRA service page](img/jira_service_page.png)
......
...@@ -12,6 +12,8 @@ in the table below. ...@@ -12,6 +12,8 @@ in the table below.
| `issues_url` | The URL to the issue in Redmine project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. | | `issues_url` | The URL to the issue in Redmine project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. |
| `new_issue_url` | This is the URL to create a new issue in Redmine for the project linked to this GitLab project. **This is currently not being used and will be removed in a future release.** | | `new_issue_url` | This is the URL to create a new issue in Redmine for the project linked to this GitLab project. **This is currently not being used and will be removed in a future release.** |
Once you have configured and enabled Redmine you'll see the Redmine link on the GitLab project pages that takes you to the appropriate Redmine project.
As an example, below is a configuration for a project named gitlab-ci. As an example, below is a configuration for a project named gitlab-ci.
![Redmine configuration](img/redmine_configuration.png) ![Redmine configuration](img/redmine_configuration.png)
......
...@@ -169,9 +169,6 @@ from the UI. ...@@ -169,9 +169,6 @@ from the UI.
If you want approvals to persist, independent of changes to the merge request, If you want approvals to persist, independent of changes to the merge request,
turn this setting to off by unchecking the box and saving the changes. turn this setting to off by unchecking the box and saving the changes.
If one of the approvers pushes a commit to the branch that is tied to the merge
request, they automatically get excluded from the approvers list.
## Merge requests with different source branch and target branch projects ## Merge requests with different source branch and target branch projects
If the merge request source branch and target branch belong to different If the merge request source branch and target branch belong to different
......
import Api from '~/api'; import Api from '~/api';
import { __ } from '~/locale';
import Flash from '~/flash';
import axios from '~/lib/utils/axios_utils';
export default class ApproversSelect { export default class ApproversSelect {
constructor() { constructor() {
...@@ -147,7 +150,7 @@ export default class ApproversSelect { ...@@ -147,7 +150,7 @@ export default class ApproversSelect {
} }
static saveApprovers(fieldName) { static saveApprovers(fieldName) {
const $input = window.$(`[name="${fieldName}"]`); const $input = $(`[name="${fieldName}"]`);
const newValue = $input.val(); const newValue = $input.val();
const $loadWrapper = $('.load-wrapper'); const $loadWrapper = $('.load-wrapper');
const $approverSelect = $('.js-select-user-and-group'); const $approverSelect = $('.js-select-user-and-group');
...@@ -158,41 +161,42 @@ export default class ApproversSelect { ...@@ -158,41 +161,42 @@ export default class ApproversSelect {
const $form = $('.js-add-approvers').closest('form'); const $form = $('.js-add-approvers').closest('form');
$loadWrapper.removeClass('hidden'); $loadWrapper.removeClass('hidden');
window.$.ajax({
url: $form.attr('action'), axios.post($form.attr('action'), `_method=PATCH&${[encodeURIComponent(fieldName)]}=${newValue}`, {
type: 'POST', headers: {
data: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
_method: 'PATCH',
[fieldName]: newValue,
},
success: ApproversSelect.updateApproverList,
complete() {
$input.val('');
$approverSelect.select2('val', '');
$loadWrapper.addClass('hidden');
},
error() {
window.Flash('Failed to add Approver', 'alert');
}, },
}).then(({ data }) => {
ApproversSelect.updateApproverList(data);
ApproversSelect.saveApproversComplete($input, $approverSelect, $loadWrapper);
}).catch(() => {
Flash(__('An error occurred while adding approver'));
ApproversSelect.saveApproversComplete($input, $approverSelect, $loadWrapper);
}); });
} }
static saveApproversComplete($input, $approverSelect, $loadWrapper) {
$input.val('');
$approverSelect.select2('val', '');
$loadWrapper.addClass('hidden');
}
static removeApprover(e) { static removeApprover(e) {
e.preventDefault(); e.preventDefault();
const target = e.currentTarget; const target = e.currentTarget;
const $loadWrapper = $('.load-wrapper'); const $loadWrapper = $('.load-wrapper');
$loadWrapper.removeClass('hidden'); $loadWrapper.removeClass('hidden');
$.ajax({
url: target.getAttribute('href'), axios.post(target.getAttribute('href'), '_method=DELETE', {
type: 'POST', headers: {
data: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
_method: 'DELETE',
},
success: ApproversSelect.updateApproverList,
complete: () => $loadWrapper.addClass('hidden'),
error() {
window.Flash('Failed to remove Approver', 'alert');
}, },
}).then(({ data }) => {
ApproversSelect.updateApproverList(data);
$loadWrapper.addClass('hidden');
}).catch(() => {
Flash(__('An error occurred while removing approver'));
$loadWrapper.addClass('hidden');
}); });
} }
......
import flash from '~/flash';
import { __ } from '~/locale';
import axios from '~/lib/utils/axios_utils';
export default function initPathLocks(url, path) { export default function initPathLocks(url, path) {
$('a.path-lock').on('click', (e) => { $('a.path-lock').on('click', (e) => {
e.preventDefault(); e.preventDefault();
$.post(url, { axios.post(url, {
path, path,
}, () => { }).then(() => {
location.reload(); location.reload();
}); }).catch(() => flash(__('An error occurred while initializing path locks')));
}); });
} }
...@@ -20,30 +20,6 @@ function WeightSelect(els, options = {}) { ...@@ -20,30 +20,6 @@ function WeightSelect(els, options = {}) {
inputField.val(options.selected); inputField.val(options.selected);
} }
updateWeight = function(selected) {
var data;
data = {};
data[abilityName] = {};
data[abilityName].weight = selected != null ? selected : null;
$loading.fadeIn();
$dropdown.trigger('loading.gl.dropdown');
return $.ajax({
type: 'PUT',
dataType: 'json',
url: updateUrl,
data: data
}).done(function(data) {
$dropdown.trigger('loaded.gl.dropdown');
$loading.fadeOut();
$selectbox.hide();
if (data.weight != null) {
$value.html(`<strong>${data.weight}</strong>`);
} else {
$value.html('<span class="no-value">None</span>');
}
return $sidebarCollapsedValue.html(data.weight);
});
};
return $dropdown.glDropdown({ return $dropdown.glDropdown({
selectable: true, selectable: true,
fieldName, fieldName,
...@@ -70,13 +46,8 @@ function WeightSelect(els, options = {}) { ...@@ -70,13 +46,8 @@ function WeightSelect(els, options = {}) {
e.preventDefault(); e.preventDefault();
selected = inputField.val(); selected = inputField.val();
options.handleClick(selected); options.handleClick(selected);
} else if ($(dropdown).is(".js-filter-submit")) {
return $(dropdown).parents('form').submit();
} else if ($dropdown.is('.js-issuable-form-weight')) { } else if ($dropdown.is('.js-issuable-form-weight')) {
e.preventDefault(); e.preventDefault();
} else {
selected = inputField.val();
return updateWeight(selected);
} }
} }
}); });
......
...@@ -80,7 +80,7 @@ module Geo ...@@ -80,7 +80,7 @@ module Geo
url = Gitlab::Geo.primary_node.url + repository.full_path + '.git' url = Gitlab::Geo.primary_node.url + repository.full_path + '.git'
# Fetch the repository, using a JWT header for authentication # Fetch the repository, using a JWT header for authentication
authorization = ::Gitlab::Geo::BaseRequest.new.authorization authorization = ::Gitlab::Geo::RepoSyncRequest.new.authorization
header = { "http.#{url}.extraHeader" => "Authorization: #{authorization}" } header = { "http.#{url}.extraHeader" => "Authorization: #{authorization}" }
repository.with_config(header) do repository.with_config(header) do
...@@ -108,6 +108,7 @@ module Geo ...@@ -108,6 +108,7 @@ module Geo
attrs["resync_#{type}"] = false attrs["resync_#{type}"] = false
attrs["#{type}_retry_count"] = nil attrs["#{type}_retry_count"] = nil
attrs["#{type}_retry_at"] = nil attrs["#{type}_retry_at"] = nil
attrs["force_to_redownload_#{type}"] = false
end end
registry.update!(attrs) registry.update!(attrs)
...@@ -183,8 +184,7 @@ module Geo ...@@ -183,8 +184,7 @@ module Geo
# Remove the deleted path in case it exists, but it may not be there # Remove the deleted path in case it exists, but it may not be there
gitlab_shell.remove_repository(project.repository_storage_path, deleted_disk_path_temp) gitlab_shell.remove_repository(project.repository_storage_path, deleted_disk_path_temp)
# Move the original repository out of the way if project.repository_exists? && !gitlab_shell.mv_repository(project.repository_storage_path, repository.disk_path, deleted_disk_path_temp)
unless gitlab_shell.mv_repository(project.repository_storage_path, repository.disk_path, deleted_disk_path_temp)
raise Gitlab::Shell::Error, 'Can not move original repository out of the way' raise Gitlab::Shell::Error, 'Can not move original repository out of the way'
end end
......
module Geo module Geo
class FileUploadService < FileService class FileUploadService < FileService
IAT_LEEWAY = 60.seconds.to_i
attr_reader :auth_header attr_reader :auth_header
def initialize(params, auth_header) def initialize(params, auth_header)
......
module Gitlab
module Ci
module External
module File
class Base
YAML_WHITELIST_EXTENSION = /(yml|yaml)$/i.freeze
def initialize(location, opts = {})
@location = location
end
def valid?
location.match(YAML_WHITELIST_EXTENSION) && content
end
def content
raise NotImplementedError, 'content must be implemented and return a string or nil'
end
def error_message
raise NotImplementedError, 'error_message must be implemented and return a string'
end
end
end
end
end
end
...@@ -2,27 +2,28 @@ module Gitlab ...@@ -2,27 +2,28 @@ module Gitlab
module Ci module Ci
module External module External
module File module File
class Local class Local < Base
attr_reader :location, :project, :sha attr_reader :location, :project, :sha
def initialize(location, opts = {}) def initialize(location, opts = {})
@location = location super
@project = opts.fetch(:project) @project = opts.fetch(:project)
@sha = opts.fetch(:sha) @sha = opts.fetch(:sha)
end end
def valid? def content
local_file_content @content ||= fetch_local_content
end end
def content def error_message
local_file_content "Local file '#{location}' is not valid."
end end
private private
def local_file_content def fetch_local_content
@local_file_content ||= project.repository.blob_data_at(sha, location) project.repository.blob_data_at(sha, location)
end end
end end
end end
......
...@@ -2,29 +2,25 @@ module Gitlab ...@@ -2,29 +2,25 @@ module Gitlab
module Ci module Ci
module External module External
module File module File
class Remote class Remote < Base
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
attr_reader :location attr_reader :location
def initialize(location, opts = {})
@location = location
end
def valid?
::Gitlab::UrlSanitizer.valid?(location) && content
end
def content def content
return @content if defined?(@content) return @content if defined?(@content)
@content = strong_memoize(:content) do @content = strong_memoize(:content) do
begin begin
HTTParty.get(location) HTTParty.get(location)
rescue HTTParty::Error, Timeout::Error rescue HTTParty::Error, Timeout::Error, SocketError
false nil
end end
end end
end end
def error_message
"Remote file '#{location}' is not valid."
end
end end
end end
end end
......
...@@ -17,10 +17,8 @@ module Gitlab ...@@ -17,10 +17,8 @@ module Gitlab
attr_reader :locations, :project, :sha attr_reader :locations, :project, :sha
def build_external_file(location) def build_external_file(location)
remote_file = Gitlab::Ci::External::File::Remote.new(location) if ::Gitlab::UrlSanitizer.valid?(location)
Gitlab::Ci::External::File::Remote.new(location)
if remote_file.valid?
remote_file
else else
options = { project: project, sha: sha } options = { project: project, sha: sha }
Gitlab::Ci::External::File::Local.new(location, options) Gitlab::Ci::External::File::Local.new(location, options)
......
...@@ -28,7 +28,7 @@ module Gitlab ...@@ -28,7 +28,7 @@ module Gitlab
def validate_external_file(external_file) def validate_external_file(external_file)
unless external_file.valid? unless external_file.valid?
raise FileError, "External file: '#{external_file.location}' should be a valid local or remote file" raise FileError, external_file.error_message
end end
end end
......
...@@ -77,6 +77,7 @@ module Gitlab ...@@ -77,6 +77,7 @@ module Gitlab
extname = File.extname(filename) extname = File.extname(filename)
basename = filename.sub(/#{extname}$/, '') basename = filename.sub(/#{extname}$/, '')
content = result["_source"]["blob"]["content"] content = result["_source"]["blob"]["content"]
project_id = result["_parent"].to_i
total_lines = content.lines.size total_lines = content.lines.size
term = term =
...@@ -113,7 +114,8 @@ module Gitlab ...@@ -113,7 +114,8 @@ module Gitlab
basename: basename, basename: basename,
ref: ref, ref: ref,
startline: from + 1, startline: from + 1,
data: data.join data: data.join,
project_id: project_id
) )
end end
......
...@@ -20,6 +20,10 @@ module Gitlab ...@@ -20,6 +20,10 @@ module Gitlab
geo_auth_token(request_data) geo_auth_token(request_data)
end end
def expiration_time
1.minute
end
private private
def geo_auth_token(message) def geo_auth_token(message)
...@@ -27,6 +31,7 @@ module Gitlab ...@@ -27,6 +31,7 @@ module Gitlab
raise GeoNodeNotFoundError unless geo_node raise GeoNodeNotFoundError unless geo_node
token = JSONWebToken::HMACToken.new(geo_node.secret_access_key) token = JSONWebToken::HMACToken.new(geo_node.secret_access_key)
token.expire_time = Time.now + expiration_time
token[:data] = message.to_json token[:data] = message.to_json
"#{GITLAB_GEO_AUTH_TOKEN_TYPE} #{geo_node.access_key}:#{token.encoded}" "#{GITLAB_GEO_AUTH_TOKEN_TYPE} #{geo_node.access_key}:#{token.encoded}"
......
module Gitlab
module Geo
class RepoSyncRequest < BaseRequest
def expiration_time
10.minutes
end
end
end
end
...@@ -141,7 +141,7 @@ describe Groups::EpicsController do ...@@ -141,7 +141,7 @@ describe Groups::EpicsController do
show_epic(:json) show_epic(:json)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(response).to match_response_schema('entities/epic') expect(response).to match_response_schema('entities/epic', dir: 'ee')
end end
context 'with unauthorized user' do context 'with unauthorized user' do
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment