Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce
This commit is contained in:
commit
3569294e64
249 changed files with 2049 additions and 1405 deletions
|
@ -75,7 +75,7 @@ stages:
|
|||
|
||||
.use-mysql: &use-mysql
|
||||
services:
|
||||
- mysql:latest
|
||||
- mysql:5.7
|
||||
- redis:alpine
|
||||
|
||||
.rails5-variables: &rails5-variables
|
||||
|
|
|
@ -143,7 +143,7 @@ Lint/MissingCopEnableDirective:
|
|||
Lint/NestedPercentLiteral:
|
||||
Exclude:
|
||||
- 'lib/gitlab/git/repository.rb'
|
||||
- 'spec/support/email_format_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/email_format_shared_examples.rb'
|
||||
|
||||
# Offense count: 1
|
||||
Lint/ReturnInVoidContext:
|
||||
|
@ -195,8 +195,8 @@ Naming/HeredocDelimiterCase:
|
|||
- 'spec/lib/gitlab/diff/parser_spec.rb'
|
||||
- 'spec/lib/json_web_token/rsa_token_spec.rb'
|
||||
- 'spec/models/commit_spec.rb'
|
||||
- 'spec/support/repo_helpers.rb'
|
||||
- 'spec/support/seed_repo.rb'
|
||||
- 'spec/support/helpers/repo_helpers.rb'
|
||||
- 'spec/support/helpers/seed_repo.rb'
|
||||
|
||||
# Offense count: 112
|
||||
# Configuration parameters: Blacklist.
|
||||
|
@ -496,7 +496,7 @@ Style/EmptyLiteral:
|
|||
- 'spec/lib/gitlab/request_context_spec.rb'
|
||||
- 'spec/lib/gitlab/workhorse_spec.rb'
|
||||
- 'spec/requests/api/jobs_spec.rb'
|
||||
- 'spec/support/chat_slash_commands_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/chat_slash_commands_shared_examples.rb'
|
||||
|
||||
# Offense count: 102
|
||||
# Cop supports --auto-correct.
|
||||
|
|
|
@ -26,7 +26,9 @@ _This notice should stay as the first item in the CONTRIBUTING.md file._
|
|||
- [Type labels (~"feature proposal", ~bug, ~customer, etc.)](#type-labels-feature-proposal-bug-customer-etc)
|
||||
- [Subject labels (~wiki, ~"container registry", ~ldap, ~api, etc.)](#subject-labels-wiki-container-registry-ldap-api-etc)
|
||||
- [Team labels (~"CI/CD", ~Discussion, ~Edge, ~Platform, etc.)](#team-labels-cicd-discussion-edge-platform-etc)
|
||||
- [Priority labels (~Deliverable, ~Stretch, ~"Next Patch Release")](#priority-labels-deliverable-stretch-next-patch-release)
|
||||
- [Milestone labels (~Deliverable, ~Stretch, ~"Next Patch Release")](#milestone-labels-deliverable-stretch-next-patch-release)
|
||||
- [Priority labels (~Deliverable, ~Stretch, ~"Next Patch Release")](#bug-priority-labels-p1-p2-p3-etc)
|
||||
- [Severity labels (~Deliverable, ~Stretch, ~"Next Patch Release")](#bug-severity-labels-s1-s2-s3-etc)
|
||||
- [Label for community contributors (~"Accepting Merge Requests")](#label-for-community-contributors-accepting-merge-requests)
|
||||
- [Implement design & UI elements](#implement-design-ui-elements)
|
||||
- [Issue tracker](#issue-tracker)
|
||||
|
@ -127,6 +129,8 @@ Most issues will have labels for at least one of the following:
|
|||
- Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc.
|
||||
- Team: ~"CI/CD", ~Discussion, ~Edge, ~Platform, etc.
|
||||
- Milestone: ~Deliverable, ~Stretch, ~"Next Patch Release"
|
||||
- Priority: ~P1, ~P2, ~P3, ~P4
|
||||
- Severity: ~S1, ~S2, ~S3, ~S4
|
||||
|
||||
All labels, their meaning and priority are defined on the
|
||||
[labels page][labels-page].
|
||||
|
@ -210,7 +214,7 @@ This label documents the planned timeline & urgency which is used to measure aga
|
|||
|
||||
| Label | Meaning | Estimate time to fix | Guidance |
|
||||
|-------|-----------------|------------------------------------------------------------------|----------|
|
||||
| ~P1 | Urgent Priority | The current release | |
|
||||
| ~P1 | Urgent Priority | The current release + potentially immediate hotfix to GitLab.com | |
|
||||
| ~P2 | High Priority | The next release | |
|
||||
| ~P3 | Medium Priority | Within the next 3 releases (approx one quarter) | |
|
||||
| ~P4 | Low Priority | Anything outside the next 3 releases (approx beyond one quarter) | The issue is prominent but does not impact user workflow and a workaround is documented |
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.95.0
|
||||
0.96.1
|
||||
|
|
|
@ -178,7 +178,7 @@ GEM
|
|||
docile (1.1.5)
|
||||
domain_name (0.5.20170404)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
doorkeeper (4.3.1)
|
||||
doorkeeper (4.3.2)
|
||||
railties (>= 4.2)
|
||||
doorkeeper-openid_connect (1.3.0)
|
||||
doorkeeper (~> 4.3)
|
||||
|
|
|
@ -84,20 +84,21 @@ export default class CreateMergeRequestDropdown {
|
|||
if (data.can_create_branch) {
|
||||
this.available();
|
||||
this.enable();
|
||||
this.updateBranchName(data.suggested_branch_name);
|
||||
|
||||
if (!this.droplabInitialized) {
|
||||
this.droplabInitialized = true;
|
||||
this.initDroplab();
|
||||
this.bindEvents();
|
||||
}
|
||||
} else if (data.has_related_branch) {
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.unavailable();
|
||||
this.disable();
|
||||
Flash('Failed to check if a new branch can be created.');
|
||||
Flash(__('Failed to check related branches.'));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -409,13 +410,16 @@ export default class CreateMergeRequestDropdown {
|
|||
this.unavailableButton.classList.remove('hide');
|
||||
}
|
||||
|
||||
updateBranchName(suggestedBranchName) {
|
||||
this.branchInput.value = suggestedBranchName;
|
||||
this.updateCreatePaths('branch', suggestedBranchName);
|
||||
}
|
||||
|
||||
updateInputState(target, ref, result) {
|
||||
// target - 'branch' or 'ref' - which the input field we are searching a ref for.
|
||||
// ref - string - what a user typed.
|
||||
// result - string - what has been found on backend.
|
||||
|
||||
const pathReplacement = `$1${ref}`;
|
||||
|
||||
// If a found branch equals exact the same text a user typed,
|
||||
// that means a new branch cannot be created as it already exists.
|
||||
if (ref === result) {
|
||||
|
@ -426,18 +430,12 @@ export default class CreateMergeRequestDropdown {
|
|||
this.refIsValid = true;
|
||||
this.refInput.dataset.value = ref;
|
||||
this.showAvailableMessage('ref');
|
||||
this.createBranchPath = this.createBranchPath.replace(this.regexps.ref.createBranchPath,
|
||||
pathReplacement);
|
||||
this.createMrPath = this.createMrPath.replace(this.regexps.ref.createMrPath,
|
||||
pathReplacement);
|
||||
this.updateCreatePaths(target, ref);
|
||||
}
|
||||
} else if (target === 'branch') {
|
||||
this.branchIsValid = true;
|
||||
this.showAvailableMessage('branch');
|
||||
this.createBranchPath = this.createBranchPath.replace(this.regexps.branch.createBranchPath,
|
||||
pathReplacement);
|
||||
this.createMrPath = this.createMrPath.replace(this.regexps.branch.createMrPath,
|
||||
pathReplacement);
|
||||
this.updateCreatePaths(target, ref);
|
||||
} else {
|
||||
this.refIsValid = false;
|
||||
this.refInput.dataset.value = ref;
|
||||
|
@ -457,4 +455,15 @@ export default class CreateMergeRequestDropdown {
|
|||
this.disableCreateAction();
|
||||
}
|
||||
}
|
||||
|
||||
// target - 'branch' or 'ref'
|
||||
// ref - string - the new value to use as branch or ref
|
||||
updateCreatePaths(target, ref) {
|
||||
const pathReplacement = `$1${ref}`;
|
||||
|
||||
this.createBranchPath = this.createBranchPath.replace(this.regexps[target].createBranchPath,
|
||||
pathReplacement);
|
||||
this.createMrPath = this.createMrPath.replace(this.regexps[target].createMrPath,
|
||||
pathReplacement);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,26 +32,38 @@ export default {
|
|||
required: true,
|
||||
},
|
||||
|
||||
buttonDisabled: {
|
||||
requestFinishedFor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isDisabled: false,
|
||||
linkRequested: '',
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
cssClass() {
|
||||
const actionIconDash = dasherize(this.actionIcon);
|
||||
return `${actionIconDash} js-icon-${actionIconDash}`;
|
||||
},
|
||||
isDisabled() {
|
||||
return this.buttonDisabled === this.link;
|
||||
},
|
||||
watch: {
|
||||
requestFinishedFor() {
|
||||
if (this.requestFinishedFor === this.linkRequested) {
|
||||
this.isDisabled = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
onClickAction() {
|
||||
$(this.$el).tooltip('hide');
|
||||
eventHub.$emit('graphAction', this.link);
|
||||
this.linkRequested = this.link;
|
||||
this.isDisabled = true;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -62,7 +74,8 @@ export default {
|
|||
@click="onClickAction"
|
||||
v-tooltip
|
||||
:title="tooltipText"
|
||||
class="btn btn-blank btn-transparent ci-action-icon-container ci-action-icon-wrapper"
|
||||
class="js-ci-action btn btn-blank
|
||||
btn-transparent ci-action-icon-container ci-action-icon-wrapper"
|
||||
:class="cssClass"
|
||||
data-container="body"
|
||||
:disabled="isDisabled"
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
<script>
|
||||
import icon from '../../../vue_shared/components/icon.vue';
|
||||
import tooltip from '../../../vue_shared/directives/tooltip';
|
||||
|
||||
/**
|
||||
* Renders either a cancel, retry or play icon pointing to the given path.
|
||||
* TODO: Remove UJS from here and use an async request instead.
|
||||
*/
|
||||
export default {
|
||||
components: {
|
||||
icon,
|
||||
},
|
||||
|
||||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
props: {
|
||||
tooltipText: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
link: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
actionMethod: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
actionIcon: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<a
|
||||
v-tooltip
|
||||
:data-method="actionMethod"
|
||||
:title="tooltipText"
|
||||
:href="link"
|
||||
rel="nofollow"
|
||||
class="ci-action-icon-wrapper js-ci-status-icon"
|
||||
data-container="body"
|
||||
aria-label="Job's action"
|
||||
>
|
||||
<icon :name="actionIcon" />
|
||||
</a>
|
||||
</template>
|
|
@ -1,10 +1,10 @@
|
|||
<script>
|
||||
import $ from 'jquery';
|
||||
import jobNameComponent from './job_name_component.vue';
|
||||
import jobComponent from './job_component.vue';
|
||||
import tooltip from '../../../vue_shared/directives/tooltip';
|
||||
import $ from 'jquery';
|
||||
import JobNameComponent from './job_name_component.vue';
|
||||
import JobComponent from './job_component.vue';
|
||||
import tooltip from '../../../vue_shared/directives/tooltip';
|
||||
|
||||
/**
|
||||
/**
|
||||
* Renders the dropdown for the pipeline graph.
|
||||
*
|
||||
* The following object should be provided as `job`:
|
||||
|
@ -27,14 +27,14 @@
|
|||
* }
|
||||
* }
|
||||
*/
|
||||
export default {
|
||||
export default {
|
||||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
|
||||
components: {
|
||||
jobComponent,
|
||||
jobNameComponent,
|
||||
JobComponent,
|
||||
JobNameComponent,
|
||||
},
|
||||
|
||||
props: {
|
||||
|
@ -42,6 +42,11 @@
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
requestFinishedFor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
@ -56,22 +61,23 @@
|
|||
|
||||
methods: {
|
||||
/**
|
||||
* When the user right clicks or cmd/ctrl + click in the job name
|
||||
* the dropdown should not be closed and the link should open in another tab,
|
||||
* so we stop propagation of the click event inside the dropdown.
|
||||
* When the user right clicks or cmd/ctrl + click in the job name or the action icon
|
||||
* the dropdown should not be closed so we stop propagation
|
||||
* of the click event inside the dropdown.
|
||||
*
|
||||
* Since this component is rendered multiple times per page we need to guarantee we only
|
||||
* target the click event of this component.
|
||||
*/
|
||||
stopDropdownClickPropagation() {
|
||||
$(this.$el
|
||||
.querySelectorAll('.js-grouped-pipeline-dropdown a.mini-pipeline-graph-dropdown-item'))
|
||||
.on('click', (e) => {
|
||||
$(
|
||||
'.js-grouped-pipeline-dropdown button, .js-grouped-pipeline-dropdown a.mini-pipeline-graph-dropdown-item',
|
||||
this.$el,
|
||||
).on('click', e => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="ci-job-dropdown-container">
|
||||
|
@ -101,8 +107,8 @@
|
|||
:key="i">
|
||||
<job-component
|
||||
:job="item"
|
||||
:is-dropdown="true"
|
||||
css-class-job-name="mini-pipeline-graph-dropdown-item"
|
||||
:request-finished-for="requestFinishedFor"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -7,7 +7,6 @@ export default {
|
|||
StageColumnComponent,
|
||||
LoadingIcon,
|
||||
},
|
||||
|
||||
props: {
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
|
@ -17,10 +16,10 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
actionDisabled: {
|
||||
requestFinishedFor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -75,7 +74,7 @@ export default {
|
|||
:key="stage.name"
|
||||
:stage-connector-class="stageConnectorClass(index, stage)"
|
||||
:is-first-column="isFirstColumn(index)"
|
||||
:action-disabled="actionDisabled"
|
||||
:request-finished-for="requestFinishedFor"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<script>
|
||||
import ActionComponent from './action_component.vue';
|
||||
import DropdownActionComponent from './dropdown_action_component.vue';
|
||||
import JobNameComponent from './job_name_component.vue';
|
||||
import tooltip from '../../../vue_shared/directives/tooltip';
|
||||
|
||||
|
@ -32,10 +31,8 @@ import tooltip from '../../../vue_shared/directives/tooltip';
|
|||
export default {
|
||||
components: {
|
||||
ActionComponent,
|
||||
DropdownActionComponent,
|
||||
JobNameComponent,
|
||||
},
|
||||
|
||||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
|
@ -44,26 +41,17 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
|
||||
cssClassJobName: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
|
||||
isDropdown: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
|
||||
actionDisabled: {
|
||||
requestFinishedFor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
status() {
|
||||
return this.job && this.job.status ? this.job.status : {};
|
||||
|
@ -134,19 +122,11 @@ export default {
|
|||
</div>
|
||||
|
||||
<action-component
|
||||
v-if="hasAction && !isDropdown"
|
||||
v-if="hasAction"
|
||||
:tooltip-text="status.action.title"
|
||||
:link="status.action.path"
|
||||
:action-icon="status.action.icon"
|
||||
:button-disabled="actionDisabled"
|
||||
/>
|
||||
|
||||
<dropdown-action-component
|
||||
v-if="hasAction && isDropdown"
|
||||
:tooltip-text="status.action.title"
|
||||
:link="status.action.path"
|
||||
:action-icon="status.action.icon"
|
||||
:action-method="status.action.method"
|
||||
:request-finished-for="requestFinishedFor"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -29,10 +29,11 @@ export default {
|
|||
required: false,
|
||||
default: '',
|
||||
},
|
||||
actionDisabled: {
|
||||
|
||||
requestFinishedFor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -74,12 +75,12 @@ export default {
|
|||
v-if="job.size === 1"
|
||||
:job="job"
|
||||
css-class-job-name="build-content"
|
||||
:action-disabled="actionDisabled"
|
||||
/>
|
||||
|
||||
<dropdown-job-component
|
||||
v-if="job.size > 1"
|
||||
:job="job"
|
||||
:request-finished-for="requestFinishedFor"
|
||||
/>
|
||||
|
||||
</li>
|
||||
|
|
|
@ -25,7 +25,7 @@ export default () => {
|
|||
data() {
|
||||
return {
|
||||
mediator,
|
||||
actionDisabled: null,
|
||||
requestFinishedFor: null,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
|
@ -36,15 +36,17 @@ export default () => {
|
|||
},
|
||||
methods: {
|
||||
postAction(action) {
|
||||
this.actionDisabled = action;
|
||||
// Click was made, reset this variable
|
||||
this.requestFinishedFor = null;
|
||||
|
||||
this.mediator.service.postAction(action)
|
||||
this.mediator.service
|
||||
.postAction(action)
|
||||
.then(() => {
|
||||
this.mediator.refreshPipeline();
|
||||
this.actionDisabled = null;
|
||||
this.requestFinishedFor = action;
|
||||
})
|
||||
.catch(() => {
|
||||
this.actionDisabled = null;
|
||||
this.requestFinishedFor = action;
|
||||
Flash(__('An error occurred while making the request.'));
|
||||
});
|
||||
},
|
||||
|
@ -54,7 +56,7 @@ export default () => {
|
|||
props: {
|
||||
isLoading: this.mediator.state.isLoading,
|
||||
pipeline: this.mediator.store.state.pipeline,
|
||||
actionDisabled: this.actionDisabled,
|
||||
requestFinishedFor: this.requestFinishedFor,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
@ -79,7 +81,8 @@ export default () => {
|
|||
},
|
||||
methods: {
|
||||
postAction(action) {
|
||||
this.mediator.service.postAction(action.path)
|
||||
this.mediator.service
|
||||
.postAction(action.path)
|
||||
.then(() => this.mediator.refreshPipeline())
|
||||
.catch(() => Flash(__('An error occurred while making the request.')));
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import ciIcon from './ci_icon.vue';
|
||||
import tooltip from '../directives/tooltip';
|
||||
/**
|
||||
import CiIcon from './ci_icon.vue';
|
||||
import tooltip from '../directives/tooltip';
|
||||
/**
|
||||
* Renders CI Badge link with CI icon and status text based on
|
||||
* API response shared between all places where it is used.
|
||||
*
|
||||
|
@ -22,9 +22,9 @@
|
|||
* - MR widget
|
||||
*/
|
||||
|
||||
export default {
|
||||
export default {
|
||||
components: {
|
||||
ciIcon,
|
||||
CiIcon,
|
||||
},
|
||||
directives: {
|
||||
tooltip,
|
||||
|
@ -46,7 +46,7 @@
|
|||
return className ? `ci-status ci-${className}` : 'ci-status';
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<a
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import icon from '../../vue_shared/components/icon.vue';
|
||||
import Icon from '../../vue_shared/components/icon.vue';
|
||||
|
||||
/**
|
||||
/**
|
||||
* Renders CI icon based on API response shared between all places where it is used.
|
||||
*
|
||||
* Receives status object containing:
|
||||
|
@ -22,9 +22,9 @@
|
|||
* - Jobs show view header
|
||||
* - Jobs show view sidebar
|
||||
*/
|
||||
export default {
|
||||
export default {
|
||||
components: {
|
||||
icon,
|
||||
Icon,
|
||||
},
|
||||
props: {
|
||||
status: {
|
||||
|
@ -32,14 +32,13 @@
|
|||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
cssClass() {
|
||||
const status = this.status.group;
|
||||
return `ci-status-icon ci-status-icon-${status} js-ci-status-icon-${status}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<span :class="cssClass">
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
<script>
|
||||
/**
|
||||
/**
|
||||
* Falls back to the code used in `copy_to_clipboard.js`
|
||||
*
|
||||
* Renders a button with a clipboard icon that copies the content of `data-clipboard-text`
|
||||
* when clicked.
|
||||
*
|
||||
* @example
|
||||
* <clipboard-button
|
||||
* title="Copy to clipbard"
|
||||
* text="Content to be copied"
|
||||
* css-class="btn-transparent"
|
||||
* />
|
||||
*/
|
||||
import tooltip from '../directives/tooltip';
|
||||
import tooltip from '../directives/tooltip';
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: 'ClipboardButton',
|
||||
directives: {
|
||||
tooltip,
|
||||
|
@ -34,7 +44,7 @@
|
|||
default: 'btn-default',
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
<script>
|
||||
import commitIconSvg from 'icons/_icon_commit.svg';
|
||||
import userAvatarLink from './user_avatar/user_avatar_link.vue';
|
||||
import tooltip from '../directives/tooltip';
|
||||
import icon from '../../vue_shared/components/icon.vue';
|
||||
import UserAvatarLink from './user_avatar/user_avatar_link.vue';
|
||||
import tooltip from '../directives/tooltip';
|
||||
import Icon from '../../vue_shared/components/icon.vue';
|
||||
|
||||
export default {
|
||||
export default {
|
||||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
components: {
|
||||
userAvatarLink,
|
||||
icon,
|
||||
UserAvatarLink,
|
||||
Icon,
|
||||
},
|
||||
props: {
|
||||
/**
|
||||
|
@ -94,10 +93,7 @@
|
|||
* @returns {Boolean}
|
||||
*/
|
||||
hasAuthor() {
|
||||
return this.author &&
|
||||
this.author.avatar_url &&
|
||||
this.author.path &&
|
||||
this.author.username;
|
||||
return this.author && this.author.avatar_url && this.author.path && this.author.username;
|
||||
},
|
||||
/**
|
||||
* If information about the author is provided will return a string
|
||||
|
@ -106,14 +102,10 @@
|
|||
* @returns {String}
|
||||
*/
|
||||
userImageAltDescription() {
|
||||
return this.author &&
|
||||
this.author.username ? `${this.author.username}'s avatar` : null;
|
||||
return this.author && this.author.username ? `${this.author.username}'s avatar` : null;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.commitIconSvg = commitIconSvg;
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="branch-commit">
|
||||
|
@ -141,11 +133,10 @@
|
|||
{{ commitRef.name }}
|
||||
</a>
|
||||
</template>
|
||||
<div
|
||||
v-html="commitIconSvg"
|
||||
<icon
|
||||
name="commit"
|
||||
class="commit-icon js-commit-icon"
|
||||
>
|
||||
</div>
|
||||
/>
|
||||
|
||||
<a
|
||||
class="commit-sha"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { __ } from '~/locale';
|
||||
/**
|
||||
import { __ } from '~/locale';
|
||||
/**
|
||||
* Port of detail_behavior expand button.
|
||||
*
|
||||
* @example
|
||||
|
@ -10,7 +10,7 @@
|
|||
* </template>
|
||||
* </expand-button>
|
||||
*/
|
||||
export default {
|
||||
export default {
|
||||
name: 'ExpandButton',
|
||||
data() {
|
||||
return {
|
||||
|
@ -27,7 +27,7 @@
|
|||
this.isCollapsed = !this.isCollapsed;
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<span>
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
<script>
|
||||
import ciIconBadge from './ci_badge_link.vue';
|
||||
import loadingIcon from './loading_icon.vue';
|
||||
import timeagoTooltip from './time_ago_tooltip.vue';
|
||||
import tooltip from '../directives/tooltip';
|
||||
import userAvatarImage from './user_avatar/user_avatar_image.vue';
|
||||
import CiIconBadge from './ci_badge_link.vue';
|
||||
import LoadingIcon from './loading_icon.vue';
|
||||
import TimeagoTooltip from './time_ago_tooltip.vue';
|
||||
import tooltip from '../directives/tooltip';
|
||||
import UserAvatarImage from './user_avatar/user_avatar_image.vue';
|
||||
|
||||
/**
|
||||
/**
|
||||
* Renders header component for job and pipeline page based on UI mockups
|
||||
*
|
||||
* Used in:
|
||||
* - job show page
|
||||
* - pipeline show page
|
||||
*/
|
||||
export default {
|
||||
export default {
|
||||
components: {
|
||||
ciIconBadge,
|
||||
loadingIcon,
|
||||
timeagoTooltip,
|
||||
userAvatarImage,
|
||||
CiIconBadge,
|
||||
LoadingIcon,
|
||||
TimeagoTooltip,
|
||||
UserAvatarImage,
|
||||
},
|
||||
directives: {
|
||||
tooltip,
|
||||
|
@ -72,7 +72,7 @@
|
|||
this.$emit('actionClicked', action);
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<script>
|
||||
|
||||
/* This is a re-usable vue component for rendering a svg sprite
|
||||
/* This is a re-usable vue component for rendering a svg sprite
|
||||
icon
|
||||
|
||||
Sample configuration:
|
||||
|
@ -11,11 +10,11 @@
|
|||
css-classes="top"
|
||||
/>
|
||||
|
||||
*/
|
||||
// only allow classes in images.scss e.g. s12
|
||||
const validSizes = [8, 12, 16, 18, 24, 32, 48, 72];
|
||||
*/
|
||||
// only allow classes in images.scss e.g. s12
|
||||
const validSizes = [8, 12, 16, 18, 24, 32, 48, 72];
|
||||
|
||||
export default {
|
||||
export default {
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
|
@ -70,7 +69,7 @@
|
|||
return this.size ? `s${this.size}` : '';
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -79,7 +78,8 @@
|
|||
:width="width"
|
||||
:height="height"
|
||||
:x="x"
|
||||
:y="y">
|
||||
:y="y"
|
||||
>
|
||||
<use v-bind="{ 'xlink:href':spriteHref }" />
|
||||
</svg>
|
||||
</template>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import $ from 'jquery';
|
||||
import { __ } from '~/locale';
|
||||
import LabelsSelect from '~/labels_select';
|
||||
import LoadingIcon from '../../loading_icon.vue';
|
||||
|
@ -98,11 +99,18 @@ export default {
|
|||
this.labelsDropdown = new LabelsSelect(this.$refs.dropdownButton, {
|
||||
handleClick: this.handleClick,
|
||||
});
|
||||
$(this.$refs.dropdown).on('hidden.gl.dropdown', this.handleDropdownHidden);
|
||||
},
|
||||
methods: {
|
||||
handleClick(label) {
|
||||
this.$emit('onLabelClick', label);
|
||||
},
|
||||
handleCollapsedValueClick() {
|
||||
this.$emit('toggleCollapse');
|
||||
},
|
||||
handleDropdownHidden() {
|
||||
this.$emit('onDropdownClose');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -112,6 +120,7 @@ export default {
|
|||
<dropdown-value-collapsed
|
||||
v-if="showCreate"
|
||||
:labels="context.labels"
|
||||
@onValueClick="handleCollapsedValueClick"
|
||||
/>
|
||||
<dropdown-title
|
||||
:can-edit="canEdit"
|
||||
|
@ -133,7 +142,10 @@ export default {
|
|||
:name="hiddenInputName"
|
||||
:label="label"
|
||||
/>
|
||||
<div class="dropdown">
|
||||
<div
|
||||
class="dropdown"
|
||||
ref="dropdown"
|
||||
>
|
||||
<dropdown-button
|
||||
:ability-name="abilityName"
|
||||
:field-name="hiddenInputName"
|
||||
|
|
|
@ -26,6 +26,11 @@ export default {
|
|||
return labelsString;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleClick() {
|
||||
this.$emit('onValueClick');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -36,6 +41,7 @@ export default {
|
|||
data-placement="left"
|
||||
data-container="body"
|
||||
:title="labelsList"
|
||||
@click="handleClick"
|
||||
>
|
||||
<i
|
||||
aria-hidden="true"
|
||||
|
|
|
@ -468,6 +468,14 @@
|
|||
margin-bottom: 10px;
|
||||
white-space: normal;
|
||||
|
||||
.ci-job-dropdown-container {
|
||||
// override dropdown.scss
|
||||
.dropdown-menu li button {
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure .build-content has hover style when action-icon is hovered
|
||||
.ci-job-dropdown-container:hover .build-content {
|
||||
@extend .build-content:hover;
|
||||
|
|
|
@ -134,11 +134,11 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
def can_create_branch
|
||||
can_create = current_user &&
|
||||
can?(current_user, :push_code, @project) &&
|
||||
@issue.can_be_worked_on?(current_user)
|
||||
@issue.can_be_worked_on?
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: { can_create_branch: can_create, has_related_branch: @issue.has_related_branch? }
|
||||
render json: { can_create_branch: can_create, suggested_branch_name: @issue.suggested_branch_name }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -177,7 +177,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def authorize_create_merge_request!
|
||||
render_404 unless can?(current_user, :push_code, @project) && @issue.can_be_worked_on?(current_user)
|
||||
render_404 unless can?(current_user, :push_code, @project) && @issue.can_be_worked_on?
|
||||
end
|
||||
|
||||
def render_issue_json
|
||||
|
|
|
@ -78,8 +78,6 @@ class Projects::JobsController < Projects::ApplicationController
|
|||
result.merge!(trace.to_h)
|
||||
end
|
||||
|
||||
result[:html] = result[:html].presence || 'No job log'
|
||||
|
||||
render json: result
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,6 +32,7 @@ class UsersFinder
|
|||
users = by_active(users)
|
||||
users = by_external_identity(users)
|
||||
users = by_external(users)
|
||||
users = by_2fa(users)
|
||||
users = by_created_at(users)
|
||||
users = by_custom_attributes(users)
|
||||
|
||||
|
@ -76,4 +77,15 @@ class UsersFinder
|
|||
|
||||
users.external
|
||||
end
|
||||
|
||||
def by_2fa(users)
|
||||
case params[:two_factor]
|
||||
when 'enabled'
|
||||
users.with_two_factor
|
||||
when 'disabled'
|
||||
users.without_two_factor
|
||||
else
|
||||
users
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -27,8 +27,9 @@ module AtomicInternalId
|
|||
module ClassMethods
|
||||
def has_internal_id(column, scope:, init:) # rubocop:disable Naming/PredicateName
|
||||
before_validation(on: :create) do
|
||||
if read_attribute(column).blank?
|
||||
scope_attrs = { scope => association(scope).reader }
|
||||
scope_value = association(scope).reader
|
||||
if read_attribute(column).blank? && scope_value
|
||||
scope_attrs = { scope_value.class.table_name.singularize.to_sym => scope_value }
|
||||
usage = self.class.table_name.to_sym
|
||||
|
||||
new_iid = InternalId.generate_next(self, scope_attrs, usage, init)
|
||||
|
|
|
@ -31,12 +31,13 @@ module Avatarable
|
|||
|
||||
asset_host = ActionController::Base.asset_host
|
||||
use_asset_host = asset_host.present?
|
||||
use_authentication = respond_to?(:public?) && !public?
|
||||
|
||||
# Avatars for private and internal groups and projects require authentication to be viewed,
|
||||
# which means they can only be served by Rails, on the regular GitLab host.
|
||||
# If an asset host is configured, we need to return the fully qualified URL
|
||||
# instead of only the avatar path, so that Rails doesn't prefix it with the asset host.
|
||||
if use_asset_host && respond_to?(:public?) && !public?
|
||||
if use_asset_host && use_authentication
|
||||
use_asset_host = false
|
||||
only_path = false
|
||||
end
|
||||
|
@ -49,6 +50,6 @@ module Avatarable
|
|||
url_base << gitlab_config.relative_url_root
|
||||
end
|
||||
|
||||
url_base + avatar.url
|
||||
url_base + avatar.local_url
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
module NonatomicInternalId
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
validate :set_iid, on: :create
|
||||
validates :iid, presence: true, numericality: true
|
||||
end
|
||||
|
||||
def set_iid
|
||||
if iid.blank?
|
||||
parent = project || group
|
||||
records = parent.public_send(self.class.name.tableize) # rubocop:disable GitlabSecurity/PublicSend
|
||||
max_iid = records.maximum(:iid)
|
||||
|
||||
self.iid = max_iid.to_i + 1
|
||||
end
|
||||
end
|
||||
|
||||
def to_param
|
||||
iid.to_s
|
||||
end
|
||||
end
|
|
@ -102,7 +102,7 @@ module Routable
|
|||
# the route. Caching this per request ensures that even if we have multiple instances,
|
||||
# we will not have to duplicate work, avoiding N+1 queries in some cases.
|
||||
def full_path
|
||||
return uncached_full_path unless RequestStore.active?
|
||||
return uncached_full_path unless RequestStore.active? && persisted?
|
||||
|
||||
RequestStore[full_path_key] ||= uncached_full_path
|
||||
end
|
||||
|
@ -124,6 +124,11 @@ module Routable
|
|||
end
|
||||
end
|
||||
|
||||
# Group would override this to check from association
|
||||
def owned_by?(user)
|
||||
owner == user
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_path_errors
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
# Uniquify
|
||||
#
|
||||
# Return a version of the given 'base' string that is unique
|
||||
# by appending a counter to it. Uniqueness is determined by
|
||||
# repeated calls to the passed block.
|
||||
#
|
||||
# You can pass an initial value for the counter, if not given
|
||||
# counting starts from 1.
|
||||
#
|
||||
# If `base` is a function/proc, we expect that calling it with a
|
||||
# candidate counter returns a string to test/return.
|
||||
class Uniquify
|
||||
# Return a version of the given 'base' string that is unique
|
||||
# by appending a counter to it. Uniqueness is determined by
|
||||
# repeated calls to the passed block.
|
||||
#
|
||||
# If `base` is a function/proc, we expect that calling it with a
|
||||
# candidate counter returns a string to test/return.
|
||||
def initialize(counter = nil)
|
||||
@counter = counter
|
||||
end
|
||||
|
||||
def string(base)
|
||||
@base = base
|
||||
@counter = nil
|
||||
|
||||
increment_counter! while yield(base_string)
|
||||
base_string
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
class Deployment < ActiveRecord::Base
|
||||
include NonatomicInternalId
|
||||
include AtomicInternalId
|
||||
|
||||
belongs_to :project, required: true
|
||||
belongs_to :environment, required: true
|
||||
belongs_to :user
|
||||
belongs_to :deployable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
|
||||
|
||||
has_internal_id :iid, scope: :project, init: ->(s) { s&.project&.deployments&.maximum(:iid) }
|
||||
|
||||
validates :sha, presence: true
|
||||
validates :ref, presence: true
|
||||
|
||||
|
|
|
@ -125,6 +125,10 @@ class Group < Namespace
|
|||
self[:lfs_enabled]
|
||||
end
|
||||
|
||||
def owned_by?(user)
|
||||
owners.include?(user)
|
||||
end
|
||||
|
||||
def add_users(users, access_level, current_user: nil, expires_at: nil)
|
||||
GroupMember.add_users(
|
||||
self,
|
||||
|
|
|
@ -12,8 +12,9 @@
|
|||
# * (Optionally) add columns to `internal_ids` if needed for scope.
|
||||
class InternalId < ActiveRecord::Base
|
||||
belongs_to :project
|
||||
belongs_to :namespace
|
||||
|
||||
enum usage: { issues: 0 }
|
||||
enum usage: { issues: 0, merge_requests: 1, deployments: 2, milestones: 3, epics: 4 }
|
||||
|
||||
validates :usage, presence: true
|
||||
|
||||
|
|
|
@ -194,6 +194,15 @@ class Issue < ActiveRecord::Base
|
|||
branches_with_iid - branches_with_merge_request
|
||||
end
|
||||
|
||||
def suggested_branch_name
|
||||
return to_branch_name unless project.repository.branch_exists?(to_branch_name)
|
||||
|
||||
start_counting_from = 2
|
||||
Uniquify.new(start_counting_from).string(-> (counter) { "#{to_branch_name}-#{counter}" }) do |suggested_branch_name|
|
||||
project.repository.branch_exists?(suggested_branch_name)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns boolean if a related branch exists for the current issue
|
||||
# ignores merge requests branchs
|
||||
def has_related_branch?
|
||||
|
@ -248,11 +257,8 @@ class Issue < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def can_be_worked_on?(current_user)
|
||||
!self.closed? &&
|
||||
!self.project.forked? &&
|
||||
self.related_branches(current_user).empty? &&
|
||||
self.closed_by_merge_requests(current_user).empty?
|
||||
def can_be_worked_on?
|
||||
!self.closed? && !self.project.forked?
|
||||
end
|
||||
|
||||
# Returns `true` if the current issue can be viewed by either a logged in User
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class MergeRequest < ActiveRecord::Base
|
||||
include NonatomicInternalId
|
||||
include AtomicInternalId
|
||||
include Issuable
|
||||
include Noteable
|
||||
include Referable
|
||||
|
@ -18,6 +18,8 @@ class MergeRequest < ActiveRecord::Base
|
|||
belongs_to :source_project, class_name: "Project"
|
||||
belongs_to :merge_user, class_name: "User"
|
||||
|
||||
has_internal_id :iid, scope: :target_project, init: ->(s) { s&.target_project&.merge_requests&.maximum(:iid) }
|
||||
|
||||
has_many :merge_request_diffs
|
||||
|
||||
has_one :merge_request_diff,
|
||||
|
|
|
@ -8,7 +8,7 @@ class Milestone < ActiveRecord::Base
|
|||
Started = MilestoneStruct.new('Started', '#started', -3)
|
||||
|
||||
include CacheMarkdownField
|
||||
include NonatomicInternalId
|
||||
include AtomicInternalId
|
||||
include Sortable
|
||||
include Referable
|
||||
include StripAttribute
|
||||
|
@ -21,6 +21,9 @@ class Milestone < ActiveRecord::Base
|
|||
belongs_to :project
|
||||
belongs_to :group
|
||||
|
||||
has_internal_id :iid, scope: :project, init: ->(s) { s&.project&.milestones&.maximum(:iid) }
|
||||
has_internal_id :iid, scope: :group, init: ->(s) { s&.group&.milestones&.maximum(:iid) }
|
||||
|
||||
has_many :issues
|
||||
has_many :labels, -> { distinct.reorder('labels.title') }, through: :issues
|
||||
has_many :merge_requests
|
||||
|
|
|
@ -1,5 +1,51 @@
|
|||
require "flowdock-git-hook"
|
||||
|
||||
# Flow dock depends on Grit to compute the number of commits between two given
|
||||
# commits. To make this depend on Gitaly, a monkey patch is applied
|
||||
module Flowdock
|
||||
class Git
|
||||
# pass down a Repository all the way down
|
||||
def repo
|
||||
@options[:repo]
|
||||
end
|
||||
|
||||
def config
|
||||
{}
|
||||
end
|
||||
|
||||
def messages
|
||||
Git::Builder.new(repo: repo,
|
||||
ref: @ref,
|
||||
before: @from,
|
||||
after: @to,
|
||||
commit_url: @commit_url,
|
||||
branch_url: @branch_url,
|
||||
diff_url: @diff_url,
|
||||
repo_url: @repo_url,
|
||||
repo_name: @repo_name,
|
||||
permanent_refs: @permanent_refs,
|
||||
tags: tags
|
||||
).to_hashes
|
||||
end
|
||||
|
||||
class Builder
|
||||
def commits
|
||||
@repo.commits_between(@before, @after).map do |commit|
|
||||
{
|
||||
url: @opts[:commit_url] ? @opts[:commit_url] % [commit.sha] : nil,
|
||||
id: commit.sha,
|
||||
message: commit.message,
|
||||
author: {
|
||||
name: commit.author_name,
|
||||
email: commit.author_email
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class FlowdockService < Service
|
||||
prop_accessor :token
|
||||
validates :token, presence: true, if: :activated?
|
||||
|
@ -34,7 +80,7 @@ class FlowdockService < Service
|
|||
data[:before],
|
||||
data[:after],
|
||||
token: token,
|
||||
repo: project.repository.path_to_repo,
|
||||
repo: project.repository,
|
||||
repo_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}",
|
||||
commit_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/%s",
|
||||
diff_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/compare/%s...%s"
|
||||
|
|
|
@ -260,7 +260,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
|
|||
if current_user && can?(current_user, :admin_pipeline, project) && repository.gitlab_ci_yml.blank? && !show_auto_devops_callout
|
||||
OpenStruct.new(enabled: auto_devops_enabled?,
|
||||
label: auto_devops_enabled? ? _('Auto DevOps enabled') : _('Enable Auto DevOps'),
|
||||
link: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
|
||||
link: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
|
||||
elsif auto_devops_enabled?
|
||||
OpenStruct.new(enabled: true,
|
||||
label: _('Auto DevOps enabled'),
|
||||
|
|
|
@ -65,6 +65,10 @@ class GitlabUploader < CarrierWave::Uploader::Base
|
|||
!!model
|
||||
end
|
||||
|
||||
def local_url
|
||||
File.join('/', self.class.base_dir, dynamic_segment, filename)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Designed to be overridden by child uploaders that have a dynamic path
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
.help-block
|
||||
Manage repository storage paths. Learn more in the
|
||||
= succeed "." do
|
||||
= link_to "repository storages documentation", help_page_path("administration/repository_storages")
|
||||
= link_to "repository storages documentation", help_page_path("administration/repository_storage_paths")
|
||||
.sub-section
|
||||
%h4 Circuit breaker
|
||||
.form-group
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
%hr
|
||||
%p
|
||||
- link_to_auto_devops_settings = link_to(s_('AutoDevOps|enable Auto DevOps (Beta)'), project_settings_ci_cd_path(@project, anchor: 'js-general-pipeline-settings'))
|
||||
- link_to_auto_devops_settings = link_to(s_('AutoDevOps|enable Auto DevOps (Beta)'), project_settings_ci_cd_path(@project, anchor: 'autodevops-settings'))
|
||||
- link_to_add_kubernetes_cluster = link_to(s_('AutoDevOps|add a Kubernetes cluster'), new_project_cluster_path(@project))
|
||||
= s_('AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}.').html_safe % { link_to_auto_devops_settings: link_to_auto_devops_settings, link_to_add_kubernetes_cluster: link_to_add_kubernetes_cluster }
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
- unless @repository.gitlab_ci_yml
|
||||
= link_to 'Get started with Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
|
||||
|
||||
= link_to ci_lint_path, class: 'btn btn-default' do
|
||||
= link_to project_ci_lint_path(@project), class: 'btn btn-default' do
|
||||
%span CI lint
|
||||
|
||||
.content-list.builds-content-list
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
%ul
|
||||
- pipeline.yaml_errors.split(",").each do |error|
|
||||
%li= error
|
||||
You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path}
|
||||
You can also test your .gitlab-ci.yml in the #{link_to "Lint", project_ci_lint_path(@project)}
|
||||
|
||||
- if pipeline.project.builds_enabled? && !pipeline.ci_yaml_file
|
||||
.bs-callout.bs-callout-warning
|
||||
|
|
40
app/views/projects/settings/ci_cd/_autodevops_form.html.haml
Normal file
40
app/views/projects/settings/ci_cd/_autodevops_form.html.haml
Normal file
|
@ -0,0 +1,40 @@
|
|||
.row.prepend-top-default
|
||||
.col-lg-12
|
||||
= form_for @project, url: project_settings_ci_cd_path(@project) do |f|
|
||||
= form_errors(@project)
|
||||
%fieldset.builds-feature
|
||||
.form-group
|
||||
- message = auto_devops_warning_message(@project)
|
||||
- ci_file_formatted = '<code>.gitlab-ci.yml</code>'.html_safe
|
||||
- if message
|
||||
%p.settings-message.text-center
|
||||
= message.html_safe
|
||||
= f.fields_for :auto_devops_attributes, @auto_devops do |form|
|
||||
.radio
|
||||
= form.label :enabled_true do
|
||||
= form.radio_button :enabled, 'true'
|
||||
%strong= s_('CICD|Enable Auto DevOps')
|
||||
%br
|
||||
= s_('CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project.').html_safe % { ci_file: ci_file_formatted }
|
||||
|
||||
.radio
|
||||
= form.label :enabled_false do
|
||||
= form.radio_button :enabled, 'false'
|
||||
%strong= s_('CICD|Disable Auto DevOps')
|
||||
%br
|
||||
= s_('CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery.').html_safe % { ci_file: ci_file_formatted }
|
||||
|
||||
.radio
|
||||
= form.label :enabled_ do
|
||||
= form.radio_button :enabled, ''
|
||||
%strong= s_('CICD|Instance default (%{state})') % { state: "#{Gitlab::CurrentSettings.auto_devops_enabled? ? _('enabled') : _('disabled')}" }
|
||||
%br
|
||||
= s_('CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}.').html_safe % { ci_file: ci_file_formatted }
|
||||
|
||||
= form.label :domain, class:"prepend-top-10" do
|
||||
= _('Domain')
|
||||
= form.text_field :domain, class: 'form-control', placeholder: 'domain.com'
|
||||
.help-block
|
||||
= s_('CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages.')
|
||||
|
||||
= f.submit 'Save changes', class: "btn btn-success prepend-top-15"
|
|
@ -3,44 +3,6 @@
|
|||
= form_for @project, url: project_settings_ci_cd_path(@project) do |f|
|
||||
= form_errors(@project)
|
||||
%fieldset.builds-feature
|
||||
.form-group
|
||||
%h5 Auto DevOps (Beta)
|
||||
%p
|
||||
Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration.
|
||||
= link_to 'Learn more about Auto DevOps', help_page_path('topics/autodevops/index.md')
|
||||
- message = auto_devops_warning_message(@project)
|
||||
- if message
|
||||
%p.settings-message.text-center
|
||||
= message.html_safe
|
||||
= f.fields_for :auto_devops_attributes, @auto_devops do |form|
|
||||
.radio
|
||||
= form.label :enabled_true do
|
||||
= form.radio_button :enabled, 'true'
|
||||
%strong Enable Auto DevOps
|
||||
%br
|
||||
%span.descr
|
||||
The Auto DevOps pipeline configuration will be used when there is no <code>.gitlab-ci.yml</code> in the project.
|
||||
|
||||
.radio
|
||||
= form.label :enabled_false do
|
||||
= form.radio_button :enabled, 'false'
|
||||
%strong Disable Auto DevOps
|
||||
%br
|
||||
%span.descr
|
||||
An explicit <code>.gitlab-ci.yml</code> needs to be specified before you can begin using Continuous Integration and Delivery.
|
||||
|
||||
.radio
|
||||
= form.label :enabled_ do
|
||||
= form.radio_button :enabled, ''
|
||||
%strong Instance default (#{Gitlab::CurrentSettings.auto_devops_enabled? ? 'enabled' : 'disabled'})
|
||||
%br
|
||||
%span.descr
|
||||
Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific <code>.gitlab-ci.yml</code>.
|
||||
%p
|
||||
You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages.
|
||||
= form.text_field :domain, class: 'form-control', placeholder: 'domain.com'
|
||||
|
||||
%hr
|
||||
.form-group.append-bottom-default.js-secret-runner-token
|
||||
= f.label :runners_token, "Runner token", class: 'label-light'
|
||||
.form-control.js-secret-value-placeholder
|
||||
|
|
|
@ -12,10 +12,22 @@
|
|||
%button.btn.js-settings-toggle{ type: 'button' }
|
||||
= expanded ? 'Collapse' : 'Expand'
|
||||
%p
|
||||
Update your CI/CD configuration, like job timeout or Auto DevOps.
|
||||
Access your runner token, customize your pipeline configuration, and view your pipeline status and coverage report.
|
||||
.settings-content
|
||||
= render 'form'
|
||||
|
||||
%section.settings#autodevops-settings.no-animate{ class: ('expanded' if expanded) }
|
||||
.settings-header
|
||||
%h4
|
||||
= s_('CICD|Auto DevOps (Beta)')
|
||||
%button.btn.btn-default.js-settings-toggle{ type: 'button' }
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p
|
||||
= s_('CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration.')
|
||||
= link_to s_('CICD|Learn more about Auto DevOps'), help_page_path('topics/autodevops/index.md')
|
||||
.settings-content
|
||||
= render 'autodevops_form'
|
||||
|
||||
%section.settings.no-animate{ class: ('expanded' if expanded) }
|
||||
.settings-header
|
||||
%h4
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
- link = link_to(s_('AutoDevOps|Auto DevOps documentation'), help_page_path('topics/autodevops/index.md'), target: '_blank', rel: 'noopener noreferrer')
|
||||
= s_('AutoDevOps|Learn more in the %{link_to_documentation}').html_safe % { link_to_documentation: link }
|
||||
.banner-buttons
|
||||
= link_to s_('AutoDevOps|Enable in settings'), project_settings_ci_cd_path(@project, anchor: 'js-general-pipeline-settings'), class: 'btn js-close-callout'
|
||||
= link_to s_('AutoDevOps|Enable in settings'), project_settings_ci_cd_path(@project, anchor: 'autodevops-settings'), class: 'btn js-close-callout'
|
||||
|
||||
%button.btn-transparent.banner-close.close.js-close-callout{ type: 'button',
|
||||
'aria-label' => 'Dismiss Auto DevOps box' }
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Prevent pipeline actions in dropdown to redirct to a new page
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
5
changelogs/unreleased/39345-get-raw-archive.yml
Normal file
5
changelogs/unreleased/39345-get-raw-archive.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add an API endpoint to download git repository snapshots
|
||||
merge_request: 18173
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Show new branch/mr button even when branch exists
|
||||
merge_request: 17712
|
||||
author: Jacopo Beschi @jacopo-beschi
|
||||
type: added
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Fix discussions API setting created_at for notable in a group or notable in
|
||||
a project in a group with owners
|
||||
merge_request: 18464
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Create settings section for autodevops
|
||||
merge_request: 18321
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fixed wrong avatar URL when the avatar is on object storage.
|
||||
merge_request: 18092
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix project creation for user endpoint when jobs_enabled parameter supplied
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
5
changelogs/unreleased/45625-job-log-shows-no-job-log.yml
Normal file
5
changelogs/unreleased/45625-job-log-shows-no-job-log.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Removes 'No Job log' message from build trace
|
||||
merge_request: 18523
|
||||
author:
|
||||
type: fixed
|
5
changelogs/unreleased/45666-project-ci-lint-links.yml
Normal file
5
changelogs/unreleased/45666-project-ci-lint-links.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update links to /ci/lint with ones to project ci/lint
|
||||
merge_request: 18539
|
||||
author: Takuya Noguchi
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Transition to atomic internal ids for all models.
|
||||
merge_request: 44259
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add missing changelog type to docs
|
||||
merge_request: 18526
|
||||
author: "@blackst0ne"
|
||||
type: other
|
5
changelogs/unreleased/dz-add-2fa-filter-admin-api.yml
Normal file
5
changelogs/unreleased/dz-add-2fa-filter-admin-api.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add 2FA filter to users API for admins only
|
||||
merge_request: 18503
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix missing namespace for some internal users
|
||||
merge_request: 18357
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Don't include lfs_file_locks data in export bundle
|
||||
merge_request: 18495
|
||||
author:
|
||||
type: fixed
|
5
changelogs/unreleased/update-doorkeeper-changelog.yml
Normal file
5
changelogs/unreleased/update-doorkeeper-changelog.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update doorkeeper to 4.3.2 to fix GitLab OAuth authentication
|
||||
merge_request: 18543
|
||||
author:
|
||||
type: fixed
|
|
@ -1,131 +1,4 @@
|
|||
# rubocop:disable GitlabSecurity/PublicSend
|
||||
|
||||
require_dependency Rails.root.join('lib/gitlab') # Load Gitlab as soon as possible
|
||||
|
||||
class Settings < Settingslogic
|
||||
source ENV.fetch('GITLAB_CONFIG') { "#{Rails.root}/config/gitlab.yml" }
|
||||
namespace Rails.env
|
||||
|
||||
class << self
|
||||
def gitlab_on_standard_port?
|
||||
on_standard_port?(gitlab)
|
||||
end
|
||||
|
||||
def host_without_www(url)
|
||||
host(url).sub('www.', '')
|
||||
end
|
||||
|
||||
def build_gitlab_ci_url
|
||||
custom_port =
|
||||
if on_standard_port?(gitlab)
|
||||
nil
|
||||
else
|
||||
":#{gitlab.port}"
|
||||
end
|
||||
|
||||
[
|
||||
gitlab.protocol,
|
||||
"://",
|
||||
gitlab.host,
|
||||
custom_port,
|
||||
gitlab.relative_url_root
|
||||
].join('')
|
||||
end
|
||||
|
||||
def build_pages_url
|
||||
base_url(pages).join('')
|
||||
end
|
||||
|
||||
def build_gitlab_shell_ssh_path_prefix
|
||||
user_host = "#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}"
|
||||
|
||||
if gitlab_shell.ssh_port != 22
|
||||
"ssh://#{user_host}:#{gitlab_shell.ssh_port}/"
|
||||
else
|
||||
if gitlab_shell.ssh_host.include? ':'
|
||||
"[#{user_host}]:"
|
||||
else
|
||||
"#{user_host}:"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def build_base_gitlab_url
|
||||
base_url(gitlab).join('')
|
||||
end
|
||||
|
||||
def build_gitlab_url
|
||||
(base_url(gitlab) + [gitlab.relative_url_root]).join('')
|
||||
end
|
||||
|
||||
# check that values in `current` (string or integer) is a contant in `modul`.
|
||||
def verify_constant_array(modul, current, default)
|
||||
values = default || []
|
||||
unless current.nil?
|
||||
values = []
|
||||
current.each do |constant|
|
||||
values.push(verify_constant(modul, constant, nil))
|
||||
end
|
||||
values.delete_if { |value| value.nil? }
|
||||
end
|
||||
|
||||
values
|
||||
end
|
||||
|
||||
# check that `current` (string or integer) is a contant in `modul`.
|
||||
def verify_constant(modul, current, default)
|
||||
constant = modul.constants.find { |name| modul.const_get(name) == current }
|
||||
value = constant.nil? ? default : modul.const_get(constant)
|
||||
if current.is_a? String
|
||||
value = modul.const_get(current.upcase) rescue default
|
||||
end
|
||||
|
||||
value
|
||||
end
|
||||
|
||||
def absolute(path)
|
||||
File.expand_path(path, Rails.root)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def base_url(config)
|
||||
custom_port = on_standard_port?(config) ? nil : ":#{config.port}"
|
||||
|
||||
[
|
||||
config.protocol,
|
||||
"://",
|
||||
config.host,
|
||||
custom_port
|
||||
]
|
||||
end
|
||||
|
||||
def on_standard_port?(config)
|
||||
config.port.to_i == (config.https ? 443 : 80)
|
||||
end
|
||||
|
||||
# Extract the host part of the given +url+.
|
||||
def host(url)
|
||||
url = url.downcase
|
||||
url = "http://#{url}" unless url.start_with?('http')
|
||||
|
||||
# Get rid of the path so that we don't even have to encode it
|
||||
url_without_path = url.sub(%r{(https?://[^/]+)/?.*}, '\1')
|
||||
|
||||
URI.parse(url_without_path).host
|
||||
end
|
||||
|
||||
# Runs every minute in a random ten-minute period on Sundays, to balance the
|
||||
# load on the server receiving these pings. The usage ping is safe to run
|
||||
# multiple times because of a 24 hour exclusive lock.
|
||||
def cron_for_usage_ping
|
||||
hour = rand(24)
|
||||
minute = rand(6)
|
||||
|
||||
"#{minute}0-#{minute}9 #{hour} * * 0"
|
||||
end
|
||||
end
|
||||
end
|
||||
require_dependency File.expand_path('../../lib/gitlab', __dir__) # Load Gitlab as soon as possible
|
||||
|
||||
# Default settings
|
||||
Settings['ldap'] ||= Settingslogic.new({})
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
module Gitlab
|
||||
def self.config
|
||||
Settings
|
||||
end
|
||||
|
||||
VERSION = File.read(Rails.root.join("VERSION")).strip.freeze
|
||||
REVISION = Gitlab::Popen.popen(%W(#{config.git.bin_path} log --pretty=format:%h -n 1)).first.chomp.freeze
|
||||
end
|
|
@ -1,5 +1,5 @@
|
|||
require './spec/support/sidekiq'
|
||||
require './spec/support/test_env'
|
||||
require './spec/support/helpers/test_env'
|
||||
|
||||
class Gitlab::Seeder::CycleAnalytics
|
||||
def initialize(project, perf: false)
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
class CreateMissingNamespaceForInternalUsers < ActiveRecord::Migration
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
connection.exec_query(users_query.to_sql).rows.each do |id, username|
|
||||
create_namespace(id, username)
|
||||
# When testing locally I've noticed that these internal users are missing
|
||||
# the notification email, for more details visit the below link:
|
||||
# https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18357#note_68327560
|
||||
set_notification_email(id)
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def users
|
||||
@users ||= Arel::Table.new(:users)
|
||||
end
|
||||
|
||||
def namespaces
|
||||
@namespaces ||= Arel::Table.new(:namespaces)
|
||||
end
|
||||
|
||||
def users_query
|
||||
condition = users[:ghost].eq(true)
|
||||
|
||||
if column_exists?(:users, :support_bot)
|
||||
condition = condition.or(users[:support_bot].eq(true))
|
||||
end
|
||||
|
||||
users.join(namespaces, Arel::Nodes::OuterJoin)
|
||||
.on(namespaces[:type].eq(nil).and(namespaces[:owner_id].eq(users[:id])))
|
||||
.where(namespaces[:owner_id].eq(nil))
|
||||
.where(condition)
|
||||
.project(users[:id], users[:username])
|
||||
end
|
||||
|
||||
def create_namespace(user_id, username)
|
||||
path = Uniquify.new.string(username) do |str|
|
||||
query = "SELECT id FROM namespaces WHERE parent_id IS NULL AND path='#{str}' LIMIT 1"
|
||||
connection.exec_query(query).present?
|
||||
end
|
||||
|
||||
insert_query = "INSERT INTO namespaces(owner_id, path, name) VALUES(#{user_id}, '#{path}', '#{path}')"
|
||||
namespace_id = connection.insert_sql(insert_query)
|
||||
|
||||
create_route(namespace_id)
|
||||
end
|
||||
|
||||
def create_route(namespace_id)
|
||||
return unless namespace_id
|
||||
|
||||
row = connection.exec_query("SELECT id, path FROM namespaces WHERE id=#{namespace_id}").first
|
||||
id, path = row.values_at('id', 'path')
|
||||
|
||||
execute("INSERT INTO routes(source_id, source_type, path, name) VALUES(#{id}, 'Namespace', '#{path}', '#{path}')")
|
||||
end
|
||||
|
||||
def set_notification_email(user_id)
|
||||
execute "UPDATE users SET notification_email = email WHERE notification_email IS NULL AND id = #{user_id}"
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
class AddFurtherScopeColumnsToInternalIdTable < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
change_column_null :internal_ids, :project_id, true
|
||||
add_column :internal_ids, :namespace_id, :integer, null: true
|
||||
end
|
||||
|
||||
def down
|
||||
change_column_null :internal_ids, :project_id, false
|
||||
remove_column :internal_ids, :namespace_id
|
||||
end
|
||||
end
|
|
@ -0,0 +1,40 @@
|
|||
class AddIndexConstraintsToInternalIdTable < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_index :internal_ids, [:usage, :namespace_id], unique: true, where: 'namespace_id IS NOT NULL'
|
||||
|
||||
replace_index(:internal_ids, [:usage, :project_id], name: 'index_internal_ids_on_usage_and_project_id') do
|
||||
add_concurrent_index :internal_ids, [:usage, :project_id], unique: true, where: 'project_id IS NOT NULL'
|
||||
end
|
||||
|
||||
add_concurrent_foreign_key :internal_ids, :namespaces, column: :namespace_id, on_delete: :cascade
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index :internal_ids, [:usage, :namespace_id]
|
||||
|
||||
replace_index(:internal_ids, [:usage, :project_id], name: 'index_internal_ids_on_usage_and_project_id') do
|
||||
add_concurrent_index :internal_ids, [:usage, :project_id], unique: true
|
||||
end
|
||||
|
||||
remove_foreign_key :internal_ids, column: :namespace_id
|
||||
end
|
||||
|
||||
private
|
||||
def replace_index(table, columns, name:)
|
||||
temporary_name = "#{name}_old"
|
||||
|
||||
if index_exists?(table, columns, name: name)
|
||||
rename_index table, name, temporary_name
|
||||
end
|
||||
|
||||
yield
|
||||
|
||||
remove_concurrent_index_by_name table, temporary_name
|
||||
end
|
||||
end
|
|
@ -896,12 +896,14 @@ ActiveRecord::Schema.define(version: 20180418053107) do
|
|||
add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree
|
||||
|
||||
create_table "internal_ids", id: :bigserial, force: :cascade do |t|
|
||||
t.integer "project_id", null: false
|
||||
t.integer "project_id"
|
||||
t.integer "usage", null: false
|
||||
t.integer "last_value", null: false
|
||||
t.integer "namespace_id"
|
||||
end
|
||||
|
||||
add_index "internal_ids", ["usage", "project_id"], name: "index_internal_ids_on_usage_and_project_id", unique: true, using: :btree
|
||||
add_index "internal_ids", ["usage", "namespace_id"], name: "index_internal_ids_on_usage_and_namespace_id", unique: true, where: "(namespace_id IS NOT NULL)", using: :btree
|
||||
add_index "internal_ids", ["usage", "project_id"], name: "index_internal_ids_on_usage_and_project_id", unique: true, where: "(project_id IS NOT NULL)", using: :btree
|
||||
|
||||
create_table "issue_assignees", id: false, force: :cascade do |t|
|
||||
t.integer "user_id", null: false
|
||||
|
@ -2113,6 +2115,7 @@ ActiveRecord::Schema.define(version: 20180418053107) do
|
|||
add_foreign_key "gpg_signatures", "gpg_keys", on_delete: :nullify
|
||||
add_foreign_key "gpg_signatures", "projects", on_delete: :cascade
|
||||
add_foreign_key "group_custom_attributes", "namespaces", column: "group_id", on_delete: :cascade
|
||||
add_foreign_key "internal_ids", "namespaces", name: "fk_162941d509", on_delete: :cascade
|
||||
add_foreign_key "internal_ids", "projects", on_delete: :cascade
|
||||
add_foreign_key "issue_assignees", "issues", name: "fk_b7d881734a", on_delete: :cascade
|
||||
add_foreign_key "issue_assignees", "users", name: "fk_5e0c8d9154", on_delete: :cascade
|
||||
|
|
|
@ -650,6 +650,47 @@ gitlab_rails['redis_sentinels'] = [
|
|||
Omnibus GitLab configures some things behind the curtains to make the sysadmins'
|
||||
lives easier. If you want to know what happens underneath keep reading.
|
||||
|
||||
### Running multiple Redis clusters
|
||||
|
||||
GitLab supports running [separate Redis clusters for different persistent
|
||||
classes](https://docs.gitlab.com/omnibus/settings/redis.html#running-with-multiple-redis-instances):
|
||||
cache, queues, and shared_state. To make this work with Sentinel:
|
||||
|
||||
1. Set the appropriate variable in `/etc/gitlab/gitlab.rb` for each instance you are using:
|
||||
|
||||
```ruby
|
||||
gitlab_rails['redis_cache_instance'] = REDIS_CACHE_URL
|
||||
gitlab_rails['redis_queues_instance'] = REDIS_QUEUES_URL
|
||||
gitlab_rails['redis_shared_state_instance'] = REDIS_SHARED_STATE_URL
|
||||
```
|
||||
**Note**: Redis URLs should be in the format: `redis://:PASSWORD@SENTINEL_MASTER_NAME`
|
||||
|
||||
1. PASSWORD is the plaintext password for the Redis instance
|
||||
2. SENTINEL_MASTER_NAME is the Sentinel master name (e.g. `gitlab-redis-cache`)
|
||||
1. Include an array of hashes with host/port combinations, such as the following:
|
||||
|
||||
```ruby
|
||||
gitlab_rails['redis_cache_sentinels'] = [
|
||||
{ host: REDIS_CACHE_SENTINEL_HOST, port: PORT1 },
|
||||
{ host: REDIS_CACHE_SENTINEL_HOST2, port: PORT2 }
|
||||
]
|
||||
gitlab_rails['redis_queues_sentinels'] = [
|
||||
{ host: REDIS_QUEUES_SENTINEL_HOST, port: PORT1 },
|
||||
{ host: REDIS_QUEUES_SENTINEL_HOST2, port: PORT2 }
|
||||
]
|
||||
gitlab_rails['redis_shared_state_sentinels'] = [
|
||||
{ host: SHARED_STATE_SENTINEL_HOST, port: PORT1 },
|
||||
{ host: SHARED_STATE_SENTINEL_HOST2, port: PORT2 }
|
||||
]
|
||||
```
|
||||
1. Note that for each persistence class, GitLab will default to using the
|
||||
configuration specified in `gitlab_rails['redis_sentinels']` unless
|
||||
overriden by the settings above.
|
||||
1. Be sure to include BOTH configuration options for each persistent classes. For example,
|
||||
if you choose to configure a cache instance, you must specify both `gitlab_rails['redis_cache_instance']`
|
||||
and `gitlab_rails['redis_cache_sentinels']` for GitLab to generate the proper configuration files.
|
||||
1. Run `gitlab-ctl reconfigure`
|
||||
|
||||
### Control running services
|
||||
|
||||
In the previous example, we've used `redis_sentinel_role` and
|
||||
|
|
|
@ -119,11 +119,17 @@ The Pages daemon doesn't listen to the outside world.
|
|||
|
||||
1. Set the external URL for GitLab Pages in `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
```shell
|
||||
pages_external_url 'http://example.io'
|
||||
```
|
||||
|
||||
1. [Reconfigure GitLab][reconfigure]
|
||||
1. Restart gitlab-pages by running the following command:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl restart gitlab-pages
|
||||
```
|
||||
|
||||
|
||||
Watch the [video tutorial][video-admin] for this configuration.
|
||||
|
||||
|
@ -143,7 +149,7 @@ outside world.
|
|||
1. Place the certificate and key inside `/etc/gitlab/ssl`
|
||||
1. In `/etc/gitlab/gitlab.rb` specify the following configuration:
|
||||
|
||||
```ruby
|
||||
```shell
|
||||
pages_external_url 'https://example.io'
|
||||
|
||||
pages_nginx['redirect_http_to_https'] = true
|
||||
|
@ -155,6 +161,11 @@ outside world.
|
|||
respectively.
|
||||
|
||||
1. [Reconfigure GitLab][reconfigure]
|
||||
1. Restart gitlab-pages by running the following command:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl restart gitlab-pages
|
||||
```
|
||||
|
||||
## Advanced configuration
|
||||
|
||||
|
@ -180,7 +191,7 @@ world. Custom domains are supported, but no TLS.
|
|||
|
||||
1. Edit `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
```shell
|
||||
pages_external_url "http://example.io"
|
||||
nginx['listen_addresses'] = ['1.1.1.1']
|
||||
pages_nginx['enable'] = false
|
||||
|
@ -192,6 +203,11 @@ world. Custom domains are supported, but no TLS.
|
|||
listens on. If you don't have IPv6, you can omit the IPv6 address.
|
||||
|
||||
1. [Reconfigure GitLab][reconfigure]
|
||||
1. Restart gitlab-pages by running the following command:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl restart gitlab-pages
|
||||
```
|
||||
|
||||
### Custom domains with TLS support
|
||||
|
||||
|
@ -210,7 +226,7 @@ world. Custom domains and TLS are supported.
|
|||
|
||||
1. Edit `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
```shell
|
||||
pages_external_url "https://example.io"
|
||||
nginx['listen_addresses'] = ['1.1.1.1']
|
||||
pages_nginx['enable'] = false
|
||||
|
@ -225,6 +241,11 @@ world. Custom domains and TLS are supported.
|
|||
listens on. If you don't have IPv6, you can omit the IPv6 address.
|
||||
|
||||
1. [Reconfigure GitLab][reconfigure]
|
||||
1. Restart gitlab-pages by running the following command:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl restart gitlab-pages
|
||||
```
|
||||
|
||||
### Custom domain verification
|
||||
|
||||
|
@ -247,11 +268,16 @@ are stored.
|
|||
If you wish to store them in another location you must set it up in
|
||||
`/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
```shell
|
||||
gitlab_rails['pages_path'] = "/mnt/storage/pages"
|
||||
```
|
||||
|
||||
1. [Reconfigure GitLab][reconfigure]
|
||||
1. Restart gitlab-pages by running the following command:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl restart gitlab-pages
|
||||
```
|
||||
|
||||
## Set maximum pages size
|
||||
|
||||
|
|
|
@ -112,13 +112,13 @@ POST /projects/import
|
|||
| `file` | string | yes | The file to be uploaded |
|
||||
| `path` | string | yes | Name and path for new project |
|
||||
| `overwrite` | boolean | no | If there is a project with the same path the import will overwrite it. Default to false |
|
||||
| `override_params` | Hash | no | Supports all fields defined in the [Project API](projects.md)] |
|
||||
| `override_params` | Hash | no | Supports all fields defined in the [Project API](projects.md) |
|
||||
|
||||
The override params passed will take precendence over all values defined inside the export file.
|
||||
The override params passed will take precedence over all values defined inside the export file.
|
||||
|
||||
To upload a file from your filesystem, use the `--form` argument. This causes
|
||||
To upload a file from your file system, 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
|
||||
The `file=` parameter must point to a file on your file system and be preceded
|
||||
by `@`. For example:
|
||||
|
||||
```console
|
||||
|
|
|
@ -1399,3 +1399,26 @@ Read more in the [Project Badges](project_badges.md) documentation.
|
|||
## Issue and merge request description templates
|
||||
|
||||
The non-default [issue and merge request description templates](../user/project/description_templates.md) are managed inside the project's repository. So you can manage them via the API through the [Repositories API](repositories.md) and the [Repository Files API](repository_files.md).
|
||||
|
||||
## Download snapshot of a git repository
|
||||
|
||||
> Introduced in GitLab 10.7
|
||||
|
||||
This endpoint may only be accessed by an administrative user.
|
||||
|
||||
Download a snapshot of the project (or wiki, if requested) git repository. This
|
||||
snapshot is always in uncompressed [tar](https://en.wikipedia.org/wiki/Tar_(computing))
|
||||
format.
|
||||
|
||||
If a repository is corrupted to the point where `git clone` does not work, the
|
||||
snapshot may allow some of the data to be retrieved.
|
||||
|
||||
```
|
||||
GET /projects/:id/snapshot
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
|
||||
| `wiki` | boolean | no | Whether to download the wiki, rather than project, repository |
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ GET /users
|
|||
| --------- | ---- | -------- | ----------- |
|
||||
| `order_by` | string | no | Return projects ordered by `id`, `name`, `username`, `created_at`, or `updated_at` fields. Default is `id` |
|
||||
| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
|
||||
| `two_factor` | string | no | Filter users by Two-factor authentication. Filter values are `enabled` or `disabled`. By default it returns all users |
|
||||
|
||||
```json
|
||||
[
|
||||
|
|
|
@ -22,7 +22,7 @@ The `merge_request` value is a reference to a merge request that adds this
|
|||
entry, and the `author` key is used to give attribution to community
|
||||
contributors. **Both are optional**.
|
||||
The `type` field maps the category of the change,
|
||||
valid options are: added, fixed, changed, deprecated, removed, security, other. **Type field is mandatory**.
|
||||
valid options are: added, fixed, changed, deprecated, removed, security, performance, other. **Type field is mandatory**.
|
||||
|
||||
Community contributors and core team members are encouraged to add their name to
|
||||
the `author` field. GitLab team members **should not**.
|
||||
|
|
|
@ -90,6 +90,25 @@ Finished in 34.51 seconds (files took 0.76702 seconds to load)
|
|||
|
||||
Note: `live_debug` only works on javascript enabled specs.
|
||||
|
||||
### Fast unit tests
|
||||
|
||||
Some classes are well-isolated from Rails and you should be able to test them
|
||||
without the overhead added by the Rails environment and Bundler's `:default`
|
||||
group's gem loading. In these cases, you can `require 'fast_spec_helper'`
|
||||
instead of `require 'spec_helper'` in your test file, and your test should run
|
||||
really fast since:
|
||||
|
||||
- Gems loading is skipped
|
||||
- Rails app boot is skipped
|
||||
- gitlab-shell and Gitaly setup are skipped
|
||||
- Test repositories setup are skipped
|
||||
|
||||
Note that in some cases, you might have to add some `require_dependency 'foo'`
|
||||
in your file under test since Rails autoloading is not available in these cases.
|
||||
|
||||
This shouldn't be a problem since explicitely listing dependencies should be
|
||||
considered a good practice anyway.
|
||||
|
||||
### `let` variables
|
||||
|
||||
GitLab's RSpec suite has made extensive use of `let` variables to reduce
|
||||
|
@ -281,14 +300,13 @@ All fixtures should be be placed under `spec/fixtures/`.
|
|||
|
||||
RSpec config files are files that change the RSpec config (i.e.
|
||||
`RSpec.configure do |config|` blocks). They should be placed under
|
||||
`spec/support/config/`.
|
||||
`spec/support/`.
|
||||
|
||||
Each file should be related to a specific domain, e.g.
|
||||
`spec/support/config/capybara.rb`, `spec/support/config/carrierwave.rb`, etc.
|
||||
`spec/support/capybara.rb`, `spec/support/carrierwave.rb`, etc.
|
||||
|
||||
Helpers can be included in the `spec/support/config/rspec.rb` file. If a
|
||||
helpers module applies only to a certain kind of specs, it should add modifiers
|
||||
to the `config.include` call. For instance if
|
||||
If a helpers module applies only to a certain kind of specs, it should add
|
||||
modifiers to the `config.include` call. For instance if
|
||||
`spec/support/helpers/cycle_analytics_helpers.rb` applies to `:lib` and
|
||||
`type: :model` specs only, you would write the following:
|
||||
|
||||
|
@ -299,6 +317,14 @@ RSpec.configure do |config|
|
|||
end
|
||||
```
|
||||
|
||||
If a config file only consists of `config.include`, you can add these
|
||||
`config.include` directly in `spec/spec_helper.rb`.
|
||||
|
||||
For very generic helpers, consider including them in the `spec/support/rspec.rb`
|
||||
file which is used by the `spec/fast_spec_helper.rb` file. See
|
||||
[Fast unit tests](#fast-unit-tests) for more details about the
|
||||
`spec/fast_spec_helper.rb` file.
|
||||
|
||||
---
|
||||
|
||||
[Return to Testing documentation](index.md)
|
||||
|
|
|
@ -9,7 +9,7 @@ At a minimum, requiring the Rake helper will redirect `stdout`, include the
|
|||
runtime task helpers, and include the `RakeHelpers` Spec support module.
|
||||
|
||||
The `RakeHelpers` module exposes a `run_rake_task(<task>)` method to make
|
||||
executing tasks simple. See `spec/support/rake_helpers.rb` for all available
|
||||
executing tasks simple. See `spec/support/helpers/rake_helpers.rb` for all available
|
||||
methods.
|
||||
|
||||
Example:
|
||||
|
|
|
@ -23,7 +23,7 @@ You can create as many deploy tokens as you like from the settings of your proje
|
|||
|
||||
![Personal access tokens page](img/deploy_tokens.png)
|
||||
|
||||
## Revoking a personal access token
|
||||
## Revoking a deploy token
|
||||
|
||||
At any time, you can revoke any deploy token by just clicking the
|
||||
respective **Revoke** button under the 'Active deploy tokens' area.
|
||||
|
|
|
@ -12,7 +12,11 @@ end
|
|||
|
||||
WebMock.enable!
|
||||
|
||||
%w(select2_helper test_env repo_helpers wait_for_requests sidekiq project_forks_helper webmock).each do |f|
|
||||
%w(select2_helper test_env repo_helpers wait_for_requests project_forks_helper).each do |f|
|
||||
require Rails.root.join('spec', 'support', 'helpers', f)
|
||||
end
|
||||
|
||||
%w(sidekiq webmock).each do |f|
|
||||
require Rails.root.join('spec', 'support', f)
|
||||
end
|
||||
|
||||
|
|
|
@ -154,6 +154,7 @@ module API
|
|||
mount ::API::ProjectHooks
|
||||
mount ::API::Projects
|
||||
mount ::API::ProjectMilestones
|
||||
mount ::API::ProjectSnapshots
|
||||
mount ::API::ProjectSnippets
|
||||
mount ::API::ProtectedBranches
|
||||
mount ::API::Repositories
|
||||
|
|
|
@ -64,8 +64,10 @@ module API
|
|||
authorize! :create_note, noteable
|
||||
|
||||
parent = noteable_parent(noteable)
|
||||
|
||||
if opts[:created_at]
|
||||
opts.delete(:created_at) unless current_user.admin? || parent.owner == current_user
|
||||
opts.delete(:created_at) unless
|
||||
current_user.admin? || parent.owned_by?(current_user)
|
||||
end
|
||||
|
||||
project = parent if parent.is_a?(Project)
|
||||
|
|
25
lib/api/helpers/project_snapshots_helpers.rb
Normal file
25
lib/api/helpers/project_snapshots_helpers.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
module API
|
||||
module Helpers
|
||||
module ProjectSnapshotsHelpers
|
||||
def authorize_read_git_snapshot!
|
||||
authenticated_with_full_private_access!
|
||||
end
|
||||
|
||||
def send_git_snapshot(repository)
|
||||
header(*Gitlab::Workhorse.send_git_snapshot(repository))
|
||||
end
|
||||
|
||||
def snapshot_project
|
||||
user_project
|
||||
end
|
||||
|
||||
def snapshot_repository
|
||||
if to_boolean(params[:wiki])
|
||||
snapshot_project.wiki.repository
|
||||
else
|
||||
snapshot_project.repository
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
19
lib/api/project_snapshots.rb
Normal file
19
lib/api/project_snapshots.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
module API
|
||||
class ProjectSnapshots < Grape::API
|
||||
helpers ::API::Helpers::ProjectSnapshotsHelpers
|
||||
|
||||
before { authorize_read_git_snapshot! }
|
||||
|
||||
resource :projects do
|
||||
desc 'Download a (possibly inconsistent) snapshot of a repository' do
|
||||
detail 'This feature was introduced in GitLab 10.7'
|
||||
end
|
||||
params do
|
||||
optional :wiki, type: Boolean, desc: 'Set to true to receive the wiki repository'
|
||||
end
|
||||
get ':id/snapshot' do
|
||||
send_git_snapshot(snapshot_repository)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -74,6 +74,11 @@ module API
|
|||
|
||||
present options[:with].prepare_relation(projects, options), options
|
||||
end
|
||||
|
||||
def translate_params_for_compatibility(params)
|
||||
params[:builds_enabled] = params.delete(:jobs_enabled) if params.key?(:jobs_enabled)
|
||||
params
|
||||
end
|
||||
end
|
||||
|
||||
resource :users, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
|
||||
|
@ -123,7 +128,7 @@ module API
|
|||
end
|
||||
post do
|
||||
attrs = declared_params(include_missing: false)
|
||||
attrs[:builds_enabled] = attrs.delete(:jobs_enabled) if attrs.key?(:jobs_enabled)
|
||||
attrs = translate_params_for_compatibility(attrs)
|
||||
project = ::Projects::CreateService.new(current_user, attrs).execute
|
||||
|
||||
if project.saved?
|
||||
|
@ -155,6 +160,7 @@ module API
|
|||
not_found!('User') unless user
|
||||
|
||||
attrs = declared_params(include_missing: false)
|
||||
attrs = translate_params_for_compatibility(attrs)
|
||||
project = ::Projects::CreateService.new(user, attrs).execute
|
||||
|
||||
if project.saved?
|
||||
|
@ -276,7 +282,7 @@ module API
|
|||
authorize! :rename_project, user_project if attrs[:name].present?
|
||||
authorize! :change_visibility_level, user_project if attrs[:visibility].present?
|
||||
|
||||
attrs[:builds_enabled] = attrs.delete(:jobs_enabled) if attrs.key?(:jobs_enabled)
|
||||
attrs = translate_params_for_compatibility(attrs)
|
||||
|
||||
result = ::Projects::UpdateService.new(user_project, current_user, attrs).execute
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ module API
|
|||
authenticated_as_admin! if params[:external].present? || (params[:extern_uid].present? && params[:provider].present?)
|
||||
|
||||
unless current_user&.admin?
|
||||
params.except!(:created_after, :created_before, :order_by, :sort)
|
||||
params.except!(:created_after, :created_before, :order_by, :sort, :two_factor)
|
||||
end
|
||||
|
||||
users = UsersFinder.new(current_user, params).execute
|
||||
|
|
|
@ -1,9 +1,20 @@
|
|||
require_dependency 'gitlab/git'
|
||||
require_dependency 'settings'
|
||||
require_dependency 'gitlab/popen'
|
||||
|
||||
module Gitlab
|
||||
def self.root
|
||||
Pathname.new(File.expand_path('..', __dir__))
|
||||
end
|
||||
|
||||
def self.config
|
||||
Settings
|
||||
end
|
||||
|
||||
COM_URL = 'https://gitlab.com'.freeze
|
||||
APP_DIRS_PATTERN = %r{^/?(app|config|ee|lib|spec|\(\w*\))}
|
||||
SUBDOMAIN_REGEX = %r{\Ahttps://[a-z0-9]+\.gitlab\.com\z}
|
||||
VERSION = File.read(root.join("VERSION")).strip.freeze
|
||||
REVISION = Gitlab::Popen.popen(%W(#{config.git.bin_path} log --pretty=format:%h -n 1)).first.chomp.freeze
|
||||
|
||||
def self.com?
|
||||
# Check `gl_subdomain?` as well to keep parity with gitlab.com
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require_dependency 'gitlab/encoding_helper'
|
||||
|
||||
module Gitlab
|
||||
module Git
|
||||
# The ID of empty tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
module Gitlab
|
||||
module Wiki
|
||||
module Git
|
||||
class CommitterWithHooks < Gollum::Committer
|
||||
attr_reader :gl_wiki
|
||||
|
||||
|
@ -9,6 +9,9 @@ module Gitlab
|
|||
end
|
||||
|
||||
def commit
|
||||
# TODO: Remove after 10.8
|
||||
return super unless allowed_to_run_hooks?
|
||||
|
||||
result = Gitlab::Git::OperationService.new(git_user, gl_wiki.repository).with_branch(
|
||||
@wiki.ref,
|
||||
start_branch_name: @wiki.ref
|
||||
|
@ -24,6 +27,11 @@ module Gitlab
|
|||
|
||||
private
|
||||
|
||||
# TODO: Remove after 10.8
|
||||
def allowed_to_run_hooks?
|
||||
@options[:user_id] != 0 && @options[:username].present?
|
||||
end
|
||||
|
||||
def git_user
|
||||
@git_user ||= Gitlab::Git::User.new(@options[:username],
|
||||
@options[:name],
|
|
@ -1258,6 +1258,10 @@ module Gitlab
|
|||
true
|
||||
end
|
||||
|
||||
def create_from_snapshot(url, auth)
|
||||
gitaly_repository_client.create_from_snapshot(url, auth)
|
||||
end
|
||||
|
||||
def rebase(user, rebase_id, branch:, branch_sha:, remote_repository:, remote_branch:)
|
||||
gitaly_migrate(:rebase) do |is_enabled|
|
||||
if is_enabled
|
||||
|
|
|
@ -290,7 +290,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def committer_with_hooks(commit_details)
|
||||
Gitlab::Wiki::CommitterWithHooks.new(self, commit_details.to_h)
|
||||
Gitlab::Git::CommitterWithHooks.new(self, commit_details.to_h)
|
||||
end
|
||||
|
||||
def with_committer_with_hooks(commit_details, &block)
|
||||
|
|
|
@ -235,6 +235,22 @@ module Gitlab
|
|||
)
|
||||
end
|
||||
|
||||
def create_from_snapshot(http_url, http_auth)
|
||||
request = Gitaly::CreateRepositoryFromSnapshotRequest.new(
|
||||
repository: @gitaly_repo,
|
||||
http_url: http_url,
|
||||
http_auth: http_auth
|
||||
)
|
||||
|
||||
GitalyClient.call(
|
||||
@storage,
|
||||
:repository_service,
|
||||
:create_repository_from_snapshot,
|
||||
request,
|
||||
timeout: GitalyClient.default_timeout
|
||||
)
|
||||
end
|
||||
|
||||
def write_ref(ref_path, ref, old_ref, shell)
|
||||
request = Gitaly::WriteRefRequest.new(
|
||||
repository: @gitaly_repo,
|
||||
|
|
|
@ -27,8 +27,6 @@ project_tree:
|
|||
- :releases
|
||||
- project_members:
|
||||
- :user
|
||||
- lfs_file_locks:
|
||||
- :user
|
||||
- merge_requests:
|
||||
- notes:
|
||||
- :author
|
||||
|
|
|
@ -81,6 +81,20 @@ module Gitlab
|
|||
]
|
||||
end
|
||||
|
||||
def send_git_snapshot(repository)
|
||||
params = {
|
||||
'GitalyServer' => gitaly_server_hash(repository),
|
||||
'GetSnapshotRequest' => Gitaly::GetSnapshotRequest.new(
|
||||
repository: repository.gitaly_repository
|
||||
).to_json
|
||||
}
|
||||
|
||||
[
|
||||
SEND_DATA_HEADER,
|
||||
"git-snapshot:#{encode(params)}"
|
||||
]
|
||||
end
|
||||
|
||||
def send_git_diff(repository, diff_refs)
|
||||
params = if Gitlab::GitalyClient.feature_enabled?(:workhorse_send_git_diff, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT)
|
||||
{
|
||||
|
|
126
lib/settings.rb
Normal file
126
lib/settings.rb
Normal file
|
@ -0,0 +1,126 @@
|
|||
require 'settingslogic'
|
||||
|
||||
class Settings < Settingslogic
|
||||
source ENV.fetch('GITLAB_CONFIG') { Pathname.new(File.expand_path('..', __dir__)).join('config/gitlab.yml') }
|
||||
namespace ENV.fetch('GITLAB_ENV') { Rails.env }
|
||||
|
||||
class << self
|
||||
def gitlab_on_standard_port?
|
||||
on_standard_port?(gitlab)
|
||||
end
|
||||
|
||||
def host_without_www(url)
|
||||
host(url).sub('www.', '')
|
||||
end
|
||||
|
||||
def build_gitlab_ci_url
|
||||
custom_port =
|
||||
if on_standard_port?(gitlab)
|
||||
nil
|
||||
else
|
||||
":#{gitlab.port}"
|
||||
end
|
||||
|
||||
[
|
||||
gitlab.protocol,
|
||||
"://",
|
||||
gitlab.host,
|
||||
custom_port,
|
||||
gitlab.relative_url_root
|
||||
].join('')
|
||||
end
|
||||
|
||||
def build_pages_url
|
||||
base_url(pages).join('')
|
||||
end
|
||||
|
||||
def build_gitlab_shell_ssh_path_prefix
|
||||
user_host = "#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}"
|
||||
|
||||
if gitlab_shell.ssh_port != 22
|
||||
"ssh://#{user_host}:#{gitlab_shell.ssh_port}/"
|
||||
else
|
||||
if gitlab_shell.ssh_host.include? ':'
|
||||
"[#{user_host}]:"
|
||||
else
|
||||
"#{user_host}:"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def build_base_gitlab_url
|
||||
base_url(gitlab).join('')
|
||||
end
|
||||
|
||||
def build_gitlab_url
|
||||
(base_url(gitlab) + [gitlab.relative_url_root]).join('')
|
||||
end
|
||||
|
||||
# check that values in `current` (string or integer) is a contant in `modul`.
|
||||
def verify_constant_array(modul, current, default)
|
||||
values = default || []
|
||||
unless current.nil?
|
||||
values = []
|
||||
current.each do |constant|
|
||||
values.push(verify_constant(modul, constant, nil))
|
||||
end
|
||||
values.delete_if { |value| value.nil? }
|
||||
end
|
||||
|
||||
values
|
||||
end
|
||||
|
||||
# check that `current` (string or integer) is a contant in `modul`.
|
||||
def verify_constant(modul, current, default)
|
||||
constant = modul.constants.find { |name| modul.const_get(name) == current }
|
||||
value = constant.nil? ? default : modul.const_get(constant)
|
||||
if current.is_a? String
|
||||
value = modul.const_get(current.upcase) rescue default
|
||||
end
|
||||
|
||||
value
|
||||
end
|
||||
|
||||
def absolute(path)
|
||||
File.expand_path(path, Rails.root)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def base_url(config)
|
||||
custom_port = on_standard_port?(config) ? nil : ":#{config.port}"
|
||||
|
||||
[
|
||||
config.protocol,
|
||||
"://",
|
||||
config.host,
|
||||
custom_port
|
||||
]
|
||||
end
|
||||
|
||||
def on_standard_port?(config)
|
||||
config.port.to_i == (config.https ? 443 : 80)
|
||||
end
|
||||
|
||||
# Extract the host part of the given +url+.
|
||||
def host(url)
|
||||
url = url.downcase
|
||||
url = "http://#{url}" unless url.start_with?('http')
|
||||
|
||||
# Get rid of the path so that we don't even have to encode it
|
||||
url_without_path = url.sub(%r{(https?://[^/]+)/?.*}, '\1')
|
||||
|
||||
URI.parse(url_without_path).host
|
||||
end
|
||||
|
||||
# Runs every minute in a random ten-minute period on Sundays, to balance the
|
||||
# load on the server receiving these pings. The usage ping is safe to run
|
||||
# multiple times because of a 24 hour exclusive lock.
|
||||
def cron_for_usage_ping
|
||||
hour = rand(24)
|
||||
minute = rand(6)
|
||||
|
||||
"#{minute}0-#{minute}9 #{hour} * * 0"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1776,6 +1776,9 @@ msgstr ""
|
|||
msgid "Failed to change the owner"
|
||||
msgstr ""
|
||||
|
||||
msgid "Failed to check related branches."
|
||||
msgstr ""
|
||||
|
||||
msgid "Failed to remove issue from board, please try again."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module RuboCop
|
||||
module SpecHelpers
|
||||
SPEC_HELPERS = %w[spec_helper.rb rails_helper.rb].freeze
|
||||
SPEC_HELPERS = %w[fast_spec_helper.rb rails_helper.rb spec_helper.rb].freeze
|
||||
|
||||
# Returns true if the given node originated from the spec directory.
|
||||
def in_spec?(node)
|
||||
|
|
|
@ -223,11 +223,12 @@ describe Import::BitbucketController do
|
|||
end
|
||||
|
||||
context 'user has chosen an existing nested namespace and name for the project', :postgresql do
|
||||
let(:parent_namespace) { create(:group, name: 'foo', owner: user) }
|
||||
let(:parent_namespace) { create(:group, name: 'foo') }
|
||||
let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) }
|
||||
let(:test_name) { 'test_name' }
|
||||
|
||||
before do
|
||||
parent_namespace.add_owner(user)
|
||||
nested_namespace.add_owner(user)
|
||||
end
|
||||
|
||||
|
@ -273,7 +274,7 @@ describe Import::BitbucketController do
|
|||
|
||||
context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do
|
||||
let(:test_name) { 'test_name' }
|
||||
let!(:parent_namespace) { create(:group, name: 'foo', owner: user) }
|
||||
let!(:parent_namespace) { create(:group, name: 'foo') }
|
||||
|
||||
before do
|
||||
parent_namespace.add_owner(user)
|
||||
|
|
|
@ -196,10 +196,11 @@ describe Import::GitlabController do
|
|||
end
|
||||
|
||||
context 'user has chosen an existing nested namespace for the project', :postgresql do
|
||||
let(:parent_namespace) { create(:group, name: 'foo', owner: user) }
|
||||
let(:parent_namespace) { create(:group, name: 'foo') }
|
||||
let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) }
|
||||
|
||||
before do
|
||||
parent_namespace.add_owner(user)
|
||||
nested_namespace.add_owner(user)
|
||||
end
|
||||
|
||||
|
@ -245,7 +246,7 @@ describe Import::GitlabController do
|
|||
|
||||
context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do
|
||||
let(:test_name) { 'test_name' }
|
||||
let!(:parent_namespace) { create(:group, name: 'foo', owner: user) }
|
||||
let!(:parent_namespace) { create(:group, name: 'foo') }
|
||||
|
||||
before do
|
||||
parent_namespace.add_owner(user)
|
||||
|
|
|
@ -4,7 +4,11 @@ describe Projects::ForksController do
|
|||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :public, :repository) }
|
||||
let(:forked_project) { Projects::ForkService.new(project, user).execute }
|
||||
let(:group) { create(:group, owner: forked_project.creator) }
|
||||
let(:group) { create(:group) }
|
||||
|
||||
before do
|
||||
group.add_owner(user)
|
||||
end
|
||||
|
||||
describe 'GET index' do
|
||||
def get_forks
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue