Merge branch 'master' into live-trace-v2
This commit is contained in:
commit
fd8a63f71e
222 changed files with 1540 additions and 1156 deletions
|
@ -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.
|
||||
|
|
28
CHANGELOG.md
28
CHANGELOG.md
|
@ -2,6 +2,34 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 10.7.1 (2018-04-23)
|
||||
|
||||
### Fixed (11 changes)
|
||||
|
||||
- [API] Fix URLs in the `Link` header for `GET /projects/:id/repository/contributors` when no value is passed for `order_by` or `sort`. !18393
|
||||
- Fix a case with secret variables being empty sometimes. !18400
|
||||
- Fix `Trace::HttpIO` can not render multi-byte chars. !18417
|
||||
- Fix specifying a non-default ref when requesting an archive using the legacy URL. !18468
|
||||
- Respect visibility options and description when importing project from template. !18473
|
||||
- Removes 'No Job log' message from build trace. !18523
|
||||
- Align action icons in pipeline graph.
|
||||
- Fix direct_upload when records with null file_store are used.
|
||||
- Removed alert box in IDE when redirecting to new merge request.
|
||||
- Fixed IDE not loading for sub groups.
|
||||
- Fixed IDE not showing loading state when tree is loading.
|
||||
|
||||
### Performance (4 changes)
|
||||
|
||||
- Validate project path prior to hitting the database. !18322
|
||||
- Add index to file_store on ci_job_artifacts. !18444
|
||||
- Fix N+1 queries when loading participants for a commit note.
|
||||
- Support Markdown rendering using multiple projects.
|
||||
|
||||
### Added (1 change)
|
||||
|
||||
- Add an API endpoint to download git repository snapshots. !18173
|
||||
|
||||
|
||||
## 10.7.0 (2018-04-22)
|
||||
|
||||
### Security (6 changes, 2 of them are from the community)
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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)
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
10.7.0-pre
|
||||
10.8.0-pre
|
||||
|
|
|
@ -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,77 +1,83 @@
|
|||
<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`:
|
||||
*
|
||||
* {
|
||||
* "id": 4256,
|
||||
* "name": "test",
|
||||
* "status": {
|
||||
* "icon": "icon_status_success",
|
||||
* "text": "passed",
|
||||
* "label": "passed",
|
||||
* "group": "success",
|
||||
* "details_path": "/root/ci-mock/builds/4256",
|
||||
* "action": {
|
||||
* "icon": "retry",
|
||||
* "title": "Retry",
|
||||
* "path": "/root/ci-mock/builds/4256/retry",
|
||||
* "method": "post"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export default {
|
||||
directives: {
|
||||
tooltip,
|
||||
/**
|
||||
* Renders the dropdown for the pipeline graph.
|
||||
*
|
||||
* The following object should be provided as `job`:
|
||||
*
|
||||
* {
|
||||
* "id": 4256,
|
||||
* "name": "test",
|
||||
* "status": {
|
||||
* "icon": "icon_status_success",
|
||||
* "text": "passed",
|
||||
* "label": "passed",
|
||||
* "group": "success",
|
||||
* "details_path": "/root/ci-mock/builds/4256",
|
||||
* "action": {
|
||||
* "icon": "retry",
|
||||
* "title": "Retry",
|
||||
* "path": "/root/ci-mock/builds/4256/retry",
|
||||
* "method": "post"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export default {
|
||||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
|
||||
components: {
|
||||
JobComponent,
|
||||
JobNameComponent,
|
||||
},
|
||||
|
||||
props: {
|
||||
job: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
|
||||
components: {
|
||||
jobComponent,
|
||||
jobNameComponent,
|
||||
requestFinishedFor: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
props: {
|
||||
job: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
computed: {
|
||||
tooltipText() {
|
||||
return `${this.job.name} - ${this.job.status.label}`;
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
tooltipText() {
|
||||
return `${this.job.name} - ${this.job.status.label}`;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.stopDropdownClickPropagation();
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.stopDropdownClickPropagation();
|
||||
},
|
||||
|
||||
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.
|
||||
methods: {
|
||||
/**
|
||||
* 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) => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
},
|
||||
stopDropdownClickPropagation() {
|
||||
$(
|
||||
'.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,52 +1,52 @@
|
|||
<script>
|
||||
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.
|
||||
*
|
||||
* Receives status object containing:
|
||||
* status: {
|
||||
* details_path: "/gitlab-org/gitlab-ce/pipelines/8150156" // url
|
||||
* group:"running" // used for CSS class
|
||||
* icon: "icon_status_running" // used to render the icon
|
||||
* label:"running" // used for potential tooltip
|
||||
* text:"running" // text rendered
|
||||
* }
|
||||
*
|
||||
* Used in:
|
||||
* - Pipelines table - first column
|
||||
* - Jobs table - first column
|
||||
* - Pipeline show view - header
|
||||
* - Job show view - header
|
||||
* - MR widget
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* Receives status object containing:
|
||||
* status: {
|
||||
* details_path: "/gitlab-org/gitlab-ce/pipelines/8150156" // url
|
||||
* group:"running" // used for CSS class
|
||||
* icon: "icon_status_running" // used to render the icon
|
||||
* label:"running" // used for potential tooltip
|
||||
* text:"running" // text rendered
|
||||
* }
|
||||
*
|
||||
* Used in:
|
||||
* - Pipelines table - first column
|
||||
* - Jobs table - first column
|
||||
* - Pipeline show view - header
|
||||
* - Job show view - header
|
||||
* - MR widget
|
||||
*/
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ciIcon,
|
||||
export default {
|
||||
components: {
|
||||
CiIcon,
|
||||
},
|
||||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
props: {
|
||||
status: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
directives: {
|
||||
tooltip,
|
||||
showText: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
props: {
|
||||
status: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
showText: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
cssClass() {
|
||||
const className = this.status.group;
|
||||
return className ? `ci-status ci-${className}` : 'ci-status';
|
||||
},
|
||||
computed: {
|
||||
cssClass() {
|
||||
const className = this.status.group;
|
||||
return className ? `ci-status ci-${className}` : 'ci-status';
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<a
|
||||
|
|
|
@ -1,45 +1,44 @@
|
|||
<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:
|
||||
* status: {
|
||||
* details_path: "/gitlab-org/gitlab-ce/pipelines/8150156" // url
|
||||
* group:"running" // used for CSS class
|
||||
* icon: "icon_status_running" // used to render the icon
|
||||
* label:"running" // used for potential tooltip
|
||||
* text:"running" // text rendered
|
||||
* }
|
||||
*
|
||||
* Used in:
|
||||
* - Pipelines table Badge
|
||||
* - Pipelines table mini graph
|
||||
* - Pipeline graph
|
||||
* - Pipeline show view badge
|
||||
* - Jobs table
|
||||
* - Jobs show view header
|
||||
* - Jobs show view sidebar
|
||||
*/
|
||||
export default {
|
||||
components: {
|
||||
icon,
|
||||
/**
|
||||
* Renders CI icon based on API response shared between all places where it is used.
|
||||
*
|
||||
* Receives status object containing:
|
||||
* status: {
|
||||
* details_path: "/gitlab-org/gitlab-ce/pipelines/8150156" // url
|
||||
* group:"running" // used for CSS class
|
||||
* icon: "icon_status_running" // used to render the icon
|
||||
* label:"running" // used for potential tooltip
|
||||
* text:"running" // text rendered
|
||||
* }
|
||||
*
|
||||
* Used in:
|
||||
* - Pipelines table Badge
|
||||
* - Pipelines table mini graph
|
||||
* - Pipeline graph
|
||||
* - Pipeline show view badge
|
||||
* - Jobs table
|
||||
* - Jobs show view header
|
||||
* - Jobs show view sidebar
|
||||
*/
|
||||
export default {
|
||||
components: {
|
||||
Icon,
|
||||
},
|
||||
props: {
|
||||
status: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
props: {
|
||||
status: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
cssClass() {
|
||||
const status = this.status.group;
|
||||
return `ci-status-icon ci-status-icon-${status} js-ci-status-icon-${status}`;
|
||||
},
|
||||
|
||||
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,40 +1,50 @@
|
|||
<script>
|
||||
/**
|
||||
* Falls back to the code used in `copy_to_clipboard.js`
|
||||
*/
|
||||
import tooltip from '../directives/tooltip';
|
||||
/**
|
||||
* 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';
|
||||
|
||||
export default {
|
||||
name: 'ClipboardButton',
|
||||
directives: {
|
||||
tooltip,
|
||||
export default {
|
||||
name: 'ClipboardButton',
|
||||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
props: {
|
||||
text: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
props: {
|
||||
text: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
tooltipPlacement: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'top',
|
||||
},
|
||||
tooltipContainer: {
|
||||
type: [String, Boolean],
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
cssClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'btn-default',
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
tooltipPlacement: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'top',
|
||||
},
|
||||
tooltipContainer: {
|
||||
type: [String, Boolean],
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
cssClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'btn-default',
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,119 +1,111 @@
|
|||
<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 {
|
||||
directives: {
|
||||
tooltip,
|
||||
export default {
|
||||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
components: {
|
||||
UserAvatarLink,
|
||||
Icon,
|
||||
},
|
||||
props: {
|
||||
/**
|
||||
* Indicates the existance of a tag.
|
||||
* Used to render the correct icon, if true will render `fa-tag` icon,
|
||||
* if false will render a svg sprite fork icon
|
||||
*/
|
||||
tag: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
components: {
|
||||
userAvatarLink,
|
||||
icon,
|
||||
/**
|
||||
* If provided is used to render the branch name and url.
|
||||
* Should contain the following properties:
|
||||
* name
|
||||
* ref_url
|
||||
*/
|
||||
commitRef: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
/**
|
||||
* Used to link to the commit sha.
|
||||
*/
|
||||
commitUrl: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
props: {
|
||||
/**
|
||||
* Indicates the existance of a tag.
|
||||
* Used to render the correct icon, if true will render `fa-tag` icon,
|
||||
* if false will render a svg sprite fork icon
|
||||
*/
|
||||
tag: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* If provided is used to render the branch name and url.
|
||||
* Should contain the following properties:
|
||||
* name
|
||||
* ref_url
|
||||
*/
|
||||
commitRef: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
/**
|
||||
* Used to link to the commit sha.
|
||||
*/
|
||||
commitUrl: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to show the commit short sha that links to the commit url.
|
||||
*/
|
||||
shortSha: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
/**
|
||||
* If provided shows the commit tile.
|
||||
*/
|
||||
title: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
/**
|
||||
* If provided renders information about the author of the commit.
|
||||
* When provided should include:
|
||||
* `avatar_url` to render the avatar icon
|
||||
* `web_url` to link to user profile
|
||||
* `username` to render alt and title tags
|
||||
*/
|
||||
author: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
showBranch: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
/**
|
||||
* Used to show the commit short sha that links to the commit url.
|
||||
*/
|
||||
shortSha: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* Used to verify if all the properties needed to render the commit
|
||||
* ref section were provided.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
hasCommitRef() {
|
||||
return this.commitRef && this.commitRef.name && this.commitRef.ref_url;
|
||||
},
|
||||
/**
|
||||
* Used to verify if all the properties needed to render the commit
|
||||
* author section were provided.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
hasAuthor() {
|
||||
return this.author &&
|
||||
this.author.avatar_url &&
|
||||
this.author.path &&
|
||||
this.author.username;
|
||||
},
|
||||
/**
|
||||
* If information about the author is provided will return a string
|
||||
* to be rendered as the alt attribute of the img tag.
|
||||
*
|
||||
* @returns {String}
|
||||
*/
|
||||
userImageAltDescription() {
|
||||
return this.author &&
|
||||
this.author.username ? `${this.author.username}'s avatar` : null;
|
||||
},
|
||||
/**
|
||||
* If provided shows the commit tile.
|
||||
*/
|
||||
title: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
created() {
|
||||
this.commitIconSvg = commitIconSvg;
|
||||
/**
|
||||
* If provided renders information about the author of the commit.
|
||||
* When provided should include:
|
||||
* `avatar_url` to render the avatar icon
|
||||
* `web_url` to link to user profile
|
||||
* `username` to render alt and title tags
|
||||
*/
|
||||
author: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
};
|
||||
showBranch: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* Used to verify if all the properties needed to render the commit
|
||||
* ref section were provided.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
hasCommitRef() {
|
||||
return this.commitRef && this.commitRef.name && this.commitRef.ref_url;
|
||||
},
|
||||
/**
|
||||
* Used to verify if all the properties needed to render the commit
|
||||
* author section were provided.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
hasAuthor() {
|
||||
return this.author && this.author.avatar_url && this.author.path && this.author.username;
|
||||
},
|
||||
/**
|
||||
* If information about the author is provided will return a string
|
||||
* to be rendered as the alt attribute of the img tag.
|
||||
*
|
||||
* @returns {String}
|
||||
*/
|
||||
userImageAltDescription() {
|
||||
return this.author && this.author.username ? `${this.author.username}'s avatar` : null;
|
||||
},
|
||||
},
|
||||
};
|
||||
</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,33 +1,33 @@
|
|||
<script>
|
||||
import { __ } from '~/locale';
|
||||
/**
|
||||
* Port of detail_behavior expand button.
|
||||
*
|
||||
* @example
|
||||
* <expand-button>
|
||||
* <template slot="expanded">
|
||||
* Text goes here.
|
||||
* </template>
|
||||
* </expand-button>
|
||||
*/
|
||||
export default {
|
||||
name: 'ExpandButton',
|
||||
data() {
|
||||
return {
|
||||
isCollapsed: true,
|
||||
};
|
||||
import { __ } from '~/locale';
|
||||
/**
|
||||
* Port of detail_behavior expand button.
|
||||
*
|
||||
* @example
|
||||
* <expand-button>
|
||||
* <template slot="expanded">
|
||||
* Text goes here.
|
||||
* </template>
|
||||
* </expand-button>
|
||||
*/
|
||||
export default {
|
||||
name: 'ExpandButton',
|
||||
data() {
|
||||
return {
|
||||
isCollapsed: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
ariaLabel() {
|
||||
return __('Click to expand text');
|
||||
},
|
||||
computed: {
|
||||
ariaLabel() {
|
||||
return __('Click to expand text');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onClick() {
|
||||
this.isCollapsed = !this.isCollapsed;
|
||||
},
|
||||
methods: {
|
||||
onClick() {
|
||||
this.isCollapsed = !this.isCollapsed;
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<span>
|
||||
|
|
|
@ -1,78 +1,78 @@
|
|||
<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 {
|
||||
components: {
|
||||
ciIconBadge,
|
||||
loadingIcon,
|
||||
timeagoTooltip,
|
||||
userAvatarImage,
|
||||
/**
|
||||
* Renders header component for job and pipeline page based on UI mockups
|
||||
*
|
||||
* Used in:
|
||||
* - job show page
|
||||
* - pipeline show page
|
||||
*/
|
||||
export default {
|
||||
components: {
|
||||
CiIconBadge,
|
||||
LoadingIcon,
|
||||
TimeagoTooltip,
|
||||
UserAvatarImage,
|
||||
},
|
||||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
props: {
|
||||
status: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
directives: {
|
||||
tooltip,
|
||||
itemName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
props: {
|
||||
status: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
itemName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
itemId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
time: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
user: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
actions: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => [],
|
||||
},
|
||||
hasSidebarButton: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
shouldRenderTriggeredLabel: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
itemId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
time: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
user: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
actions: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => [],
|
||||
},
|
||||
hasSidebarButton: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
shouldRenderTriggeredLabel: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
userAvatarAltText() {
|
||||
return `${this.user.name}'s avatar`;
|
||||
},
|
||||
computed: {
|
||||
userAvatarAltText() {
|
||||
return `${this.user.name}'s avatar`;
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
onClickAction(action) {
|
||||
this.$emit('actionClicked', action);
|
||||
},
|
||||
methods: {
|
||||
onClickAction(action) {
|
||||
this.$emit('actionClicked', action);
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,76 +1,75 @@
|
|||
<script>
|
||||
/* This is a re-usable vue component for rendering a svg sprite
|
||||
icon
|
||||
|
||||
/* This is a re-usable vue component for rendering a svg sprite
|
||||
icon
|
||||
Sample configuration:
|
||||
|
||||
Sample configuration:
|
||||
<icon
|
||||
name="retry"
|
||||
:size="32"
|
||||
css-classes="top"
|
||||
/>
|
||||
|
||||
<icon
|
||||
name="retry"
|
||||
:size="32"
|
||||
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 {
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
export default {
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
size: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 16,
|
||||
validator(value) {
|
||||
return validSizes.includes(value);
|
||||
},
|
||||
},
|
||||
|
||||
cssClasses: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
|
||||
width: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
|
||||
height: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
|
||||
y: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
|
||||
x: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
size: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 16,
|
||||
validator(value) {
|
||||
return validSizes.includes(value);
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
spriteHref() {
|
||||
return `${gon.sprite_icons}#${this.name}`;
|
||||
},
|
||||
iconSizeClass() {
|
||||
return this.size ? `s${this.size}` : '';
|
||||
},
|
||||
cssClasses: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
};
|
||||
|
||||
width: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
|
||||
height: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
|
||||
y: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
|
||||
x: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
spriteHref() {
|
||||
return `${gon.sprite_icons}#${this.name}`;
|
||||
},
|
||||
iconSizeClass() {
|
||||
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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Add an API endpoint to download git repository snapshots
|
||||
merge_request: 18173
|
||||
author:
|
||||
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
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Fix `Trace::HttpIO` can not render multi-byte chars
|
||||
merge_request: 18417
|
||||
author:
|
||||
type: fixed
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Align action icons in pipeline graph
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
title: '[API] Fix URLs in the `Link` header for `GET /projects/:id/repository/contributors`
|
||||
when no value is passed for `order_by` or `sort`'
|
||||
merge_request: 18393
|
||||
author:
|
||||
type: fixed
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Add index to file_store on ci_job_artifacts
|
||||
merge_request: 18444
|
||||
author:
|
||||
type: performance
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
title: Fix specifying a non-default ref when requesting an archive using the legacy
|
||||
URL
|
||||
merge_request: 18468
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix project creation for user endpoint when jobs_enabled parameter supplied
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
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
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Validate project path prior to hitting the database.
|
||||
merge_request: 18322
|
||||
author:
|
||||
type: performance
|
|
@ -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
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Fix direct_upload when records with null file_store are used
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Fix a case with secret variables being empty sometimes
|
||||
merge_request: 18400
|
||||
author:
|
||||
type: fixed
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Respect visibility options and description when importing project from template
|
||||
merge_request: 18473
|
||||
author:
|
||||
type: fixed
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Removed alert box in IDE when redirecting to new merge request
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Fixed IDE not loading for sub groups
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Fixed IDE not showing loading state when tree is loading
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix missing namespace for some internal users
|
||||
merge_request: 18357
|
||||
author:
|
||||
type: fixed
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Support Markdown rendering using multiple projects
|
||||
merge_request:
|
||||
author:
|
||||
type: performance
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: Fix N+1 queries when loading participants for a commit note
|
||||
merge_request:
|
||||
author:
|
||||
type: performance
|
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
|
|
@ -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
|
||||
|
|
|
@ -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**.
|
||||
|
|
|
@ -63,6 +63,8 @@ writing one](testing_levels.md#consider-not-writing-a-system-test)!
|
|||
|
||||
Sometimes you may need to debug Capybara tests by observing browser behavior.
|
||||
|
||||
#### Live debug
|
||||
|
||||
You can pause Capybara and view the website on the browser by using the
|
||||
`live_debug` method in your spec. The current page will be automatically opened
|
||||
in your default browser.
|
||||
|
@ -90,6 +92,54 @@ Finished in 34.51 seconds (files took 0.76702 seconds to load)
|
|||
|
||||
Note: `live_debug` only works on javascript enabled specs.
|
||||
|
||||
#### Run `:js` spec in a visible browser
|
||||
|
||||
Run the spec with `CHROME_HEADLESS=0`, e.g.:
|
||||
|
||||
```
|
||||
CHROME_HEADLESS=0 bundle exec rspec some_spec.rb
|
||||
```
|
||||
|
||||
The test will go by quickly, but this will give you an idea of what's happening.
|
||||
|
||||
You can also add `byebug` or `binding.pry` to pause execution and step through
|
||||
the test.
|
||||
|
||||
#### Screenshots
|
||||
|
||||
We use the `capybara-screenshot` gem to automatically take a screenshot on
|
||||
failure. In CI you can download these files as job artifacts.
|
||||
|
||||
Also, you can manually take screenshots at any point in a test by adding the
|
||||
methods below. Be sure to remove them when they are no longer needed! See
|
||||
https://github.com/mattheworiordan/capybara-screenshot#manual-screenshots for
|
||||
more.
|
||||
|
||||
Add `screenshot_and_save_page` in a `:js` spec to screenshot what Capybara
|
||||
"sees", and save the page source.
|
||||
|
||||
Add `screenshot_and_open_image` in a `:js` spec to screenshot what Capybara
|
||||
"sees", and automatically open the image.
|
||||
|
||||
### 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 +331,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 +348,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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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],
|
|
@ -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)
|
||||
|
|
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
|
|
@ -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
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
require_relative '../support/repo_helpers'
|
||||
require_relative '../support/helpers/repo_helpers'
|
||||
|
||||
FactoryBot.define do
|
||||
factory :commit do
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
require_relative '../support/gpg_helpers'
|
||||
|
||||
FactoryBot.define do
|
||||
factory :gpg_key_subkey do
|
||||
gpg_key
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
require_relative '../support/gpg_helpers'
|
||||
require_relative '../support/helpers/gpg_helpers'
|
||||
|
||||
FactoryBot.define do
|
||||
factory :gpg_key do
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
require_relative '../support/gpg_helpers'
|
||||
|
||||
FactoryBot.define do
|
||||
factory :gpg_signature do
|
||||
commit_sha { Digest::SHA1.hexdigest(SecureRandom.hex) }
|
||||
|
|
|
@ -5,6 +5,14 @@ FactoryBot.define do
|
|||
type 'Group'
|
||||
owner nil
|
||||
|
||||
after(:create) do |group|
|
||||
if group.owner
|
||||
# We could remove this after we have proper constraint:
|
||||
# https://gitlab.com/gitlab-org/gitlab-ce/issues/43292
|
||||
raise "Don't set owner for groups, use `group.add_owner(user)` instead"
|
||||
end
|
||||
end
|
||||
|
||||
trait :public do
|
||||
visibility_level Gitlab::VisibilityLevel::PUBLIC
|
||||
end
|
||||
|
|
|
@ -2,6 +2,22 @@ FactoryBot.define do
|
|||
factory :namespace do
|
||||
sequence(:name) { |n| "namespace#{n}" }
|
||||
path { name.downcase.gsub(/\s/, '_') }
|
||||
owner
|
||||
|
||||
# This is a workaround to avoid the user creating another namespace via
|
||||
# User#ensure_namespace_correct. We should try to remove it and then
|
||||
# we could remove this workaround
|
||||
association :owner, factory: :user, strategy: :build
|
||||
before(:create) do |namespace|
|
||||
owner = namespace.owner
|
||||
|
||||
if owner
|
||||
# We're changing the username here because we want to keep our path,
|
||||
# and User#ensure_namespace_correct would change the path based on
|
||||
# username, so we're forced to do this otherwise we'll need to change
|
||||
# a lot of existing tests.
|
||||
owner.username = namespace.path
|
||||
owner.namespace = namespace
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
require_relative '../support/repo_helpers'
|
||||
require_relative '../support/helpers/repo_helpers'
|
||||
|
||||
include ActionDispatch::TestProcess
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
require_relative '../support/test_env'
|
||||
require_relative '../support/helpers/test_env'
|
||||
|
||||
FactoryBot.define do
|
||||
# Project without repository
|
||||
|
|
15
spec/fast_spec_helper.rb
Normal file
15
spec/fast_spec_helper.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
require 'bundler/setup'
|
||||
|
||||
ENV['GITLAB_ENV'] = 'test'
|
||||
ENV['IN_MEMORY_APPLICATION_SETTINGS'] = 'true'
|
||||
|
||||
unless Object.respond_to?(:require_dependency)
|
||||
class Object
|
||||
alias_method :require_dependency, :require
|
||||
end
|
||||
end
|
||||
|
||||
# Defines Gitlab and Gitlab.config which are at the center of the app
|
||||
require_relative '../lib/gitlab' unless defined?(Gitlab.config)
|
||||
|
||||
require_relative 'support/rspec'
|
|
@ -26,7 +26,7 @@ describe 'User browses jobs' do
|
|||
page.within('.nav-controls') do
|
||||
ci_lint_tool_link = page.find_link('CI lint')
|
||||
|
||||
expect(ci_lint_tool_link[:href]).to end_with(ci_lint_path)
|
||||
expect(ci_lint_tool_link[:href]).to end_with(project_ci_lint_path(project))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ feature 'New project' do
|
|||
end
|
||||
|
||||
context 'with group namespace' do
|
||||
let(:group) { create(:group, :private, owner: user) }
|
||||
let(:group) { create(:group, :private) }
|
||||
|
||||
before do
|
||||
group.add_owner(user)
|
||||
|
@ -81,7 +81,7 @@ feature 'New project' do
|
|||
end
|
||||
|
||||
context 'with subgroup namespace' do
|
||||
let(:group) { create(:group, owner: user) }
|
||||
let(:group) { create(:group) }
|
||||
let(:subgroup) { create(:group, parent: group) }
|
||||
|
||||
before do
|
||||
|
|
|
@ -8,6 +8,7 @@ describe "Projects > Settings > Pipelines settings" do
|
|||
before do
|
||||
sign_in(user)
|
||||
project.add_role(user, role)
|
||||
create(:project_auto_devops, project: project)
|
||||
end
|
||||
|
||||
context 'for developer' do
|
||||
|
@ -27,10 +28,17 @@ describe "Projects > Settings > Pipelines settings" do
|
|||
visit project_settings_ci_cd_path(project)
|
||||
|
||||
fill_in('Test coverage parsing', with: 'coverage_regex')
|
||||
click_on 'Save changes'
|
||||
|
||||
page.within '#js-general-pipeline-settings' do
|
||||
click_on 'Save changes'
|
||||
end
|
||||
|
||||
expect(page.status_code).to eq(200)
|
||||
expect(page).to have_button('Save changes', disabled: false)
|
||||
|
||||
page.within '#js-general-pipeline-settings' do
|
||||
expect(page).to have_button('Save changes', disabled: false)
|
||||
end
|
||||
|
||||
expect(page).to have_field('Test coverage parsing', with: 'coverage_regex')
|
||||
end
|
||||
|
||||
|
@ -38,10 +46,15 @@ describe "Projects > Settings > Pipelines settings" do
|
|||
visit project_settings_ci_cd_path(project)
|
||||
|
||||
page.check('Auto-cancel redundant, pending pipelines')
|
||||
click_on 'Save changes'
|
||||
page.within '#js-general-pipeline-settings' do
|
||||
click_on 'Save changes'
|
||||
end
|
||||
|
||||
expect(page.status_code).to eq(200)
|
||||
expect(page).to have_button('Save changes', disabled: false)
|
||||
|
||||
page.within '#js-general-pipeline-settings' do
|
||||
expect(page).to have_button('Save changes', disabled: false)
|
||||
end
|
||||
|
||||
checkbox = find_field('project_auto_cancel_pending_pipelines')
|
||||
expect(checkbox).to be_checked
|
||||
|
@ -51,13 +64,16 @@ describe "Projects > Settings > Pipelines settings" do
|
|||
it 'update auto devops settings' do
|
||||
visit project_settings_ci_cd_path(project)
|
||||
|
||||
fill_in('project_auto_devops_attributes_domain', with: 'test.com')
|
||||
page.choose('project_auto_devops_attributes_enabled_false')
|
||||
click_on 'Save changes'
|
||||
page.within '#autodevops-settings' do
|
||||
fill_in('project_auto_devops_attributes_domain', with: 'test.com')
|
||||
page.choose('project_auto_devops_attributes_enabled_false')
|
||||
click_on 'Save changes'
|
||||
end
|
||||
|
||||
expect(page.status_code).to eq(200)
|
||||
expect(project.auto_devops).to be_present
|
||||
expect(project.auto_devops).not_to be_enabled
|
||||
expect(project.auto_devops.domain).to eq('test.com')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -65,7 +65,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
|
|||
describe 'Auto DevOps button' do
|
||||
it '"Enable Auto DevOps" button linked to settings page' do
|
||||
page.within('.project-stats') do
|
||||
expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
|
||||
expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -75,7 +75,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
|
|||
visit project_path(project)
|
||||
|
||||
page.within('.project-stats') do
|
||||
expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
|
||||
expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -212,7 +212,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
|
|||
describe 'Auto DevOps button' do
|
||||
it '"Enable Auto DevOps" button linked to settings page' do
|
||||
page.within('.project-stats') do
|
||||
expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
|
||||
expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -222,7 +222,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
|
|||
visit project_path(project)
|
||||
|
||||
page.within('.project-stats') do
|
||||
expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
|
||||
expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import mountComponent from '../../helpers/vue_mount_component_helper';
|
|||
describe('pipeline graph action component', () => {
|
||||
let component;
|
||||
|
||||
beforeEach((done) => {
|
||||
beforeEach(done => {
|
||||
const ActionComponent = Vue.extend(actionComponent);
|
||||
component = mountComponent(ActionComponent, {
|
||||
tooltipText: 'bar',
|
||||
|
@ -22,7 +22,7 @@ describe('pipeline graph action component', () => {
|
|||
});
|
||||
|
||||
it('should emit an event with the provided link', () => {
|
||||
eventHub.$on('graphAction', (link) => {
|
||||
eventHub.$on('graphAction', link => {
|
||||
expect(link).toEqual('foo');
|
||||
});
|
||||
});
|
||||
|
@ -31,7 +31,7 @@ describe('pipeline graph action component', () => {
|
|||
expect(component.$el.getAttribute('data-original-title')).toEqual('bar');
|
||||
});
|
||||
|
||||
it('should update bootstrap tooltip when title changes', (done) => {
|
||||
it('should update bootstrap tooltip when title changes', done => {
|
||||
component.tooltipText = 'changed';
|
||||
|
||||
setTimeout(() => {
|
||||
|
@ -44,4 +44,45 @@ describe('pipeline graph action component', () => {
|
|||
expect(component.$el.querySelector('.ci-action-icon-wrapper')).toBeDefined();
|
||||
expect(component.$el.querySelector('svg')).toBeDefined();
|
||||
});
|
||||
|
||||
it('disables the button when clicked', done => {
|
||||
component.$el.click();
|
||||
|
||||
component.$nextTick(() => {
|
||||
expect(component.$el.getAttribute('disabled')).toEqual('disabled');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('re-enabled the button when `requestFinishedFor` matches `linkRequested`', done => {
|
||||
component.$el.click();
|
||||
|
||||
component
|
||||
.$nextTick()
|
||||
.then(() => {
|
||||
expect(component.$el.getAttribute('disabled')).toEqual('disabled');
|
||||
component.requestFinishedFor = 'foo';
|
||||
})
|
||||
.then(() => {
|
||||
expect(component.$el.getAttribute('disabled')).toBeNull();
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('does not re-enable the button when `requestFinishedFor` does not matches `linkRequested`', done => {
|
||||
component.$el.click();
|
||||
|
||||
component
|
||||
.$nextTick()
|
||||
.then(() => {
|
||||
expect(component.$el.getAttribute('disabled')).toEqual('disabled');
|
||||
component.requestFinishedFor = 'bar';
|
||||
})
|
||||
.then(() => {
|
||||
expect(component.$el.getAttribute('disabled')).toEqual('disabled');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
});
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue