Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into fix/persistent-import-data
This commit is contained in:
commit
0b2469a4e5
|
@ -1,4 +0,0 @@
|
||||||
# Prefer single quotes
|
|
||||||
StringLiterals:
|
|
||||||
EnforcedStyle: single_quotes
|
|
||||||
Enabled: true
|
|
|
@ -1,8 +0,0 @@
|
||||||
stage:
|
|
||||||
before:
|
|
||||||
- cp config/gitlab.teatro.yml config/gitlab.yml
|
|
||||||
- mkdir /apps/gitlab-satellites
|
|
||||||
- mkdir /apps/repositories
|
|
||||||
|
|
||||||
database:
|
|
||||||
- RAILS_ENV=development force=yes bundle exec rake db:create gitlab:setup
|
|
24
CHANGELOG
24
CHANGELOG
|
@ -1,11 +1,14 @@
|
||||||
Please view this file on the master branch, on stable branches it's out of date.
|
Please view this file on the master branch, on stable branches it's out of date.
|
||||||
|
|
||||||
v 8.10.0 (unreleased)
|
v 8.10.0 (unreleased)
|
||||||
|
- Expose {should,force}_remove_source_branch (Ben Boeckel)
|
||||||
- Fix commit builds API, return all builds for all pipelines for given commit. !4849
|
- Fix commit builds API, return all builds for all pipelines for given commit. !4849
|
||||||
- Replace Haml with Hamlit to make view rendering faster. !3666
|
- Replace Haml with Hamlit to make view rendering faster. !3666
|
||||||
|
- Expire the branch cache after `git gc` runs
|
||||||
- Refactor repository paths handling to allow multiple git mount points
|
- Refactor repository paths handling to allow multiple git mount points
|
||||||
- Optimize system note visibility checking by memoizing the visible reference count !5070
|
- Optimize system note visibility checking by memoizing the visible reference count !5070
|
||||||
- Add Application Setting to configure default Repository Path for new projects
|
- Add Application Setting to configure default Repository Path for new projects
|
||||||
|
- Delete award emoji when deleting a user
|
||||||
- Remove pinTo from Flash and make inline flash messages look nicer !4854 (winniehell)
|
- Remove pinTo from Flash and make inline flash messages look nicer !4854 (winniehell)
|
||||||
- Wrap code blocks on Activies and Todos page. !4783 (winniehell)
|
- Wrap code blocks on Activies and Todos page. !4783 (winniehell)
|
||||||
- Align flash messages with left side of page content !4959 (winniehell)
|
- Align flash messages with left side of page content !4959 (winniehell)
|
||||||
|
@ -30,7 +33,9 @@ v 8.10.0 (unreleased)
|
||||||
- Exclude email check from the standard health check
|
- Exclude email check from the standard health check
|
||||||
- Updated layout for Projects, Groups, Users on Admin area !4424
|
- Updated layout for Projects, Groups, Users on Admin area !4424
|
||||||
- Fix changing issue state columns in milestone view
|
- Fix changing issue state columns in milestone view
|
||||||
|
- Update health_check gem to version 2.1.0
|
||||||
- Add notification settings dropdown for groups
|
- Add notification settings dropdown for groups
|
||||||
|
- Render inline diffs for multiple changed lines following eachother
|
||||||
- Wildcards for protected branches. !4665
|
- Wildcards for protected branches. !4665
|
||||||
- Allow importing from Github using Personal Access Tokens. (Eric K Idema)
|
- Allow importing from Github using Personal Access Tokens. (Eric K Idema)
|
||||||
- API: Todos !3188 (Robert Schilling)
|
- API: Todos !3188 (Robert Schilling)
|
||||||
|
@ -38,6 +43,7 @@ v 8.10.0 (unreleased)
|
||||||
- Add "Enabled Git access protocols" to Application Settings
|
- Add "Enabled Git access protocols" to Application Settings
|
||||||
- Diffs will create button/diff form on demand no on server side
|
- Diffs will create button/diff form on demand no on server side
|
||||||
- Reduce size of HTML used by diff comment forms
|
- Reduce size of HTML used by diff comment forms
|
||||||
|
- Protected branches have a "Developers can Merge" setting. !4892 (original implementation by Mathias Vestergaard)
|
||||||
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
|
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
|
||||||
- Only show New Snippet button to users that can create snippets.
|
- Only show New Snippet button to users that can create snippets.
|
||||||
- PipelinesFinder uses git cache data
|
- PipelinesFinder uses git cache data
|
||||||
|
@ -46,14 +52,17 @@ v 8.10.0 (unreleased)
|
||||||
- Collapse large diffs by default (!4990)
|
- Collapse large diffs by default (!4990)
|
||||||
- Check for conflicts with existing Project's wiki path when creating a new project.
|
- Check for conflicts with existing Project's wiki path when creating a new project.
|
||||||
- Show last push widget in upstream after push to fork
|
- Show last push widget in upstream after push to fork
|
||||||
|
- Cache todos pending/done dashboard query counts.
|
||||||
- Don't instantiate a git tree on Projects show default view
|
- Don't instantiate a git tree on Projects show default view
|
||||||
- Bump Rinku to 2.0.0
|
- Bump Rinku to 2.0.0
|
||||||
- Remove unused front-end variable -> default_issues_tracker
|
- Remove unused front-end variable -> default_issues_tracker
|
||||||
|
- ObjectRenderer retrieve renderer content using Rails.cache.read_multi
|
||||||
- Better caching of git calls on ProjectsController#show.
|
- Better caching of git calls on ProjectsController#show.
|
||||||
- Avoid to retrieve MR closes_issues as much as possible.
|
- Avoid to retrieve MR closes_issues as much as possible.
|
||||||
- Add API endpoint for a group issues !4520 (mahcsig)
|
- Add API endpoint for a group issues !4520 (mahcsig)
|
||||||
- Add Bugzilla integration !4930 (iamtjg)
|
- Add Bugzilla integration !4930 (iamtjg)
|
||||||
- Instrument Rinku usage
|
- Instrument Rinku usage
|
||||||
|
- Be explicit to define merge request discussion variables
|
||||||
- Metrics for Rouge::Plugins::Redcarpet and Rouge::Formatters::HTMLGitlab
|
- Metrics for Rouge::Plugins::Redcarpet and Rouge::Formatters::HTMLGitlab
|
||||||
- RailsCache metris now includes fetch_hit/fetch_miss and read_hit/read_miss info.
|
- RailsCache metris now includes fetch_hit/fetch_miss and read_hit/read_miss info.
|
||||||
- Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w)
|
- Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w)
|
||||||
|
@ -67,12 +76,25 @@ v 8.10.0 (unreleased)
|
||||||
- Allow '?', or '&' for label names
|
- Allow '?', or '&' for label names
|
||||||
- Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests
|
- Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests
|
||||||
- Add date when user joined the team on the member page
|
- Add date when user joined the team on the member page
|
||||||
- Fix 404 redirect after validation fails importing a GitLab project
|
- Fix 404 redirect after validation fails importing a GitLab project
|
||||||
- Fix 404 redirect after validation fails importing a GitLab project
|
- Fix 404 redirect after validation fails importing a GitLab project
|
||||||
- Added setting to set new users by default as external !4545 (Dravere)
|
- Added setting to set new users by default as external !4545 (Dravere)
|
||||||
- Add min value for project limit field on user's form !3622 (jastkand)
|
- Add min value for project limit field on user's form !3622 (jastkand)
|
||||||
|
- Reset project pushes_since_gc when we enqueue the git gc call
|
||||||
- Add reminder to not paste private SSH keys !4399 (Ingo Blechschmidt)
|
- Add reminder to not paste private SSH keys !4399 (Ingo Blechschmidt)
|
||||||
- Remove duplicate `description` field in `MergeRequest` entities (Ben Boeckel)
|
- Remove duplicate `description` field in `MergeRequest` entities (Ben Boeckel)
|
||||||
|
- Style of import project buttons were fixed in the new project page. !5183 (rdemirbay)
|
||||||
|
- Fix GitHub client requests when rate limit is disabled
|
||||||
|
- Optimistic locking for Issues and Merge Requests (Title and description overriding prevention)
|
||||||
|
- Redesign Builds and Pipelines pages
|
||||||
|
- Change status color and icon for running builds
|
||||||
|
|
||||||
|
v 8.9.6
|
||||||
|
- Fix importing of events under notes for GitLab projects. !5154
|
||||||
|
- Fix log statements in import/export. !5129
|
||||||
|
- Fix commit avatar alignment in compare view. !5128
|
||||||
|
- Fix broken migration in MySQL. !5005
|
||||||
|
- Overwrite Host and X-Forwarded-Host headers in NGINX !5213
|
||||||
|
|
||||||
v 8.9.7 (unreleased)
|
v 8.9.7 (unreleased)
|
||||||
- Fix import_data wrongly saved as a result of an invalid import_url
|
- Fix import_data wrongly saved as a result of an invalid import_url
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
0.7.7
|
0.7.8
|
||||||
|
|
2
Gemfile
2
Gemfile
|
@ -344,7 +344,7 @@ gem 'oauth2', '~> 1.2.0'
|
||||||
gem 'paranoia', '~> 2.0'
|
gem 'paranoia', '~> 2.0'
|
||||||
|
|
||||||
# Health check
|
# Health check
|
||||||
gem 'health_check', '~> 1.5.1'
|
gem 'health_check', '~> 2.1.0'
|
||||||
|
|
||||||
# System information
|
# System information
|
||||||
gem 'vmstat', '~> 2.1.0'
|
gem 'vmstat', '~> 2.1.0'
|
||||||
|
|
|
@ -322,8 +322,8 @@ GEM
|
||||||
thor
|
thor
|
||||||
tilt
|
tilt
|
||||||
hashie (3.4.3)
|
hashie (3.4.3)
|
||||||
health_check (1.5.1)
|
health_check (2.1.0)
|
||||||
rails (>= 2.3.0)
|
rails (>= 4.0)
|
||||||
hipchat (1.5.2)
|
hipchat (1.5.2)
|
||||||
httparty
|
httparty
|
||||||
mimemagic
|
mimemagic
|
||||||
|
@ -870,7 +870,7 @@ DEPENDENCIES
|
||||||
grape (~> 0.13.0)
|
grape (~> 0.13.0)
|
||||||
grape-entity (~> 0.4.2)
|
grape-entity (~> 0.4.2)
|
||||||
hamlit (~> 2.5)
|
hamlit (~> 2.5)
|
||||||
health_check (~> 1.5.1)
|
health_check (~> 2.1.0)
|
||||||
hipchat (~> 1.5.0)
|
hipchat (~> 1.5.0)
|
||||||
html-pipeline (~> 1.11.0)
|
html-pipeline (~> 1.11.0)
|
||||||
httparty (~> 0.13.3)
|
httparty (~> 0.13.3)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
class @Diff
|
class @Diff
|
||||||
UNFOLD_COUNT = 20
|
UNFOLD_COUNT = 20
|
||||||
constructor: ->
|
constructor: ->
|
||||||
|
$('.files .diff-file').singleFileDiff()
|
||||||
@filesCommentButton = $('.files .diff-file').filesCommentButton()
|
@filesCommentButton = $('.files .diff-file').filesCommentButton()
|
||||||
|
|
||||||
$(document).off('click', '.js-unfold')
|
$(document).off('click', '.js-unfold')
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
$ ->
|
$ ->
|
||||||
$(".protected-branches-list :checkbox").change (e) ->
|
$(".protected-branches-list :checkbox").change (e) ->
|
||||||
name = $(this).attr("name")
|
name = $(this).attr("name")
|
||||||
if name == "developers_can_push"
|
if name == "developers_can_push" || name == "developers_can_merge"
|
||||||
id = $(this).val()
|
id = $(this).val()
|
||||||
checked = $(this).is(":checked")
|
can_push = $(this).is(":checked")
|
||||||
url = $(this).data("url")
|
url = $(this).data("url")
|
||||||
$.ajax
|
$.ajax
|
||||||
type: "PUT"
|
type: "PATCH"
|
||||||
url: url
|
url: url
|
||||||
dataType: "json"
|
dataType: "json"
|
||||||
data:
|
data:
|
||||||
id: id
|
id: id
|
||||||
protected_branch:
|
protected_branch:
|
||||||
developers_can_push: checked
|
"#{name}": can_push
|
||||||
|
|
||||||
success: ->
|
success: ->
|
||||||
row = $(e.target)
|
row = $(e.target)
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.s16 { width: 16px; height: 16px; margin-right: 6px; }
|
&.s16 { width: 16px; height: 16px; margin-right: 6px; }
|
||||||
|
&.s20 { width: 20px; height: 20px; margin-right: 7px; }
|
||||||
&.s24 { width: 24px; height: 24px; margin-right: 8px; }
|
&.s24 { width: 24px; height: 24px; margin-right: 8px; }
|
||||||
&.s26 { width: 26px; height: 26px; margin-right: 8px; }
|
&.s26 { width: 26px; height: 26px; margin-right: 8px; }
|
||||||
&.s32 { width: 32px; height: 32px; margin-right: 10px; }
|
&.s32 { width: 32px; height: 32px; margin-right: 10px; }
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
.toggle-nav-collapse,
|
.toggle-nav-collapse,
|
||||||
.pin-nav-btn {
|
.pin-nav-btn {
|
||||||
color: $color-light;
|
color: $color-light;
|
||||||
background: $color;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $white-light;
|
color: $white-light;
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: width $sidebar-transition-duration;
|
transition: width $sidebar-transition-duration;
|
||||||
@include box-shadow(2px 0 16px 0 #bbb);
|
@include box-shadow(2px 0 16px 0 $black-transparent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
padding: 7px 15px 7px 12px;
|
padding: 7px 16px;
|
||||||
font-size: $gl-font-size;
|
font-size: $gl-font-size;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -108,7 +108,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-nav-collapse {
|
.sidebar-action-buttons {
|
||||||
width: $sidebar_width;
|
width: $sidebar_width;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -117,12 +117,37 @@
|
||||||
padding: 5px 0;
|
padding: 5px 0;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
|
|
||||||
|
.toggle-nav-collapse {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pin-nav-btn {
|
||||||
|
right: 0;
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
@media (min-width: $sidebar-breakpoint) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
transition: transform .15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
.fa {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-header-btn {
|
.nav-header-btn {
|
||||||
padding: 10px 5px;
|
padding: 10px 16px;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
transition-duration: .3s;
|
transition-duration: .3s;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
|
@ -131,30 +156,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pin-nav-btn {
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
height: 50px;
|
|
||||||
width: $sidebar_width;
|
|
||||||
line-height: 30px;
|
|
||||||
|
|
||||||
@media (min-width: $sidebar-breakpoint) {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fa {
|
|
||||||
transition: transform .15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-active {
|
|
||||||
.fa {
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-sidebar-collapsed {
|
.page-sidebar-collapsed {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ $focus-border-color: #3aabf0;
|
||||||
$table-border-color: #f0f0f0;
|
$table-border-color: #f0f0f0;
|
||||||
$background-color: #fafafa;
|
$background-color: #fafafa;
|
||||||
$dark-background-color: #f7f7f7;
|
$dark-background-color: #f7f7f7;
|
||||||
|
$table-text-gray: #8f8f8f;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Text
|
* Text
|
||||||
|
|
|
@ -83,14 +83,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table.builds {
|
|
||||||
.build-link {
|
|
||||||
a {
|
|
||||||
color: $gl-dark-link-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.build-trace {
|
.build-trace {
|
||||||
background: $ci-output-bg;
|
background: $ci-output-bg;
|
||||||
color: $ci-text-color;
|
color: $ci-text-color;
|
||||||
|
|
|
@ -162,9 +162,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.filtered-labels {
|
.filtered-labels {
|
||||||
|
font-size: 0;
|
||||||
|
padding: 12px 16px;
|
||||||
|
|
||||||
.label-row {
|
.label-row {
|
||||||
|
margin-top: 4px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
margin-right: 5px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,11 +73,14 @@
|
||||||
color: #888;
|
color: #888;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ci-pending,
|
&.ci-pending {
|
||||||
&.ci-running {
|
|
||||||
color: $gl-warning;
|
color: $gl-warning;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.ci-running {
|
||||||
|
color: $blue-normal;
|
||||||
|
}
|
||||||
|
|
||||||
&.ci-failed,
|
&.ci-failed,
|
||||||
&.ci-error {
|
&.ci-error {
|
||||||
color: $gl-danger;
|
color: $gl-danger;
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
.pipelines {
|
.pipelines {
|
||||||
.stage {
|
.stage {
|
||||||
max-width: 100px;
|
max-width: 80px;
|
||||||
|
width: 80px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.duration, .finished_at {
|
|
||||||
margin: 4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.commit-title {
|
.commit-title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
@ -22,3 +19,136 @@
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content-list {
|
||||||
|
|
||||||
|
&.pipelines,
|
||||||
|
&.builds-content-list {
|
||||||
|
width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table.builds {
|
||||||
|
min-width: 1100px;
|
||||||
|
|
||||||
|
tr {
|
||||||
|
th {
|
||||||
|
padding: 16px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody {
|
||||||
|
border-top-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-commit {
|
||||||
|
|
||||||
|
.branch-name {
|
||||||
|
margin-left: 8px;
|
||||||
|
font-weight: bold;
|
||||||
|
max-width: 180px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: top;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
margin: 0 6px;
|
||||||
|
height: 14px;
|
||||||
|
width: auto;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commit-id {
|
||||||
|
color: $gl-link-color;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commit-title {
|
||||||
|
margin-top: 4px;
|
||||||
|
max-width: 320px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-container {
|
||||||
|
|
||||||
|
.label {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.duration,
|
||||||
|
.finished-at {
|
||||||
|
color: $table-text-gray;
|
||||||
|
margin: 4px 0;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
height: 12px;
|
||||||
|
width: auto;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa,
|
||||||
|
svg {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pipeline-actions {
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
margin: 0;
|
||||||
|
color: $table-text-gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-toggle,
|
||||||
|
.dropdown-menu {
|
||||||
|
color: $table-text-gray;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
color: $table-text-gray;
|
||||||
|
margin-right: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-remove {
|
||||||
|
color: $white-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-group {
|
||||||
|
&.open {
|
||||||
|
.btn-default {
|
||||||
|
background-color: $white-normal;
|
||||||
|
border-color: $border-white-normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.build-link {
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $gl-dark-link-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-group.open .dropdown-toggle {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -340,23 +340,30 @@ a.deploy-project-label {
|
||||||
|
|
||||||
.project-import {
|
.project-import {
|
||||||
.form-group {
|
.form-group {
|
||||||
margin-bottom: 0;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.import-buttons {
|
.import-buttons {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
display: -webkit-flex;
|
display: -webkit-flex;
|
||||||
display: flex;
|
display: flex;
|
||||||
-webkit-flex-wrap: wrap;
|
-webkit-flex-wrap: wrap;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
margin-right: 10px;
|
margin: 0 10px 10px 0;
|
||||||
padding: 8px 12px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
&> div {
|
|
||||||
margin-bottom: 14px;
|
> div {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,11 +32,15 @@
|
||||||
border-color: $gl-gray;
|
border-color: $gl-gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ci-pending,
|
&.ci-pending {
|
||||||
&.ci-running {
|
|
||||||
color: $gl-warning;
|
color: $gl-warning;
|
||||||
border-color: $gl-warning;
|
border-color: $gl-warning;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.ci-running {
|
||||||
|
color: $blue-normal;
|
||||||
|
border-color: $blue-normal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ci-status-icon-success {
|
.ci-status-icon-success {
|
||||||
|
@ -45,10 +49,12 @@
|
||||||
.ci-status-icon-failed {
|
.ci-status-icon-failed {
|
||||||
color: $gl-danger;
|
color: $gl-danger;
|
||||||
}
|
}
|
||||||
.ci-status-icon-running,
|
|
||||||
.ci-status-icon-pending {
|
.ci-status-icon-pending {
|
||||||
color: $gl-warning;
|
color: $gl-warning;
|
||||||
}
|
}
|
||||||
|
.ci-status-icon-running {
|
||||||
|
color: $blue-normal;
|
||||||
|
}
|
||||||
.ci-status-icon-canceled,
|
.ci-status-icon-canceled,
|
||||||
.ci-status-icon-disabled,
|
.ci-status-icon-disabled,
|
||||||
.ci-status-icon-not-found,
|
.ci-status-icon-not-found,
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
class Dashboard::TodosController < Dashboard::ApplicationController
|
class Dashboard::TodosController < Dashboard::ApplicationController
|
||||||
include TodosHelper
|
|
||||||
|
|
||||||
before_action :find_todos, only: [:index, :destroy_all]
|
before_action :find_todos, only: [:index, :destroy_all]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@ -13,7 +11,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to dashboard_todos_path, notice: 'Todo was successfully marked as done.' }
|
format.html { redirect_to dashboard_todos_path, notice: 'Todo was successfully marked as done.' }
|
||||||
format.js { head :ok }
|
format.js { head :ok }
|
||||||
format.json { render json: { count: todos_pending_count, done_count: todos_done_count } }
|
format.json { render json: todos_counts }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -23,7 +21,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' }
|
format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' }
|
||||||
format.js { head :ok }
|
format.js { head :ok }
|
||||||
format.json { render json: { count: todos_pending_count, done_count: todos_done_count } }
|
format.json { render json: todos_counts }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -36,4 +34,11 @@ class Dashboard::TodosController < Dashboard::ApplicationController
|
||||||
def find_todos
|
def find_todos
|
||||||
@todos ||= TodosFinder.new(current_user, params).execute
|
@todos ||= TodosFinder.new(current_user, params).execute
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def todos_counts
|
||||||
|
{
|
||||||
|
count: TodosFinder.new(current_user, state: :pending).execute.count,
|
||||||
|
done_count: TodosFinder.new(current_user, state: :done).execute.count
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -107,7 +107,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||||
# Only allow properly saved users to login.
|
# Only allow properly saved users to login.
|
||||||
if @user.persisted? && @user.valid?
|
if @user.persisted? && @user.valid?
|
||||||
log_audit_event(@user, with: oauth['provider'])
|
log_audit_event(@user, with: oauth['provider'])
|
||||||
sign_in_and_redirect(@user)
|
if @user.two_factor_enabled?
|
||||||
|
prompt_for_two_factor(@user)
|
||||||
|
else
|
||||||
|
sign_in_and_redirect(@user)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
error_message = @user.errors.full_messages.to_sentence
|
error_message = @user.errors.full_messages.to_sentence
|
||||||
|
|
||||||
|
|
|
@ -119,6 +119,10 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||||
render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
|
render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
rescue ActiveRecord::StaleObjectError
|
||||||
|
@conflict = true
|
||||||
|
render :edit
|
||||||
end
|
end
|
||||||
|
|
||||||
def referenced_merge_requests
|
def referenced_merge_requests
|
||||||
|
@ -216,7 +220,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||||
def issue_params
|
def issue_params
|
||||||
params.require(:issue).permit(
|
params.require(:issue).permit(
|
||||||
:title, :assignee_id, :position, :description, :confidential,
|
:title, :assignee_id, :position, :description, :confidential,
|
||||||
:milestone_id, :due_date, :state_event, :task_num, label_ids: []
|
:milestone_id, :due_date, :state_event, :task_num, :lock_version, label_ids: []
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||||
|
|
||||||
def show
|
def show
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html
|
format.html { define_discussion_vars }
|
||||||
|
|
||||||
format.json do
|
format.json do
|
||||||
render json: @merge_request
|
render json: @merge_request
|
||||||
|
@ -82,7 +82,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||||
@merge_request_diff = @merge_request.merge_request_diff
|
@merge_request_diff = @merge_request.merge_request_diff
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html
|
format.html { define_discussion_vars }
|
||||||
format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } }
|
format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -108,7 +108,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||||
|
|
||||||
def commits
|
def commits
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { render 'show' }
|
format.html do
|
||||||
|
define_discussion_vars
|
||||||
|
|
||||||
|
render 'show'
|
||||||
|
end
|
||||||
format.json do
|
format.json do
|
||||||
# Get commits from repository
|
# Get commits from repository
|
||||||
# or from cache if already merged
|
# or from cache if already merged
|
||||||
|
@ -123,7 +127,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||||
|
|
||||||
def builds
|
def builds
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { render 'show' }
|
format.html do
|
||||||
|
define_discussion_vars
|
||||||
|
|
||||||
|
render 'show'
|
||||||
|
end
|
||||||
format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_builds') } }
|
format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_builds') } }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -188,6 +196,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||||
else
|
else
|
||||||
render "edit"
|
render "edit"
|
||||||
end
|
end
|
||||||
|
rescue ActiveRecord::StaleObjectError
|
||||||
|
@conflict = true
|
||||||
|
render :edit
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_wip
|
def remove_wip
|
||||||
|
@ -353,14 +364,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||||
@merge_request.unlock_mr
|
@merge_request.unlock_mr
|
||||||
@merge_request.close
|
@merge_request.close
|
||||||
end
|
end
|
||||||
|
|
||||||
if request.format == :html || action_name == 'show'
|
|
||||||
define_show_html_vars
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Discussion tab data is only required on html requests
|
# Discussion tab data is rendered on html responses of actions
|
||||||
def define_show_html_vars
|
# :show, :diff, :commits, :builds. but not when request the data through AJAX
|
||||||
|
def define_discussion_vars
|
||||||
# Build a note object for comment form
|
# Build a note object for comment form
|
||||||
@note = @project.notes.new(noteable: @noteable)
|
@note = @project.notes.new(noteable: @noteable)
|
||||||
|
|
||||||
|
@ -419,7 +427,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||||
:title, :assignee_id, :source_project_id, :source_branch,
|
:title, :assignee_id, :source_project_id, :source_branch,
|
||||||
:target_project_id, :target_branch, :milestone_id,
|
:target_project_id, :target_branch, :milestone_id,
|
||||||
:state_event, :description, :task_num, :force_remove_source_branch,
|
:state_event, :description, :task_num, :force_remove_source_branch,
|
||||||
label_ids: []
|
:lock_version, label_ids: []
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,6 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def protected_branch_params
|
def protected_branch_params
|
||||||
params.require(:protected_branch).permit(:name, :developers_can_push)
|
params.require(:protected_branch).permit(:name, :developers_can_push, :developers_can_merge)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,7 @@ class Projects::TodosController < Projects::ApplicationController
|
||||||
todo = TodoService.new.mark_todo(issuable, current_user)
|
todo = TodoService.new.mark_todo(issuable, current_user)
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
count: current_user.todos_pending_count,
|
count: TodosFinder.new(current_user, state: :pending).execute.count,
|
||||||
delete_path: dashboard_todo_path(todo)
|
delete_path: dashboard_todo_path(todo)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
# action_id: integer
|
# action_id: integer
|
||||||
# author_id: integer
|
# author_id: integer
|
||||||
# project_id; integer
|
# project_id; integer
|
||||||
# state: 'pending' or 'done'
|
# state: 'pending' (default) or 'done'
|
||||||
# type: 'Issue' or 'MergeRequest'
|
# type: 'Issue' or 'MergeRequest'
|
||||||
#
|
#
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class TodosFinder
|
||||||
private
|
private
|
||||||
|
|
||||||
def action_id?
|
def action_id?
|
||||||
action_id.present? && [Todo::ASSIGNED, Todo::MENTIONED, Todo::BUILD_FAILED, Todo::MARKED].include?(action_id.to_i)
|
action_id.present? && Todo::ACTION_NAMES.has_key?(action_id.to_i)
|
||||||
end
|
end
|
||||||
|
|
||||||
def action_id
|
def action_id
|
||||||
|
|
|
@ -31,7 +31,7 @@ module AppearancesHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def navbar_icon(icon_name, size: 16)
|
def custom_icon(icon_name, size: 16)
|
||||||
render "shared/icons/#{icon_name}.svg", size: size
|
render "shared/icons/#{icon_name}.svg", size: size
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@ module BranchesHelper
|
||||||
def can_push_branch?(project, branch_name)
|
def can_push_branch?(project, branch_name)
|
||||||
return false unless project.repository.branch_exists?(branch_name)
|
return false unless project.repository.branch_exists?(branch_name)
|
||||||
|
|
||||||
::Gitlab::GitAccess.new(current_user, project, 'web').can_push_to_branch?(branch_name)
|
::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(branch_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_branches
|
def project_branches
|
||||||
|
|
|
@ -29,8 +29,10 @@ module CiStatusHelper
|
||||||
'check'
|
'check'
|
||||||
when 'failed'
|
when 'failed'
|
||||||
'close'
|
'close'
|
||||||
when 'running', 'pending'
|
when 'pending'
|
||||||
'clock-o'
|
'clock-o'
|
||||||
|
when 'running'
|
||||||
|
'spinner'
|
||||||
else
|
else
|
||||||
'circle'
|
'circle'
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
module TodosHelper
|
module TodosHelper
|
||||||
def todos_pending_count
|
def todos_pending_count
|
||||||
TodosFinder.new(current_user, state: :pending).execute.count
|
@todos_pending_count ||= TodosFinder.new(current_user, state: :pending).execute.count
|
||||||
end
|
end
|
||||||
|
|
||||||
def todos_done_count
|
def todos_done_count
|
||||||
TodosFinder.new(current_user, state: :done).execute.count
|
@todos_done_count ||= TodosFinder.new(current_user, state: :done).execute.count
|
||||||
end
|
end
|
||||||
|
|
||||||
def todo_action_name(todo)
|
def todo_action_name(todo)
|
||||||
|
@ -13,6 +13,7 @@ module TodosHelper
|
||||||
when Todo::MENTIONED then 'mentioned you on'
|
when Todo::MENTIONED then 'mentioned you on'
|
||||||
when Todo::BUILD_FAILED then 'The build failed for your'
|
when Todo::BUILD_FAILED then 'The build failed for your'
|
||||||
when Todo::MARKED then 'added a todo for'
|
when Todo::MARKED then 'added a todo for'
|
||||||
|
when Todo::APPROVAL_REQUIRED then 'set you as an approver for'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -87,6 +87,12 @@ module Issuable
|
||||||
User.find(assignee_id_was).update_cache_counts if assignee_id_was
|
User.find(assignee_id_was).update_cache_counts if assignee_id_was
|
||||||
assignee.update_cache_counts if assignee
|
assignee.update_cache_counts if assignee
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# We want to use optimistic lock for cases when only title or description are involved
|
||||||
|
# http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html
|
||||||
|
def locking_enabled?
|
||||||
|
title_changed? || description_changed?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
|
|
|
@ -552,7 +552,13 @@ class MergeRequest < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_be_merged_by?(user)
|
def can_be_merged_by?(user)
|
||||||
::Gitlab::GitAccess.new(user, project, 'web').can_push_to_branch?(target_branch)
|
access = ::Gitlab::UserAccess.new(user, project: project)
|
||||||
|
access.can_push_to_branch?(target_branch) || access.can_merge_to_branch?(target_branch)
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_be_merged_via_command_line_by?(user)
|
||||||
|
access = ::Gitlab::UserAccess.new(user, project: project)
|
||||||
|
access.can_push_to_branch?(target_branch)
|
||||||
end
|
end
|
||||||
|
|
||||||
def mergeable_ci_state?
|
def mergeable_ci_state?
|
||||||
|
|
|
@ -836,6 +836,10 @@ class Project < ActiveRecord::Base
|
||||||
protected_branches.matching(branch_name).any?(&:developers_can_push)
|
protected_branches.matching(branch_name).any?(&:developers_can_push)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def developers_can_merge_to_protected_branch?(branch_name)
|
||||||
|
protected_branches.matching(branch_name).any?(&:developers_can_merge)
|
||||||
|
end
|
||||||
|
|
||||||
def forked?
|
def forked?
|
||||||
!(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
|
!(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
|
||||||
end
|
end
|
||||||
|
|
|
@ -769,9 +769,9 @@ class Repository
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def merge(user, source_sha, target_branch, options = {})
|
def merge(user, merge_request, options = {})
|
||||||
our_commit = rugged.branches[target_branch].target
|
our_commit = rugged.branches[merge_request.target_branch].target
|
||||||
their_commit = rugged.lookup(source_sha)
|
their_commit = rugged.lookup(merge_request.diff_head_sha)
|
||||||
|
|
||||||
raise "Invalid merge target" if our_commit.nil?
|
raise "Invalid merge target" if our_commit.nil?
|
||||||
raise "Invalid merge source" if their_commit.nil?
|
raise "Invalid merge source" if their_commit.nil?
|
||||||
|
@ -779,14 +779,16 @@ class Repository
|
||||||
merge_index = rugged.merge_commits(our_commit, their_commit)
|
merge_index = rugged.merge_commits(our_commit, their_commit)
|
||||||
return false if merge_index.conflicts?
|
return false if merge_index.conflicts?
|
||||||
|
|
||||||
commit_with_hooks(user, target_branch) do |ref|
|
commit_with_hooks(user, merge_request.target_branch) do |tmp_ref|
|
||||||
actual_options = options.merge(
|
actual_options = options.merge(
|
||||||
parents: [our_commit, their_commit],
|
parents: [our_commit, their_commit],
|
||||||
tree: merge_index.write_tree(rugged),
|
tree: merge_index.write_tree(rugged),
|
||||||
update_ref: ref
|
update_ref: tmp_ref
|
||||||
)
|
)
|
||||||
|
|
||||||
Rugged::Commit.create(rugged, actual_options)
|
commit_id = Rugged::Commit.create(rugged, actual_options)
|
||||||
|
merge_request.update(in_progress_merge_commit_sha: commit_id)
|
||||||
|
commit_id
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
class Todo < ActiveRecord::Base
|
class Todo < ActiveRecord::Base
|
||||||
ASSIGNED = 1
|
ASSIGNED = 1
|
||||||
MENTIONED = 2
|
MENTIONED = 2
|
||||||
BUILD_FAILED = 3
|
BUILD_FAILED = 3
|
||||||
MARKED = 4
|
MARKED = 4
|
||||||
|
APPROVAL_REQUIRED = 5 # This is an EE-only feature
|
||||||
|
|
||||||
ACTION_NAMES = {
|
ACTION_NAMES = {
|
||||||
ASSIGNED => :assigned,
|
ASSIGNED => :assigned,
|
||||||
MENTIONED => :mentioned,
|
MENTIONED => :mentioned,
|
||||||
BUILD_FAILED => :build_failed,
|
BUILD_FAILED => :build_failed,
|
||||||
MARKED => :marked
|
MARKED => :marked,
|
||||||
|
APPROVAL_REQUIRED => :approval_required
|
||||||
}
|
}
|
||||||
|
|
||||||
belongs_to :author, class_name: "User"
|
belongs_to :author, class_name: "User"
|
||||||
|
|
|
@ -87,7 +87,7 @@ class User < ActiveRecord::Base
|
||||||
has_many :builds, dependent: :nullify, class_name: 'Ci::Build'
|
has_many :builds, dependent: :nullify, class_name: 'Ci::Build'
|
||||||
has_many :todos, dependent: :destroy
|
has_many :todos, dependent: :destroy
|
||||||
has_many :notification_settings, dependent: :destroy
|
has_many :notification_settings, dependent: :destroy
|
||||||
has_many :award_emoji, as: :awardable, dependent: :destroy
|
has_many :award_emoji, dependent: :destroy
|
||||||
|
|
||||||
#
|
#
|
||||||
# Validations
|
# Validations
|
||||||
|
|
|
@ -23,7 +23,7 @@ module Commits
|
||||||
private
|
private
|
||||||
|
|
||||||
def check_push_permissions
|
def check_push_permissions
|
||||||
allowed = ::Gitlab::GitAccess.new(current_user, project, 'web').can_push_to_branch?(@target_branch)
|
allowed = ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(@target_branch)
|
||||||
|
|
||||||
unless allowed
|
unless allowed
|
||||||
raise ValidationError.new('You are not allowed to push into this branch')
|
raise ValidationError.new('You are not allowed to push into this branch')
|
||||||
|
@ -31,7 +31,7 @@ module Commits
|
||||||
|
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_target_branch(new_branch)
|
def create_target_branch(new_branch)
|
||||||
# Temporary branch exists and contains the change commit
|
# Temporary branch exists and contains the change commit
|
||||||
return success if repository.find_branch(new_branch)
|
return success if repository.find_branch(new_branch)
|
||||||
|
|
|
@ -42,7 +42,7 @@ module Files
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate
|
def validate
|
||||||
allowed = ::Gitlab::GitAccess.new(current_user, project, 'web').can_push_to_branch?(@target_branch)
|
allowed = ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(@target_branch)
|
||||||
|
|
||||||
unless allowed
|
unless allowed
|
||||||
raise_error("You are not allowed to push into this branch")
|
raise_error("You are not allowed to push into this branch")
|
||||||
|
|
|
@ -89,7 +89,8 @@ class GitPushService < BaseService
|
||||||
# Set protection on the default branch if configured
|
# Set protection on the default branch if configured
|
||||||
if current_application_settings.default_branch_protection != PROTECTION_NONE
|
if current_application_settings.default_branch_protection != PROTECTION_NONE
|
||||||
developers_can_push = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_PUSH ? true : false
|
developers_can_push = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_PUSH ? true : false
|
||||||
@project.protected_branches.create({ name: @project.default_branch, developers_can_push: developers_can_push })
|
developers_can_merge = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_MERGE ? true : false
|
||||||
|
@project.protected_branches.create({ name: @project.default_branch, developers_can_push: developers_can_push, developers_can_merge: developers_can_merge })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ module MergeRequests
|
||||||
committer: committer
|
committer: committer
|
||||||
}
|
}
|
||||||
|
|
||||||
commit_id = repository.merge(current_user, merge_request.diff_head_sha, merge_request.target_branch, options)
|
commit_id = repository.merge(current_user, merge_request, options)
|
||||||
merge_request.update(merge_commit_sha: commit_id)
|
merge_request.update(merge_commit_sha: commit_id)
|
||||||
rescue GitHooksService::PreReceiveError => e
|
rescue GitHooksService::PreReceiveError => e
|
||||||
merge_request.update(merge_error: e.message)
|
merge_request.update(merge_error: e.message)
|
||||||
|
@ -43,6 +43,8 @@ module MergeRequests
|
||||||
merge_request.update(merge_error: "Something went wrong during merge")
|
merge_request.update(merge_error: "Something went wrong during merge")
|
||||||
Rails.logger.error(e.message)
|
Rails.logger.error(e.message)
|
||||||
false
|
false
|
||||||
|
ensure
|
||||||
|
merge_request.update(in_progress_merge_commit_sha: nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_merge
|
def after_merge
|
||||||
|
|
|
@ -48,7 +48,7 @@ module MergeRequests
|
||||||
end
|
end
|
||||||
|
|
||||||
def force_push?
|
def force_push?
|
||||||
Gitlab::ForcePushCheck.force_push?(@project, @oldrev, @newrev)
|
Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Refresh merge request diff if we push to source or target branch of merge request
|
# Refresh merge request diff if we push to source or target branch of merge request
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
#
|
#
|
||||||
module Projects
|
module Projects
|
||||||
class HousekeepingService < BaseService
|
class HousekeepingService < BaseService
|
||||||
include Gitlab::ShellAdapter
|
|
||||||
|
|
||||||
LEASE_TIMEOUT = 3600
|
LEASE_TIMEOUT = 3600
|
||||||
|
|
||||||
class LeaseTaken < StandardError
|
class LeaseTaken < StandardError
|
||||||
|
@ -24,11 +22,7 @@ module Projects
|
||||||
def execute
|
def execute
|
||||||
raise LeaseTaken unless try_obtain_lease
|
raise LeaseTaken unless try_obtain_lease
|
||||||
|
|
||||||
GitlabShellOneShotWorker.perform_async(:gc, @project.repository_storage_path, @project.path_with_namespace)
|
execute_gitlab_shell_gc
|
||||||
ensure
|
|
||||||
Gitlab::Metrics.measure(:reset_pushes_since_gc) do
|
|
||||||
update_pushes_since_gc(0)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def needed?
|
def needed?
|
||||||
|
@ -36,19 +30,27 @@ module Projects
|
||||||
end
|
end
|
||||||
|
|
||||||
def increment!
|
def increment!
|
||||||
Gitlab::Metrics.measure(:increment_pushes_since_gc) do
|
if Gitlab::ExclusiveLease.new("project_housekeeping:increment!:#{@project.id}", timeout: 60).try_obtain
|
||||||
update_pushes_since_gc(@project.pushes_since_gc + 1)
|
Gitlab::Metrics.measure(:increment_pushes_since_gc) do
|
||||||
|
update_pushes_since_gc(@project.pushes_since_gc + 1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def update_pushes_since_gc(new_value)
|
def execute_gitlab_shell_gc
|
||||||
if Gitlab::ExclusiveLease.new("project_housekeeping:update_pushes_since_gc:#{project.id}", timeout: 60).try_obtain
|
GitGarbageCollectWorker.perform_async(@project.id)
|
||||||
@project.update_column(:pushes_since_gc, new_value)
|
ensure
|
||||||
|
Gitlab::Metrics.measure(:reset_pushes_since_gc) do
|
||||||
|
update_pushes_since_gc(0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_pushes_since_gc(new_value)
|
||||||
|
@project.update_column(:pushes_since_gc, new_value)
|
||||||
|
end
|
||||||
|
|
||||||
def try_obtain_lease
|
def try_obtain_lease
|
||||||
Gitlab::Metrics.measure(:obtain_housekeeping_lease) do
|
Gitlab::Metrics.measure(:obtain_housekeeping_lease) do
|
||||||
lease = ::Gitlab::ExclusiveLease.new("project_housekeeping:#{@project.id}", timeout: LEASE_TIMEOUT)
|
lease = ::Gitlab::ExclusiveLease.new("project_housekeeping:#{@project.id}", timeout: LEASE_TIMEOUT)
|
||||||
|
|
|
@ -1,31 +1,41 @@
|
||||||
- project = build.project
|
- project = build.project
|
||||||
%tr.build
|
%tr.build.commit
|
||||||
%td.status
|
%td.status
|
||||||
= ci_status_with_icon(build.status)
|
= ci_status_with_icon(build.status)
|
||||||
|
|
||||||
%td.build-link
|
%td
|
||||||
- if can?(current_user, :read_build, build.project)
|
.branch-commit
|
||||||
= link_to namespace_project_build_url(build.project.namespace, build.project, build) do
|
- if can?(current_user, :read_build, build.project)
|
||||||
%strong Build ##{build.id}
|
= link_to namespace_project_build_url(build.project.namespace, build.project, build) do
|
||||||
- else
|
%span.build-link ##{build.id}
|
||||||
%strong Build ##{build.id}
|
- else
|
||||||
|
%span.build-link ##{build.id}
|
||||||
|
|
||||||
- if build.stuck?
|
- if build.stuck?
|
||||||
%i.fa.fa-warning.text-warning
|
%i.fa.fa-warning.text-warning
|
||||||
|
|
||||||
|
- if build.ref
|
||||||
|
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref), class: "monospace branch-name"
|
||||||
|
- else
|
||||||
|
.light none
|
||||||
|
= custom_icon("icon_commit")
|
||||||
|
|
||||||
|
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace commit-id"
|
||||||
|
|
||||||
|
.label-container
|
||||||
|
- if build.tags.any?
|
||||||
|
- build.tags.each do |tag|
|
||||||
|
%span.label.label-primary
|
||||||
|
= tag
|
||||||
|
- if build.try(:trigger_request)
|
||||||
|
%span.label.label-info triggered
|
||||||
|
- if build.try(:allow_failure)
|
||||||
|
%span.label.label-danger allowed to fail
|
||||||
|
|
||||||
%td
|
%td
|
||||||
- if project
|
- if project
|
||||||
= link_to project.name_with_namespace, admin_namespace_project_path(project.namespace, project)
|
= link_to project.name_with_namespace, admin_namespace_project_path(project.namespace, project)
|
||||||
|
|
||||||
%td
|
|
||||||
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace"
|
|
||||||
|
|
||||||
%td
|
|
||||||
- if build.ref
|
|
||||||
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref)
|
|
||||||
- else
|
|
||||||
.light none
|
|
||||||
|
|
||||||
%td
|
%td
|
||||||
- if build.try(:runner)
|
- if build.try(:runner)
|
||||||
= runner_link(build.runner)
|
= runner_link(build.runner)
|
||||||
|
@ -36,22 +46,15 @@
|
||||||
#{build.stage} / #{build.name}
|
#{build.stage} / #{build.name}
|
||||||
|
|
||||||
%td
|
%td
|
||||||
- if build.tags.any?
|
|
||||||
- build.tags.each do |tag|
|
|
||||||
%span.label.label-primary
|
|
||||||
= tag
|
|
||||||
- if build.try(:trigger_request)
|
|
||||||
%span.label.label-info triggered
|
|
||||||
- if build.try(:allow_failure)
|
|
||||||
%span.label.label-danger allowed to fail
|
|
||||||
|
|
||||||
%td.duration
|
|
||||||
- if build.duration
|
- if build.duration
|
||||||
#{duration_in_words(build.finished_at, build.started_at)}
|
%p.duration
|
||||||
|
= custom_icon("icon_timer")
|
||||||
|
= duration_in_numbers(build.finished_at, build.started_at)
|
||||||
|
|
||||||
%td.timestamp
|
|
||||||
- if build.finished_at
|
- if build.finished_at
|
||||||
%span #{time_ago_with_tooltip(build.finished_at)}
|
%p.finished-at
|
||||||
|
= icon("calendar")
|
||||||
|
%span #{time_ago_with_tooltip(build.finished_at)}
|
||||||
|
|
||||||
- if defined?(coverage) && coverage
|
- if defined?(coverage) && coverage
|
||||||
%td.coverage
|
%td.coverage
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
.row-content-block.second-block
|
.row-content-block.second-block
|
||||||
#{(@scope || 'all').capitalize} builds
|
#{(@scope || 'all').capitalize} builds
|
||||||
|
|
||||||
%ul.content-list
|
%ul.content-list.builds-content-list
|
||||||
- if @builds.blank?
|
- if @builds.blank?
|
||||||
%li
|
%li
|
||||||
.nothing-here-block No builds to show
|
.nothing-here-block No builds to show
|
||||||
|
@ -37,15 +37,11 @@
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th Status
|
%th Status
|
||||||
%th Build ID
|
|
||||||
%th Project
|
|
||||||
%th Commit
|
%th Commit
|
||||||
%th Ref
|
%th Project
|
||||||
%th Runner
|
%th Runner
|
||||||
%th Name
|
%th Name
|
||||||
%th Tags
|
%th
|
||||||
%th Duration
|
|
||||||
%th Finished at
|
|
||||||
%th
|
%th
|
||||||
|
|
||||||
- @builds.each do |build|
|
- @builds.each do |build|
|
||||||
|
|
|
@ -9,14 +9,14 @@
|
||||||
%span
|
%span
|
||||||
To do
|
To do
|
||||||
%span.badge
|
%span.badge
|
||||||
= todos_pending_count
|
= number_with_delimiter(todos_pending_count)
|
||||||
- todo_done_active = ('active' if params[:state] == 'done')
|
- todo_done_active = ('active' if params[:state] == 'done')
|
||||||
%li{class: "todos-done #{todo_done_active}"}
|
%li{class: "todos-done #{todo_done_active}"}
|
||||||
= link_to todos_filter_path(state: 'done') do
|
= link_to todos_filter_path(state: 'done') do
|
||||||
%span
|
%span
|
||||||
Done
|
Done
|
||||||
%span.badge
|
%span.badge
|
||||||
= todos_done_count
|
= number_with_delimiter(todos_done_count)
|
||||||
|
|
||||||
.nav-controls
|
.nav-controls
|
||||||
- if @todos.any?(&:pending?)
|
- if @todos.any?(&:pending?)
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
.nav-controls
|
.nav-controls
|
||||||
- if can?(current_user, :admin_milestones, @group)
|
- if can?(current_user, :admin_milestones, @group)
|
||||||
= link_to new_group_milestone_path(@group), class: "btn btn-new" do
|
= link_to new_group_milestone_path(@group), class: "btn btn-new" do
|
||||||
= icon('plus')
|
|
||||||
New Milestone
|
New Milestone
|
||||||
|
|
||||||
.row-content-block
|
.row-content-block
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
- if can? current_user, :admin_group, @group
|
- if can? current_user, :admin_group, @group
|
||||||
.controls
|
.controls
|
||||||
= link_to new_project_path(namespace_id: @group.id), class: "btn btn-sm btn-success" do
|
= link_to new_project_path(namespace_id: @group.id), class: "btn btn-sm btn-success" do
|
||||||
= icon('plus')
|
|
||||||
New Project
|
New Project
|
||||||
%ul.well-list
|
%ul.well-list
|
||||||
- @projects.each do |project|
|
- @projects.each do |project|
|
||||||
|
|
|
@ -6,8 +6,7 @@
|
||||||
|
|
||||||
.cover-block.groups-cover-block
|
.cover-block.groups-cover-block
|
||||||
%div{ class: container_class }
|
%div{ class: container_class }
|
||||||
= link_to group_icon(@group), target: '_blank' do
|
= image_tag group_icon(@group), class: "avatar group-avatar s70"
|
||||||
= image_tag group_icon(@group), class: "avatar group-avatar s70"
|
|
||||||
.group-info
|
.group-info
|
||||||
.cover-title
|
.cover-title
|
||||||
%h1
|
%h1
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
= link_to '#', class: 'nav-header-btn text-center toggle-nav-collapse', title: "Open/Close" do
|
|
||||||
%span.sr-only Toggle navigation
|
|
||||||
= icon('bars')
|
|
|
@ -1,6 +1,13 @@
|
||||||
.page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
|
.page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
|
||||||
.sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
|
.sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
|
||||||
= render partial: 'layouts/collapse_button'
|
.sidebar-action-buttons
|
||||||
|
= link_to '#', class: 'nav-header-btn toggle-nav-collapse', title: "Open/Close" do
|
||||||
|
%span.sr-only Toggle navigation
|
||||||
|
= icon('bars')
|
||||||
|
= link_to '#', class: "nav-header-btn pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: {placement: 'right', container: 'body'} do
|
||||||
|
%span.sr-only Toggle navigation pinning
|
||||||
|
= icon('thumb-tack')
|
||||||
|
|
||||||
- if defined?(sidebar) && sidebar
|
- if defined?(sidebar) && sidebar
|
||||||
= render "layouts/nav/#{sidebar}"
|
= render "layouts/nav/#{sidebar}"
|
||||||
- elsif current_user
|
- elsif current_user
|
||||||
|
@ -8,9 +15,6 @@
|
||||||
- else
|
- else
|
||||||
= render 'layouts/nav/explore'
|
= render 'layouts/nav/explore'
|
||||||
|
|
||||||
= link_to '#', class: "nav-header-btn text-center pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: {placement: 'right', container: 'body'} do
|
|
||||||
%span.sr-only Toggle navigation pinning
|
|
||||||
= icon('thumb-tack')
|
|
||||||
- if defined?(nav) && nav
|
- if defined?(nav) && nav
|
||||||
.layout-nav
|
.layout-nav
|
||||||
.container-fluid
|
.container-fluid
|
||||||
|
|
|
@ -1,64 +1,44 @@
|
||||||
%ul.nav.nav-sidebar
|
%ul.nav.nav-sidebar
|
||||||
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do
|
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do
|
||||||
= link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
|
= link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
|
||||||
.icon-container
|
|
||||||
= navbar_icon('project')
|
|
||||||
%span
|
%span
|
||||||
Projects
|
Projects
|
||||||
= nav_link(controller: :todos) do
|
= nav_link(controller: :todos) do
|
||||||
= link_to dashboard_todos_path, title: 'Todos' do
|
= link_to dashboard_todos_path, title: 'Todos' do
|
||||||
.icon-container
|
|
||||||
= icon('bell fw')
|
|
||||||
%span
|
%span
|
||||||
Todos
|
Todos
|
||||||
%span.count= number_with_delimiter(todos_pending_count)
|
%span.count= number_with_delimiter(todos_pending_count)
|
||||||
= nav_link(path: 'dashboard#activity') do
|
= nav_link(path: 'dashboard#activity') do
|
||||||
= link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
|
= link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
|
||||||
.icon-container
|
|
||||||
= navbar_icon('activity')
|
|
||||||
%span
|
%span
|
||||||
Activity
|
Activity
|
||||||
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
|
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
|
||||||
= link_to dashboard_groups_path, title: 'Groups' do
|
= link_to dashboard_groups_path, title: 'Groups' do
|
||||||
.icon-container
|
|
||||||
= navbar_icon('group')
|
|
||||||
%span
|
%span
|
||||||
Groups
|
Groups
|
||||||
= nav_link(controller: 'dashboard/milestones') do
|
= nav_link(controller: 'dashboard/milestones') do
|
||||||
= link_to dashboard_milestones_path, title: 'Milestones' do
|
= link_to dashboard_milestones_path, title: 'Milestones' do
|
||||||
.icon-container
|
|
||||||
= navbar_icon('milestones')
|
|
||||||
%span
|
%span
|
||||||
Milestones
|
Milestones
|
||||||
= nav_link(path: 'dashboard#issues') do
|
= nav_link(path: 'dashboard#issues') do
|
||||||
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
|
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
|
||||||
.icon-container
|
|
||||||
= navbar_icon('issues')
|
|
||||||
%span
|
%span
|
||||||
Issues
|
Issues
|
||||||
%span.count= number_with_delimiter(current_user.assigned_issues.opened.count)
|
%span.count= number_with_delimiter(current_user.assigned_issues.opened.count)
|
||||||
= nav_link(path: 'dashboard#merge_requests') do
|
= nav_link(path: 'dashboard#merge_requests') do
|
||||||
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
|
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
|
||||||
.icon-container
|
|
||||||
= navbar_icon('mr')
|
|
||||||
%span
|
%span
|
||||||
Merge Requests
|
Merge Requests
|
||||||
%span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count)
|
%span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count)
|
||||||
= nav_link(controller: :snippets) do
|
= nav_link(controller: :snippets) do
|
||||||
= link_to dashboard_snippets_path, title: 'Snippets' do
|
= link_to dashboard_snippets_path, title: 'Snippets' do
|
||||||
.icon-container
|
|
||||||
= icon('clipboard fw')
|
|
||||||
%span
|
%span
|
||||||
Snippets
|
Snippets
|
||||||
= nav_link(controller: :help) do
|
= nav_link(controller: :help) do
|
||||||
= link_to help_path, title: 'Help' do
|
= link_to help_path, title: 'Help' do
|
||||||
.icon-container
|
|
||||||
= icon('question-circle fw')
|
|
||||||
%span
|
%span
|
||||||
Help
|
Help
|
||||||
= nav_link(html_options: {class: profile_tab_class}) do
|
= nav_link(html_options: {class: profile_tab_class}) do
|
||||||
= link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do
|
= link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do
|
||||||
.icon-container
|
|
||||||
= icon('user fw')
|
|
||||||
%span
|
%span
|
||||||
Profile Settings
|
Profile Settings
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
%span
|
%span
|
||||||
Applications
|
Applications
|
||||||
= nav_link(controller: :personal_access_tokens) do
|
= nav_link(controller: :personal_access_tokens) do
|
||||||
= link_to profile_personal_access_tokens_path, title: 'Personal Access Tokens' do
|
= link_to profile_personal_access_tokens_path, title: 'Access Tokens' do
|
||||||
%span
|
%span
|
||||||
Personal Access Tokens
|
Access Tokens
|
||||||
= nav_link(controller: :emails) do
|
= nav_link(controller: :emails) do
|
||||||
= link_to profile_emails_path, title: 'Emails' do
|
= link_to profile_emails_path, title: 'Emails' do
|
||||||
%span
|
%span
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
= link_to ci_lint_path, class: 'btn btn-default' do
|
= link_to ci_lint_path, class: 'btn btn-default' do
|
||||||
%span CI Lint
|
%span CI Lint
|
||||||
|
|
||||||
%ul.content-list
|
%ul.content-list.builds-content-list
|
||||||
- if @builds.blank?
|
- if @builds.blank?
|
||||||
%li
|
%li
|
||||||
.nothing-here-block No builds to show
|
.nothing-here-block No builds to show
|
||||||
|
@ -46,14 +46,10 @@
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th Status
|
%th Status
|
||||||
%th Build ID
|
|
||||||
%th Commit
|
%th Commit
|
||||||
%th Ref
|
|
||||||
%th Stage
|
%th Stage
|
||||||
%th Name
|
%th Name
|
||||||
%th Tags
|
%th
|
||||||
%th Duration
|
|
||||||
%th Finished at
|
|
||||||
- if @project.build_coverage_enabled?
|
- if @project.build_coverage_enabled?
|
||||||
%th Coverage
|
%th Coverage
|
||||||
%th
|
%th
|
||||||
|
|
|
@ -1,32 +1,45 @@
|
||||||
%tr.build
|
%tr.build.commit
|
||||||
%td.status
|
%td.status
|
||||||
- if can?(current_user, :read_build, build)
|
- if can?(current_user, :read_build, build)
|
||||||
= ci_status_with_icon(build.status, namespace_project_build_url(build.project.namespace, build.project, build))
|
= ci_status_with_icon(build.status, namespace_project_build_url(build.project.namespace, build.project, build))
|
||||||
- else
|
- else
|
||||||
= ci_status_with_icon(build.status)
|
= ci_status_with_icon(build.status)
|
||||||
|
|
||||||
%td.build-link
|
%td
|
||||||
- if can?(current_user, :read_build, build)
|
.branch-commit
|
||||||
= link_to namespace_project_build_url(build.project.namespace, build.project, build) do
|
- if can?(current_user, :read_build, build)
|
||||||
%strong ##{build.id}
|
= link_to namespace_project_build_url(build.project.namespace, build.project, build) do
|
||||||
- else
|
%span ##{build.id}
|
||||||
%strong ##{build.id}
|
|
||||||
|
|
||||||
- if build.stuck?
|
|
||||||
= icon('warning', class: 'text-warning has-tooltip', title: 'Build is stuck. Check runners.')
|
|
||||||
- if defined?(retried) && retried
|
|
||||||
= icon('warning', class: 'text-warning has-tooltip', title: 'Build was retried.')
|
|
||||||
|
|
||||||
- if defined?(commit_sha) && commit_sha
|
|
||||||
%td
|
|
||||||
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace"
|
|
||||||
|
|
||||||
- if defined?(ref) && ref
|
|
||||||
%td
|
|
||||||
- if build.ref
|
|
||||||
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref)
|
|
||||||
- else
|
- else
|
||||||
.light none
|
%span ##{build.id}
|
||||||
|
|
||||||
|
- if build.stuck?
|
||||||
|
= icon('warning', class: 'text-warning has-tooltip', title: 'Build is stuck. Check runners.')
|
||||||
|
- if defined?(retried) && retried
|
||||||
|
= icon('warning', class: 'text-warning has-tooltip', title: 'Build was retried.')
|
||||||
|
|
||||||
|
- if defined?(ref) && ref
|
||||||
|
- if build.ref
|
||||||
|
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref), class: "monospace branch-name"
|
||||||
|
- else
|
||||||
|
.light none
|
||||||
|
= custom_icon("icon_commit")
|
||||||
|
|
||||||
|
- if defined?(commit_sha) && commit_sha
|
||||||
|
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "commit-id monospace"
|
||||||
|
|
||||||
|
.label-container
|
||||||
|
- if build.tags.any?
|
||||||
|
- build.tags.each do |tag|
|
||||||
|
%span.label.label-primary
|
||||||
|
= tag
|
||||||
|
- if build.try(:trigger_request)
|
||||||
|
%span.label.label-info triggered
|
||||||
|
- if build.try(:allow_failure)
|
||||||
|
%span.label.label-danger allowed to fail
|
||||||
|
- if defined?(retried) && retried
|
||||||
|
%span.label.label-warning retried
|
||||||
|
|
||||||
|
|
||||||
- if defined?(runner) && runner
|
- if defined?(runner) && runner
|
||||||
%td
|
%td
|
||||||
|
@ -43,25 +56,14 @@
|
||||||
= build.name
|
= build.name
|
||||||
|
|
||||||
%td
|
%td
|
||||||
.label-container
|
|
||||||
- if build.tags.any?
|
|
||||||
- build.tags.each do |tag|
|
|
||||||
%span.label.label-primary
|
|
||||||
= tag
|
|
||||||
- if build.try(:trigger_request)
|
|
||||||
%span.label.label-info triggered
|
|
||||||
- if build.try(:allow_failure)
|
|
||||||
%span.label.label-danger allowed to fail
|
|
||||||
- if defined?(retried) && retried
|
|
||||||
%span.label.label-warning retried
|
|
||||||
|
|
||||||
%td.duration
|
|
||||||
- if build.duration
|
- if build.duration
|
||||||
#{duration_in_words(build.finished_at, build.started_at)}
|
%p.duration
|
||||||
|
= custom_icon("icon_timer")
|
||||||
%td.timestamp
|
= duration_in_numbers(build.finished_at, build.started_at)
|
||||||
- if build.finished_at
|
- if build.finished_at
|
||||||
%span #{time_ago_with_tooltip(build.finished_at)}
|
%p.finished-at
|
||||||
|
= icon("calendar")
|
||||||
|
%span #{time_ago_with_tooltip(build.finished_at)}
|
||||||
|
|
||||||
- if defined?(coverage) && coverage
|
- if defined?(coverage) && coverage
|
||||||
%td.coverage
|
%td.coverage
|
||||||
|
@ -79,4 +81,4 @@
|
||||||
= icon('remove', class: 'cred')
|
= icon('remove', class: 'cred')
|
||||||
- elsif defined?(allow_retry) && allow_retry && build.retryable?
|
- elsif defined?(allow_retry) && allow_retry && build.retryable?
|
||||||
= link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-build' do
|
= link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-build' do
|
||||||
= icon('refresh')
|
= icon('repeat')
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
- status = pipeline.status
|
- status = pipeline.status
|
||||||
%tr.commit
|
%tr.commit
|
||||||
%td.commit-link
|
%td.commit-link
|
||||||
= link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: "ci-status ci-#{status}" do
|
= link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id) do
|
||||||
= ci_icon_for_status(status)
|
= ci_status_with_icon(status)
|
||||||
%strong ##{pipeline.id}
|
|
||||||
|
|
||||||
%td
|
%td
|
||||||
%div.branch-commit
|
.branch-commit
|
||||||
|
= link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id) do
|
||||||
|
%span ##{pipeline.id}
|
||||||
- if pipeline.ref
|
- if pipeline.ref
|
||||||
= link_to pipeline.ref, namespace_project_commits_path(@project.namespace, @project, pipeline.ref), class: "monospace"
|
= link_to pipeline.ref, namespace_project_commits_path(@project.namespace, @project, pipeline.ref), class: "monospace branch-name"
|
||||||
·
|
= custom_icon("icon_commit")
|
||||||
= link_to pipeline.short_sha, namespace_project_commit_path(@project.namespace, @project, pipeline.sha), class: "commit-id monospace"
|
= link_to pipeline.short_sha, namespace_project_commit_path(@project.namespace, @project, pipeline.sha), class: "commit-id monospace"
|
||||||
|
|
||||||
- if pipeline.tag?
|
- if pipeline.tag?
|
||||||
%span.label.label-primary tag
|
%span.label.label-primary tag
|
||||||
- elsif pipeline.latest?
|
- elsif pipeline.latest?
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
|
|
||||||
%p.commit-title
|
%p.commit-title
|
||||||
- if commit = pipeline.commit
|
- if commit = pipeline.commit
|
||||||
|
= commit_author_avatar(commit, size: 20)
|
||||||
= link_to_gfm truncate(commit.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "commit-row-message"
|
= link_to_gfm truncate(commit.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "commit-row-message"
|
||||||
- else
|
- else
|
||||||
Cant find HEAD commit for this branch
|
Cant find HEAD commit for this branch
|
||||||
|
@ -45,22 +47,37 @@
|
||||||
%td
|
%td
|
||||||
- if pipeline.started_at && pipeline.finished_at
|
- if pipeline.started_at && pipeline.finished_at
|
||||||
%p.duration
|
%p.duration
|
||||||
|
= custom_icon("icon_timer")
|
||||||
= duration_in_numbers(pipeline.finished_at, pipeline.started_at)
|
= duration_in_numbers(pipeline.finished_at, pipeline.started_at)
|
||||||
|
- if pipeline.finished_at
|
||||||
|
%p.finished-at
|
||||||
|
= icon("calendar")
|
||||||
|
#{time_ago_with_tooltip(pipeline.finished_at)}
|
||||||
|
|
||||||
%td
|
%td.pipeline-actions
|
||||||
.controls.hidden-xs.pull-right
|
.controls.hidden-xs.pull-right
|
||||||
- artifacts = pipeline.builds.latest.select { |b| b.artifacts? }
|
- artifacts = pipeline.builds.latest.select { |b| b.artifacts? }
|
||||||
- if artifacts.present?
|
- if artifacts.present?
|
||||||
.dropdown.inline.build-artifacts
|
.btn-group.inline
|
||||||
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
|
.btn-group
|
||||||
= icon('download')
|
%a.dropdown-toggle.btn.btn-default{type: 'button', 'data-toggle' => 'dropdown'}
|
||||||
%b.caret
|
= icon("play")
|
||||||
%ul.dropdown-menu.dropdown-menu-align-right
|
%b.caret
|
||||||
- artifacts.each do |build|
|
%ul.dropdown-menu.dropdown-menu-align-right
|
||||||
%li
|
%li
|
||||||
= link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, build), rel: 'nofollow' do
|
= link_to '#' do
|
||||||
= icon("download")
|
= icon("play")
|
||||||
%span Download '#{build.name}' artifacts
|
%span Deploy to production
|
||||||
|
.btn-group
|
||||||
|
%a.dropdown-toggle.btn.btn-default.build-artifacts{type: 'button', 'data-toggle' => 'dropdown'}
|
||||||
|
= icon("download")
|
||||||
|
%b.caret
|
||||||
|
%ul.dropdown-menu.dropdown-menu-align-right
|
||||||
|
- artifacts.each do |build|
|
||||||
|
%li
|
||||||
|
= link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, build), rel: 'nofollow' do
|
||||||
|
= icon("download")
|
||||||
|
%span Download '#{build.name}' artifacts
|
||||||
|
|
||||||
- if can?(current_user, :update_pipeline, @project)
|
- if can?(current_user, :update_pipeline, @project)
|
||||||
- if pipeline.retryable?
|
- if pipeline.retryable?
|
||||||
|
|
|
@ -42,9 +42,7 @@
|
||||||
%th Status
|
%th Status
|
||||||
%th Build ID
|
%th Build ID
|
||||||
%th Name
|
%th Name
|
||||||
%th Tags
|
%th
|
||||||
%th Duration
|
|
||||||
%th Finished at
|
|
||||||
- if pipeline.project.build_coverage_enabled?
|
- if pipeline.project.build_coverage_enabled?
|
||||||
%th Coverage
|
%th Coverage
|
||||||
%th
|
%th
|
||||||
|
|
|
@ -50,10 +50,12 @@
|
||||||
|
|
||||||
%td.duration
|
%td.duration
|
||||||
- if generic_commit_status.duration
|
- if generic_commit_status.duration
|
||||||
|
= icon("clock-o")
|
||||||
#{duration_in_words(generic_commit_status.finished_at, generic_commit_status.started_at)}
|
#{duration_in_words(generic_commit_status.finished_at, generic_commit_status.started_at)}
|
||||||
|
|
||||||
%td.timestamp
|
%td.timestamp
|
||||||
- if generic_commit_status.finished_at
|
- if generic_commit_status.finished_at
|
||||||
|
= icon("calendar")
|
||||||
%span #{time_ago_with_tooltip(generic_commit_status.finished_at)}
|
%span #{time_ago_with_tooltip(generic_commit_status.finished_at)}
|
||||||
|
|
||||||
- if defined?(coverage) && coverage
|
- if defined?(coverage) && coverage
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
Code, test, and deploy together
|
Code, test, and deploy together
|
||||||
.blank-state
|
.blank-state
|
||||||
.blank-state-icon
|
.blank-state-icon
|
||||||
= navbar_icon("issues", size: 50)
|
= custom_icon("issues", size: 50)
|
||||||
%h3.blank-state-title
|
%h3.blank-state-title
|
||||||
You don't have any issues right now.
|
You don't have any issues right now.
|
||||||
%p.blank-state-text
|
%p.blank-state-text
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
%p
|
%p
|
||||||
Please resolve these conflicts or
|
Please resolve these conflicts or
|
||||||
- if @merge_request.can_be_merged_by?(current_user)
|
- if @merge_request.can_be_merged_via_command_line_by?(current_user)
|
||||||
#{link_to "merge this request manually", "#modal_merge_info", class: "how_to_merge_link vlink", "data-toggle" => "modal"}.
|
#{link_to "merge this request manually", "#modal_merge_info", class: "how_to_merge_link vlink", "data-toggle" => "modal"}.
|
||||||
- else
|
- else
|
||||||
ask someone with write access to this repository to merge this request manually.
|
ask someone with write access to this repository to merge this request manually.
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
.nav-controls
|
.nav-controls
|
||||||
- if can? current_user, :create_pipeline, @project
|
- if can? current_user, :create_pipeline, @project
|
||||||
= link_to new_namespace_project_pipeline_path(@project.namespace, @project), class: 'btn btn-create' do
|
= link_to new_namespace_project_pipeline_path(@project.namespace, @project), class: 'btn btn-create' do
|
||||||
New pipeline
|
Run pipeline
|
||||||
|
|
||||||
- unless @repository.gitlab_ci_yml
|
- unless @repository.gitlab_ci_yml
|
||||||
= link_to 'Get started with Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
|
= link_to 'Get started with Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
|
||||||
|
@ -45,13 +45,13 @@
|
||||||
.table-holder
|
.table-holder
|
||||||
%table.table.builds
|
%table.table.builds
|
||||||
%tbody
|
%tbody
|
||||||
%th ID
|
%th Status
|
||||||
%th Commit
|
%th Commit
|
||||||
- stages.each do |stage|
|
- stages.each do |stage|
|
||||||
%th.stage
|
%th.stage
|
||||||
%span.has-tooltip{ title: "#{stage.titleize}" }
|
%span.has-tooltip{ title: "#{stage.titleize}" }
|
||||||
= stage.titleize
|
= stage.titleize
|
||||||
%th Duration
|
%th
|
||||||
%th
|
%th
|
||||||
= render @pipelines, commit_sha: true, stage: true, allow_retry: true, stages: stages
|
= render @pipelines, commit_sha: true, stage: true, allow_retry: true, stages: stages
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
.table-responsive
|
.table-responsive
|
||||||
%table.table.protected-branches-list
|
%table.table.protected-branches-list
|
||||||
%colgroup
|
%colgroup
|
||||||
|
%col{ width: "20%" }
|
||||||
%col{ width: "30%" }
|
%col{ width: "30%" }
|
||||||
%col{ width: "25%" }
|
%col{ width: "25%" }
|
||||||
%col{ width: "25%" }
|
%col{ width: "25%" }
|
||||||
|
@ -18,6 +19,7 @@
|
||||||
%th Protected Branch
|
%th Protected Branch
|
||||||
%th Commit
|
%th Commit
|
||||||
%th Developers Can Push
|
%th Developers Can Push
|
||||||
|
%th Developers Can Merge
|
||||||
- if can_admin_project
|
- if can_admin_project
|
||||||
%th
|
%th
|
||||||
%tbody
|
%tbody
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
(branch was removed from repository)
|
(branch was removed from repository)
|
||||||
%td
|
%td
|
||||||
= check_box_tag("developers_can_push", protected_branch.id, protected_branch.developers_can_push, data: { url: url })
|
= check_box_tag("developers_can_push", protected_branch.id, protected_branch.developers_can_push, data: { url: url })
|
||||||
|
%td
|
||||||
|
= check_box_tag("developers_can_merge", protected_branch.id, protected_branch.developers_can_merge, data: { url: url })
|
||||||
- if can_admin_project
|
- if can_admin_project
|
||||||
%td
|
%td
|
||||||
= link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning btn-sm pull-right"
|
= link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning btn-sm pull-right"
|
||||||
|
|
|
@ -36,6 +36,14 @@
|
||||||
= f.label :developers_can_push, "Developers can push", class: "label-light append-bottom-0"
|
= f.label :developers_can_push, "Developers can push", class: "label-light append-bottom-0"
|
||||||
%p.light.append-bottom-0
|
%p.light.append-bottom-0
|
||||||
Allow developers to push to this branch
|
Allow developers to push to this branch
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
= f.check_box :developers_can_merge, class: "pull-left"
|
||||||
|
.prepend-left-20
|
||||||
|
= f.label :developers_can_merge, "Developers can merge", class: "label-light append-bottom-0"
|
||||||
|
%p.light.append-bottom-0
|
||||||
|
Allow developers to accept merge requests to this branch
|
||||||
= f.submit "Protect", class: "btn-create btn protect-branch-btn", disabled: true
|
= f.submit "Protect", class: "btn-create btn protect-branch-btn", disabled: true
|
||||||
|
|
||||||
%hr
|
%hr
|
||||||
= render "branches_list"
|
= render "branches_list"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
- page_title "Snippets"
|
- page_title "Snippets"
|
||||||
|
|
||||||
.row-content-block.top-block
|
.sub-header-block
|
||||||
.pull-right
|
.pull-right
|
||||||
- if can?(current_user, :create_project_snippet, @project)
|
- if can?(current_user, :create_project_snippet, @project)
|
||||||
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do
|
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
|
||||||
|
<path fill="#8F8F8F" fill-rule="evenodd" d="M28.7769836,18 C27.8675252,13.9920226 24.2831748,11 20,11 C15.7168252,11 12.1324748,13.9920226 11.2230164,18 L4.0085302,18 C2.90195036,18 2,18.8954305 2,20 C2,21.1122704 2.8992496,22 4.0085302,22 L11.2230164,22 C12.1324748,26.0079774 15.7168252,29 20,29 C24.2831748,29 27.8675252,26.0079774 28.7769836,22 L35.9914698,22 C37.0980496,22 38,21.1045695 38,20 C38,18.8877296 37.1007504,18 35.9914698,18 L28.7769836,18 L28.7769836,18 Z M20,25 C22.7614237,25 25,22.7614237 25,20 C25,17.2385763 22.7614237,15 20,15 C17.2385763,15 15,17.2385763 15,20 C15,22.7614237 17.2385763,25 20,25 L20,25 Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 726 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><g fill="#8F8F8F" fill-rule="evenodd"><path d="M29.513 10.134A15.922 15.922 0 0 0 23 7.28V6h2.993C26.55 6 27 5.552 27 5V2a1 1 0 0 0-1.007-1H14.007C13.45 1 13 1.448 13 2v3a1 1 0 0 0 1.007 1H17v1.28C9.597 8.686 4 15.19 4 23c0 8.837 7.163 16 16 16s16-7.163 16-16c0-3.461-1.099-6.665-2.967-9.283l1.327-1.58a2.498 2.498 0 0 0-.303-3.53 2.499 2.499 0 0 0-3.528.315l-1.016 1.212zM20 34c6.075 0 11-4.925 11-11s-4.925-11-11-11S9 16.925 9 23s4.925 11 11 11z"/><path d="M19 21h-4.002c-.552 0-.998.452-.998 1.01v1.98c0 .567.447 1.01.998 1.01h7.004c.274 0 .521-.111.701-.291a.979.979 0 0 0 .297-.704v-8.01c0-.54-.452-.995-1.01-.995h-1.98a.997.997 0 0 0-1.01.995V21z"/></g></svg>
|
After Width: | Height: | Size: 748 B |
|
@ -1,5 +1,12 @@
|
||||||
= form_errors(issuable)
|
= form_errors(issuable)
|
||||||
|
|
||||||
|
- if @conflict
|
||||||
|
.alert.alert-danger
|
||||||
|
Someone edited the #{issuable.class.model_name.human.downcase} the same time you did.
|
||||||
|
Please check out
|
||||||
|
= link_to "the #{issuable.class.model_name.human.downcase}", polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), target: "_blank"
|
||||||
|
and make sure your changes will not unintentionally remove theirs
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
= f.label :title, class: 'control-label'
|
= f.label :title, class: 'control-label'
|
||||||
.col-sm-10
|
.col-sm-10
|
||||||
|
@ -149,3 +156,5 @@
|
||||||
= link_to 'Delete', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), data: { confirm: "#{issuable.class.name.titleize} will be removed! Are you sure?" },
|
= link_to 'Delete', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), data: { confirm: "#{issuable.class.name.titleize} will be removed! Are you sure?" },
|
||||||
method: :delete, class: 'btn btn-danger btn-grouped'
|
method: :delete, class: 'btn btn-danger btn-grouped'
|
||||||
= link_to 'Cancel', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), class: 'btn btn-grouped btn-cancel'
|
= link_to 'Cancel', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), class: 'btn btn-grouped btn-cancel'
|
||||||
|
|
||||||
|
= f.hidden_field :lock_version
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
%span.pull-right.tab-issues-buttons
|
%span.pull-right.tab-issues-buttons
|
||||||
- if project && can?(current_user, :create_issue, project)
|
- if project && can?(current_user, :create_issue, project)
|
||||||
= link_to new_namespace_project_issue_path(project.namespace, project, issue: { milestone_id: milestone.id }), class: "btn btn-grouped", title: "New Issue" do
|
= link_to new_namespace_project_issue_path(project.namespace, project, issue: { milestone_id: milestone.id }), class: "btn btn-grouped", title: "New Issue" do
|
||||||
%i.fa.fa-plus
|
|
||||||
New Issue
|
New Issue
|
||||||
= link_to 'Browse Issues', milestones_browse_issuables_path(milestone, type: :issues), class: "btn btn-grouped"
|
= link_to 'Browse Issues', milestones_browse_issuables_path(milestone, type: :issues), class: "btn btn-grouped"
|
||||||
%span.pull-right.tab-merge-requests-buttons.hidden
|
%span.pull-right.tab-merge-requests-buttons.hidden
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
class GitGarbageCollectWorker
|
||||||
|
include Sidekiq::Worker
|
||||||
|
include Gitlab::ShellAdapter
|
||||||
|
|
||||||
|
sidekiq_options queue: :gitlab_shell, retry: false
|
||||||
|
|
||||||
|
def perform(project_id)
|
||||||
|
project = Project.find(project_id)
|
||||||
|
|
||||||
|
gitlab_shell.gc(project.repository_storage_path, project.path_with_namespace)
|
||||||
|
# Expire the branch cache in case garbage collection caused a ref lookup to fail
|
||||||
|
project.repository.after_create_branch
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,10 +0,0 @@
|
||||||
class GitlabShellOneShotWorker
|
|
||||||
include Sidekiq::Worker
|
|
||||||
include Gitlab::ShellAdapter
|
|
||||||
|
|
||||||
sidekiq_options queue: :gitlab_shell, retry: false
|
|
||||||
|
|
||||||
def perform(action, *arg)
|
|
||||||
gitlab_shell.send(action, *arg)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,87 +0,0 @@
|
||||||
|
|
||||||
production: &base
|
|
||||||
gitlab:
|
|
||||||
host: localhost
|
|
||||||
port: 80
|
|
||||||
https: false
|
|
||||||
|
|
||||||
user: root
|
|
||||||
|
|
||||||
email_from: example@example.com
|
|
||||||
|
|
||||||
support_email: support@example.com
|
|
||||||
|
|
||||||
default_projects_features:
|
|
||||||
issues: true
|
|
||||||
merge_requests: true
|
|
||||||
wiki: true
|
|
||||||
snippets: false
|
|
||||||
visibility_level: "private" # can be "private" | "internal" | "public"
|
|
||||||
|
|
||||||
issues_tracker:
|
|
||||||
|
|
||||||
gravatar:
|
|
||||||
enabled: true # Use user avatar image from Gravatar.com (default: true)
|
|
||||||
|
|
||||||
ldap:
|
|
||||||
enabled: false
|
|
||||||
host: '_your_ldap_server'
|
|
||||||
port: 636
|
|
||||||
uid: 'sAMAccountName'
|
|
||||||
method: 'ssl' # "tls" or "ssl" or "plain"
|
|
||||||
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
|
|
||||||
password: '_the_password_of_the_bind_user'
|
|
||||||
allow_username_or_email_login: true
|
|
||||||
|
|
||||||
base: ''
|
|
||||||
|
|
||||||
user_filter: ''
|
|
||||||
|
|
||||||
omniauth:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
satellites:
|
|
||||||
# Relative paths are relative to Rails.root (default: tmp/repo_satellites/)
|
|
||||||
path: /apps/gitlab-satellites/
|
|
||||||
|
|
||||||
backup:
|
|
||||||
path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/)
|
|
||||||
|
|
||||||
repositories:
|
|
||||||
storages: # REPO PATHS MUST NOT BE A SYMLINK!!!
|
|
||||||
default: /apps/repositories/
|
|
||||||
|
|
||||||
gitlab_shell:
|
|
||||||
path: /apps/gitlab-shell/
|
|
||||||
|
|
||||||
hooks_path: /apps/gitlab-shell/hooks/
|
|
||||||
|
|
||||||
upload_pack: true
|
|
||||||
receive_pack: true
|
|
||||||
|
|
||||||
git:
|
|
||||||
bin_path: /usr/bin/git
|
|
||||||
max_size: 5242880 # 5.megabytes
|
|
||||||
timeout: 10
|
|
||||||
|
|
||||||
extra:
|
|
||||||
|
|
||||||
development:
|
|
||||||
<<: *base
|
|
||||||
|
|
||||||
test:
|
|
||||||
<<: *base
|
|
||||||
gravatar:
|
|
||||||
enabled: true
|
|
||||||
gitlab:
|
|
||||||
host: localhost
|
|
||||||
port: 80
|
|
||||||
issues_tracker:
|
|
||||||
redmine:
|
|
||||||
title: "Redmine"
|
|
||||||
project_url: "http://redmine/projects/:issues_tracker_id"
|
|
||||||
issues_url: "http://redmine/:project_id/:issues_tracker_id/:id"
|
|
||||||
new_issue_url: "http://redmine/projects/:issues_tracker_id/issues/new"
|
|
||||||
|
|
||||||
staging:
|
|
||||||
<<: *base
|
|
|
@ -1,16 +1,3 @@
|
||||||
# Email forcibly included in the standard checks, but the email health check
|
|
||||||
# doesn't support the full range of SMTP options, which can result in failures
|
|
||||||
# for valid SMTP configurations.
|
|
||||||
# Overwrite the HealthCheck's detection of whether email is configured
|
|
||||||
# in order to avoid the email check during standard checks
|
|
||||||
module HealthCheck
|
|
||||||
class Utils
|
|
||||||
def self.mailer_configured?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
HealthCheck.setup do |config|
|
HealthCheck.setup do |config|
|
||||||
config.standard_checks = ['database', 'migrations', 'cache']
|
config.standard_checks = ['database', 'migrations', 'cache']
|
||||||
config.full_checks = ['database', 'migrations', 'cache']
|
config.full_checks = ['database', 'migrations', 'cache']
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
class AddDevelopersCanMergeToProtectedBranches < ActiveRecord::Migration
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def change
|
||||||
|
add_column_with_default :protected_branches, :developers_can_merge, :boolean, default: false, allow_null: false
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,8 @@
|
||||||
|
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||||
|
# for more information on how to write migrations for GitLab.
|
||||||
|
|
||||||
|
class AddColumnInProgressMergeCommitShaToMergeRequests < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
add_column :merge_requests, :in_progress_merge_commit_sha, :string
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,17 @@
|
||||||
|
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||||
|
# for more information on how to write migrations for GitLab.
|
||||||
|
|
||||||
|
class AddLockToIssuables < ActiveRecord::Migration
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
add_column_with_default :issues, :lock_version, :integer, default: 0
|
||||||
|
add_column_with_default :merge_requests, :lock_version, :integer, default: 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_column :issues, :lock_version
|
||||||
|
remove_column :merge_requests, :lock_version
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,21 @@
|
||||||
|
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||||
|
# for more information on how to write migrations for GitLab.
|
||||||
|
|
||||||
|
class RemoveAwardEmojisWithNoUser < ActiveRecord::Migration
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
# When using the methods "add_concurrent_index" or "add_column_with_default"
|
||||||
|
# you must disable the use of transactions as these methods can not run in an
|
||||||
|
# existing transaction. When using "add_concurrent_index" make sure that this
|
||||||
|
# method is the _only_ method called in the migration, any other changes
|
||||||
|
# should go in a separate migration. This ensures that upon failure _only_ the
|
||||||
|
# index creation fails and can be retried or reverted easily.
|
||||||
|
#
|
||||||
|
# To disable transactions uncomment the following line and remove these
|
||||||
|
# comments:
|
||||||
|
# disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
AwardEmoji.joins('LEFT JOIN users ON users.id = user_id').where('users.id IS NULL').destroy_all
|
||||||
|
end
|
||||||
|
end
|
32
db/schema.rb
32
db/schema.rb
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20160705163108) do
|
ActiveRecord::Schema.define(version: 20160712171823) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -70,11 +70,11 @@ ActiveRecord::Schema.define(version: 20160705163108) do
|
||||||
t.string "recaptcha_site_key"
|
t.string "recaptcha_site_key"
|
||||||
t.string "recaptcha_private_key"
|
t.string "recaptcha_private_key"
|
||||||
t.integer "metrics_port", default: 8089
|
t.integer "metrics_port", default: 8089
|
||||||
t.boolean "akismet_enabled", default: false
|
|
||||||
t.string "akismet_api_key"
|
|
||||||
t.integer "metrics_sample_interval", default: 15
|
t.integer "metrics_sample_interval", default: 15
|
||||||
t.boolean "sentry_enabled", default: false
|
t.boolean "sentry_enabled", default: false
|
||||||
t.string "sentry_dsn"
|
t.string "sentry_dsn"
|
||||||
|
t.boolean "akismet_enabled", default: false
|
||||||
|
t.string "akismet_api_key"
|
||||||
t.boolean "email_author_in_body", default: false
|
t.boolean "email_author_in_body", default: false
|
||||||
t.integer "default_group_visibility"
|
t.integer "default_group_visibility"
|
||||||
t.boolean "repository_checks_enabled", default: false
|
t.boolean "repository_checks_enabled", default: false
|
||||||
|
@ -84,10 +84,10 @@ ActiveRecord::Schema.define(version: 20160705163108) do
|
||||||
t.string "health_check_access_token"
|
t.string "health_check_access_token"
|
||||||
t.boolean "send_user_confirmation_email", default: false
|
t.boolean "send_user_confirmation_email", default: false
|
||||||
t.integer "container_registry_token_expire_delay", default: 5
|
t.integer "container_registry_token_expire_delay", default: 5
|
||||||
t.boolean "user_default_external", default: false, null: false
|
|
||||||
t.text "after_sign_up_text"
|
t.text "after_sign_up_text"
|
||||||
t.string "repository_storage", default: "default"
|
t.string "repository_storage", default: "default"
|
||||||
t.string "enabled_git_access_protocol"
|
t.string "enabled_git_access_protocol"
|
||||||
|
t.boolean "user_default_external", default: false, null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "audit_events", force: :cascade do |t|
|
create_table "audit_events", force: :cascade do |t|
|
||||||
|
@ -165,8 +165,8 @@ ActiveRecord::Schema.define(version: 20160705163108) do
|
||||||
t.text "artifacts_metadata"
|
t.text "artifacts_metadata"
|
||||||
t.integer "erased_by_id"
|
t.integer "erased_by_id"
|
||||||
t.datetime "erased_at"
|
t.datetime "erased_at"
|
||||||
t.string "environment"
|
|
||||||
t.datetime "artifacts_expire_at"
|
t.datetime "artifacts_expire_at"
|
||||||
|
t.string "environment"
|
||||||
t.integer "artifacts_size"
|
t.integer "artifacts_size"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -481,10 +481,11 @@ ActiveRecord::Schema.define(version: 20160705163108) do
|
||||||
t.string "state"
|
t.string "state"
|
||||||
t.integer "iid"
|
t.integer "iid"
|
||||||
t.integer "updated_by_id"
|
t.integer "updated_by_id"
|
||||||
|
t.integer "moved_to_id"
|
||||||
t.boolean "confidential", default: false
|
t.boolean "confidential", default: false
|
||||||
t.datetime "deleted_at"
|
t.datetime "deleted_at"
|
||||||
t.date "due_date"
|
t.date "due_date"
|
||||||
t.integer "moved_to_id"
|
t.integer "lock_version", default: 0, null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree
|
add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree
|
||||||
|
@ -624,6 +625,8 @@ ActiveRecord::Schema.define(version: 20160705163108) do
|
||||||
t.integer "merge_user_id"
|
t.integer "merge_user_id"
|
||||||
t.string "merge_commit_sha"
|
t.string "merge_commit_sha"
|
||||||
t.datetime "deleted_at"
|
t.datetime "deleted_at"
|
||||||
|
t.integer "lock_version", default: 0, null: false
|
||||||
|
t.string "in_progress_merge_commit_sha"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
|
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
|
||||||
|
@ -773,10 +776,10 @@ ActiveRecord::Schema.define(version: 20160705163108) do
|
||||||
t.integer "user_id", null: false
|
t.integer "user_id", null: false
|
||||||
t.string "token", null: false
|
t.string "token", null: false
|
||||||
t.string "name", null: false
|
t.string "name", null: false
|
||||||
t.datetime "created_at", null: false
|
|
||||||
t.datetime "updated_at", null: false
|
|
||||||
t.boolean "revoked", default: false
|
t.boolean "revoked", default: false
|
||||||
t.datetime "expires_at"
|
t.datetime "expires_at"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "personal_access_tokens", ["token"], name: "index_personal_access_tokens_on_token", unique: true, using: :btree
|
add_index "personal_access_tokens", ["token"], name: "index_personal_access_tokens_on_token", unique: true, using: :btree
|
||||||
|
@ -858,11 +861,12 @@ ActiveRecord::Schema.define(version: 20160705163108) do
|
||||||
add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree
|
add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree
|
||||||
|
|
||||||
create_table "protected_branches", force: :cascade do |t|
|
create_table "protected_branches", force: :cascade do |t|
|
||||||
t.integer "project_id", null: false
|
t.integer "project_id", null: false
|
||||||
t.string "name", null: false
|
t.string "name", null: false
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.boolean "developers_can_push", default: false, null: false
|
t.boolean "developers_can_push", default: false, null: false
|
||||||
|
t.boolean "developers_can_merge", default: false, null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree
|
add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree
|
||||||
|
@ -896,9 +900,9 @@ ActiveRecord::Schema.define(version: 20160705163108) do
|
||||||
t.string "type"
|
t.string "type"
|
||||||
t.string "title"
|
t.string "title"
|
||||||
t.integer "project_id"
|
t.integer "project_id"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at", null: false
|
||||||
t.boolean "active", default: false, null: false
|
t.boolean "active", null: false
|
||||||
t.text "properties"
|
t.text "properties"
|
||||||
t.boolean "template", default: false
|
t.boolean "template", default: false
|
||||||
t.boolean "push_events", default: true
|
t.boolean "push_events", default: true
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
- [Importing and exporting projects between instances](user/project/settings/import_export.md).
|
- [Importing and exporting projects between instances](user/project/settings/import_export.md).
|
||||||
- [Markdown](markdown/markdown.md) GitLab's advanced formatting system.
|
- [Markdown](markdown/markdown.md) GitLab's advanced formatting system.
|
||||||
- [Migrating from SVN](workflow/importing/migrating_from_svn.md) Convert a SVN repository to Git and GitLab.
|
- [Migrating from SVN](workflow/importing/migrating_from_svn.md) Convert a SVN repository to Git and GitLab.
|
||||||
- [Permissions](permissions/permissions.md) Learn what each role in a project (external/guest/reporter/developer/master/owner) can do.
|
- [Permissions](user/permissions.md) Learn what each role in a project (external/guest/reporter/developer/master/owner) can do.
|
||||||
- [Profile Settings](profile/README.md)
|
- [Profile Settings](profile/README.md)
|
||||||
- [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
|
- [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
|
||||||
- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
|
- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
|
||||||
|
|
|
@ -68,7 +68,9 @@ Parameters:
|
||||||
"merge_when_build_succeeds": true,
|
"merge_when_build_succeeds": true,
|
||||||
"merge_status": "can_be_merged",
|
"merge_status": "can_be_merged",
|
||||||
"subscribed" : false,
|
"subscribed" : false,
|
||||||
"user_notes_count": 1
|
"user_notes_count": 1,
|
||||||
|
"should_remove_source_branch": true,
|
||||||
|
"force_remove_source_branch": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
@ -132,7 +134,9 @@ Parameters:
|
||||||
"merge_when_build_succeeds": true,
|
"merge_when_build_succeeds": true,
|
||||||
"merge_status": "can_be_merged",
|
"merge_status": "can_be_merged",
|
||||||
"subscribed" : true,
|
"subscribed" : true,
|
||||||
"user_notes_count": 1
|
"user_notes_count": 1,
|
||||||
|
"should_remove_source_branch": true,
|
||||||
|
"force_remove_source_branch": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -233,6 +237,8 @@ Parameters:
|
||||||
"merge_status": "can_be_merged",
|
"merge_status": "can_be_merged",
|
||||||
"subscribed" : true,
|
"subscribed" : true,
|
||||||
"user_notes_count": 1,
|
"user_notes_count": 1,
|
||||||
|
"should_remove_source_branch": true,
|
||||||
|
"force_remove_source_branch": false,
|
||||||
"changes": [
|
"changes": [
|
||||||
{
|
{
|
||||||
"old_path": "VERSION",
|
"old_path": "VERSION",
|
||||||
|
@ -312,7 +318,9 @@ Parameters:
|
||||||
"merge_when_build_succeeds": true,
|
"merge_when_build_succeeds": true,
|
||||||
"merge_status": "can_be_merged",
|
"merge_status": "can_be_merged",
|
||||||
"subscribed" : true,
|
"subscribed" : true,
|
||||||
"user_notes_count": 0
|
"user_notes_count": 0,
|
||||||
|
"should_remove_source_branch": true,
|
||||||
|
"force_remove_source_branch": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -383,7 +391,9 @@ Parameters:
|
||||||
"merge_when_build_succeeds": true,
|
"merge_when_build_succeeds": true,
|
||||||
"merge_status": "can_be_merged",
|
"merge_status": "can_be_merged",
|
||||||
"subscribed" : true,
|
"subscribed" : true,
|
||||||
"user_notes_count": 1
|
"user_notes_count": 1,
|
||||||
|
"should_remove_source_branch": true,
|
||||||
|
"force_remove_source_branch": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -481,7 +491,9 @@ Parameters:
|
||||||
"merge_when_build_succeeds": true,
|
"merge_when_build_succeeds": true,
|
||||||
"merge_status": "can_be_merged",
|
"merge_status": "can_be_merged",
|
||||||
"subscribed" : true,
|
"subscribed" : true,
|
||||||
"user_notes_count": 1
|
"user_notes_count": 1,
|
||||||
|
"should_remove_source_branch": true,
|
||||||
|
"force_remove_source_branch": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -547,7 +559,9 @@ Parameters:
|
||||||
"merge_when_build_succeeds": true,
|
"merge_when_build_succeeds": true,
|
||||||
"merge_status": "can_be_merged",
|
"merge_status": "can_be_merged",
|
||||||
"subscribed" : true,
|
"subscribed" : true,
|
||||||
"user_notes_count": 1
|
"user_notes_count": 1,
|
||||||
|
"should_remove_source_branch": true,
|
||||||
|
"force_remove_source_branch": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -866,7 +880,9 @@ Example response:
|
||||||
"merge_when_build_succeeds": false,
|
"merge_when_build_succeeds": false,
|
||||||
"merge_status": "unchecked",
|
"merge_status": "unchecked",
|
||||||
"subscribed": true,
|
"subscribed": true,
|
||||||
"user_notes_count": 7
|
"user_notes_count": 7,
|
||||||
|
"should_remove_source_branch": true,
|
||||||
|
"force_remove_source_branch": false
|
||||||
},
|
},
|
||||||
"target_url": "https://gitlab.example.com/gitlab-org/gitlab-ci/merge_requests/7",
|
"target_url": "https://gitlab.example.com/gitlab-org/gitlab-ci/merge_requests/7",
|
||||||
"body": "Et voluptas laudantium minus nihil recusandae ut accusamus earum aut non.",
|
"body": "Et voluptas laudantium minus nihil recusandae ut accusamus earum aut non.",
|
||||||
|
|
|
@ -15,7 +15,7 @@ Parameters:
|
||||||
|
|
||||||
| Attribute | Type | Required | Description |
|
| Attribute | Type | Required | Description |
|
||||||
| --------- | ---- | -------- | ----------- |
|
| --------- | ---- | -------- | ----------- |
|
||||||
| `action` | string | no | The action to be filtered. Can be `assigned`, `mentioned`, `build_failed`, or `marked`. |
|
| `action` | string | no | The action to be filtered. Can be `assigned`, `mentioned`, `build_failed`, `marked`, or `approval_required`. |
|
||||||
| `author_id` | integer | no | The ID of an author |
|
| `author_id` | integer | no | The ID of an author |
|
||||||
| `project_id` | integer | no | The ID of a project |
|
| `project_id` | integer | no | The ID of a project |
|
||||||
| `state` | string | no | The state of the todo. Can be either `pending` or `done` |
|
| `state` | string | no | The state of the todo. Can be either `pending` or `done` |
|
||||||
|
|
|
@ -15,6 +15,6 @@
|
||||||
- [Use SSH keys in your build environment](ssh_keys/README.md)
|
- [Use SSH keys in your build environment](ssh_keys/README.md)
|
||||||
- [Trigger builds through the API](triggers/README.md)
|
- [Trigger builds through the API](triggers/README.md)
|
||||||
- [Build artifacts](build_artifacts/README.md)
|
- [Build artifacts](build_artifacts/README.md)
|
||||||
- [User permissions](permissions/README.md)
|
- [User permissions](../user/permissions.md#gitlab-ci)
|
||||||
- [API](../api/ci/README.md)
|
- [API](../api/ci/README.md)
|
||||||
- [CI services (linked docker containers)](services/README.md)
|
- [CI services (linked docker containers)](services/README.md)
|
||||||
|
|
|
@ -1,24 +1,3 @@
|
||||||
# Users Permissions
|
# Users Permissions
|
||||||
|
|
||||||
GitLab CI relies on user's role on the GitLab. There are three permissions levels on GitLab CI: admin, master, developer, other.
|
This document was moved to [user/permissions.md](../../user/permissions.md#gitlab-ci).
|
||||||
|
|
||||||
Admin user can perform any actions on GitLab CI in scope of instance and project. Also user with admin permission can use admin interface.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
| Action | Guest, Reporter | Developer | Master | Admin |
|
|
||||||
|---------------------------------------|-----------------|-------------|----------|--------|
|
|
||||||
| See commits and builds | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| Retry or cancel build | | ✓ | ✓ | ✓ |
|
|
||||||
| Remove project | | | ✓ | ✓ |
|
|
||||||
| Create project | | | ✓ | ✓ |
|
|
||||||
| Change project configuration | | | ✓ | ✓ |
|
|
||||||
| Add specific runners | | | ✓ | ✓ |
|
|
||||||
| Add shared runners | | | | ✓ |
|
|
||||||
| See events in the system | | | | ✓ |
|
|
||||||
| Admin interface | | | | ✓ |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,8 @@ We try to keep the amount of tabs in the header navigation between 5 and 10 so t
|
||||||
tab should represent separate functionality. Everything related to the issue
|
tab should represent separate functionality. Everything related to the issue
|
||||||
tracker should be under the 'Issues' tab while everything related to the wiki should
|
tracker should be under the 'Issues' tab while everything related to the wiki should
|
||||||
be under 'Wiki' tab and so on and so forth.
|
be under 'Wiki' tab and so on and so forth.
|
||||||
|
When adding a new tab to the header don't use more than 2 words for text in the link.
|
||||||
|
We want to keep links short and easy to remember and fit all of them in the small screen.
|
||||||
|
|
||||||
## Mobile screen size
|
## Mobile screen size
|
||||||
|
|
||||||
|
|
|
@ -138,7 +138,7 @@ This setting is only available on GitLab 8.7 and above.
|
||||||
|
|
||||||
SAML login includes support for external groups. You can define in the SAML
|
SAML login includes support for external groups. You can define in the SAML
|
||||||
settings which groups, to which your users belong in your IdP, you wish to be
|
settings which groups, to which your users belong in your IdP, you wish to be
|
||||||
marked as [external](../permissions/permissions.md).
|
marked as [external](../user/permissions.md).
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
|
@ -306,4 +306,4 @@ For this you need take the following into account:
|
||||||
validators are optional
|
validators are optional
|
||||||
|
|
||||||
Make sure that one of the above described scenarios is valid, or the requests will
|
Make sure that one of the above described scenarios is valid, or the requests will
|
||||||
fail with one of the mentioned errors.
|
fail with one of the mentioned errors.
|
||||||
|
|
|
@ -1,104 +1,3 @@
|
||||||
# Permissions
|
# Permissions
|
||||||
|
|
||||||
Users have different abilities depending on the access level they have in a particular group or project.
|
This document was moved to [user/permissions.md](../user/permissions.md).
|
||||||
|
|
||||||
If a user is both in a project group and in the project itself, the highest permission level is used.
|
|
||||||
|
|
||||||
If a user is a GitLab administrator they receive all permissions.
|
|
||||||
|
|
||||||
On public and internal projects the Guest role is not enforced.
|
|
||||||
All users will be able to create issues, leave comments, and pull or download the project code.
|
|
||||||
|
|
||||||
To add or import a user, you can follow the [project users and members
|
|
||||||
documentation](../workflow/add-user/add-user.md).
|
|
||||||
|
|
||||||
## Project
|
|
||||||
|
|
||||||
| Action | Guest | Reporter | Developer | Master | Owner |
|
|
||||||
|---------------------------------------|---------|------------|-------------|----------|--------|
|
|
||||||
| Create new issue | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| Leave comments | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| See a list of builds | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| See a build log | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| Download and browse build artifacts | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| Pull project code | | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| Download project | | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| Create code snippets | | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| Manage issue tracker | | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| Manage labels | | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| See a commit status | | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| See a container registry | | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| See environments | | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| Manage merge requests | | | ✓ | ✓ | ✓ |
|
|
||||||
| Create new merge request | | | ✓ | ✓ | ✓ |
|
|
||||||
| Create new branches | | | ✓ | ✓ | ✓ |
|
|
||||||
| Push to non-protected branches | | | ✓ | ✓ | ✓ |
|
|
||||||
| Force push to non-protected branches | | | ✓ | ✓ | ✓ |
|
|
||||||
| Remove non-protected branches | | | ✓ | ✓ | ✓ |
|
|
||||||
| Add tags | | | ✓ | ✓ | ✓ |
|
|
||||||
| Write a wiki | | | ✓ | ✓ | ✓ |
|
|
||||||
| Cancel and retry builds | | | ✓ | ✓ | ✓ |
|
|
||||||
| Create or update commit status | | | ✓ | ✓ | ✓ |
|
|
||||||
| Update a container registry | | | ✓ | ✓ | ✓ |
|
|
||||||
| Remove a container registry image | | | ✓ | ✓ | ✓ |
|
|
||||||
| Create new environments | | | ✓ | ✓ | ✓ |
|
|
||||||
| Create new milestones | | | | ✓ | ✓ |
|
|
||||||
| Add new team members | | | | ✓ | ✓ |
|
|
||||||
| Push to protected branches | | | | ✓ | ✓ |
|
|
||||||
| Enable/disable branch protection | | | | ✓ | ✓ |
|
|
||||||
| Turn on/off prot. branch push for devs| | | | ✓ | ✓ |
|
|
||||||
| Rewrite/remove git tags | | | | ✓ | ✓ |
|
|
||||||
| Edit project | | | | ✓ | ✓ |
|
|
||||||
| Add deploy keys to project | | | | ✓ | ✓ |
|
|
||||||
| Configure project hooks | | | | ✓ | ✓ |
|
|
||||||
| Manage runners | | | | ✓ | ✓ |
|
|
||||||
| Manage build triggers | | | | ✓ | ✓ |
|
|
||||||
| Manage variables | | | | ✓ | ✓ |
|
|
||||||
| Delete environments | | | | ✓ | ✓ |
|
|
||||||
| Switch visibility level | | | | | ✓ |
|
|
||||||
| Transfer project to another namespace | | | | | ✓ |
|
|
||||||
| Remove project | | | | | ✓ |
|
|
||||||
| Force push to protected branches [^2] | | | | | |
|
|
||||||
| Remove protected branches [^2] | | | | | |
|
|
||||||
|
|
||||||
[^1]: If **Allow guest to access builds** is enabled in CI settings
|
|
||||||
[^2]: Not allowed for Guest, Reporter, Developer, Master, or Owner
|
|
||||||
|
|
||||||
## Group
|
|
||||||
|
|
||||||
In order for a group to appear as public and be browsable, it must contain at
|
|
||||||
least one public project.
|
|
||||||
|
|
||||||
Any user can remove themselves from a group, unless they are the last Owner of the group.
|
|
||||||
|
|
||||||
| Action | Guest | Reporter | Developer | Master | Owner |
|
|
||||||
|-------------------------|-------|----------|-----------|--------|-------|
|
|
||||||
| Browse group | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
||||||
| Edit group | | | | | ✓ |
|
|
||||||
| Create project in group | | | | ✓ | ✓ |
|
|
||||||
| Manage group members | | | | | ✓ |
|
|
||||||
| Remove group | | | | | ✓ |
|
|
||||||
|
|
||||||
## External Users
|
|
||||||
|
|
||||||
In cases where it is desired that a user has access only to some internal or
|
|
||||||
private projects, there is the option of creating **External Users**. This
|
|
||||||
feature may be useful when for example a contractor is working on a given
|
|
||||||
project and should only have access to that project.
|
|
||||||
|
|
||||||
External users can only access projects to which they are explicitly granted
|
|
||||||
access, thus hiding all other internal or private ones from them. Access can be
|
|
||||||
granted by adding the user as member to the project or group.
|
|
||||||
|
|
||||||
They will, like usual users, receive a role in the project or group with all
|
|
||||||
the abilities that are mentioned in the table above. They cannot however create
|
|
||||||
groups or projects, and they have the same access as logged out users in all
|
|
||||||
other cases.
|
|
||||||
|
|
||||||
An administrator can flag a user as external [through the API](../api/users.md)
|
|
||||||
or by checking the checkbox on the admin panel. As an administrator, navigate
|
|
||||||
to **Admin > Users** to create a new user or edit an existing one. There, you
|
|
||||||
will find the option to flag the user as external.
|
|
||||||
|
|
||||||
By default new users are not set as external users. This behavior can be changed
|
|
||||||
by an administrator under **Admin > Application Settings**.
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ Public projects can be cloned **without any** authentication.
|
||||||
|
|
||||||
They will also be listed on the public access directory (`/public`).
|
They will also be listed on the public access directory (`/public`).
|
||||||
|
|
||||||
**Any logged in user** will have [Guest](../permissions/permissions.md)
|
**Any logged in user** will have [Guest](../user/permissions.md)
|
||||||
permissions on the repository.
|
permissions on the repository.
|
||||||
|
|
||||||
### Internal projects
|
### Internal projects
|
||||||
|
@ -27,7 +27,7 @@ Internal projects can be cloned by any logged in user.
|
||||||
They will also be listed on the public access directory (`/public`) for logged
|
They will also be listed on the public access directory (`/public`) for logged
|
||||||
in users.
|
in users.
|
||||||
|
|
||||||
Any logged in user will have [Guest](../permissions/permissions.md) permissions
|
Any logged in user will have [Guest](../user/permissions.md) permissions
|
||||||
on the repository.
|
on the repository.
|
||||||
|
|
||||||
### How to change project visibility
|
### How to change project visibility
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
# Permissions
|
||||||
|
|
||||||
|
Users have different abilities depending on the access level they have in a
|
||||||
|
particular group or project. If a user is both in a group's project and the
|
||||||
|
project itself, the highest permission level is used.
|
||||||
|
|
||||||
|
On public and internal projects the Guest role is not enforced. All users will
|
||||||
|
be able to create issues, leave comments, and pull or download the project code.
|
||||||
|
|
||||||
|
GitLab administrators receive all permissions.
|
||||||
|
|
||||||
|
To add or import a user, you can follow the [project users and members
|
||||||
|
documentation](../workflow/add-user/add-user.md).
|
||||||
|
|
||||||
|
## Project
|
||||||
|
|
||||||
|
The following table depicts the various user permission levels in a project.
|
||||||
|
|
||||||
|
| Action | Guest | Reporter | Developer | Master | Owner |
|
||||||
|
|---------------------------------------|---------|------------|-------------|----------|--------|
|
||||||
|
| Create new issue | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Leave comments | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| See a list of builds | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| See a build log | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Download and browse build artifacts | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Pull project code | | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Download project | | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Create code snippets | | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Manage issue tracker | | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Manage labels | | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| See a commit status | | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| See a container registry | | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| See environments | | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Manage/Accept merge requests | | | ✓ | ✓ | ✓ |
|
||||||
|
| Create new merge request | | | ✓ | ✓ | ✓ |
|
||||||
|
| Create new branches | | | ✓ | ✓ | ✓ |
|
||||||
|
| Push to non-protected branches | | | ✓ | ✓ | ✓ |
|
||||||
|
| Force push to non-protected branches | | | ✓ | ✓ | ✓ |
|
||||||
|
| Remove non-protected branches | | | ✓ | ✓ | ✓ |
|
||||||
|
| Add tags | | | ✓ | ✓ | ✓ |
|
||||||
|
| Write a wiki | | | ✓ | ✓ | ✓ |
|
||||||
|
| Cancel and retry builds | | | ✓ | ✓ | ✓ |
|
||||||
|
| Create or update commit status | | | ✓ | ✓ | ✓ |
|
||||||
|
| Update a container registry | | | ✓ | ✓ | ✓ |
|
||||||
|
| Remove a container registry image | | | ✓ | ✓ | ✓ |
|
||||||
|
| Create new environments | | | ✓ | ✓ | ✓ |
|
||||||
|
| Create new milestones | | | | ✓ | ✓ |
|
||||||
|
| Add new team members | | | | ✓ | ✓ |
|
||||||
|
| Push to protected branches | | | | ✓ | ✓ |
|
||||||
|
| Enable/disable branch protection | | | | ✓ | ✓ |
|
||||||
|
| Turn on/off protected branch push for devs| | | | ✓ | ✓ |
|
||||||
|
| Rewrite/remove Git tags | | | | ✓ | ✓ |
|
||||||
|
| Edit project | | | | ✓ | ✓ |
|
||||||
|
| Add deploy keys to project | | | | ✓ | ✓ |
|
||||||
|
| Configure project hooks | | | | ✓ | ✓ |
|
||||||
|
| Manage runners | | | | ✓ | ✓ |
|
||||||
|
| Manage build triggers | | | | ✓ | ✓ |
|
||||||
|
| Manage variables | | | | ✓ | ✓ |
|
||||||
|
| Delete environments | | | | ✓ | ✓ |
|
||||||
|
| Switch visibility level | | | | | ✓ |
|
||||||
|
| Transfer project to another namespace | | | | | ✓ |
|
||||||
|
| Remove project | | | | | ✓ |
|
||||||
|
| Force push to protected branches [^2] | | | | | |
|
||||||
|
| Remove protected branches [^2] | | | | | |
|
||||||
|
|
||||||
|
[^1]: If **Allow guest to access builds** is enabled in CI settings
|
||||||
|
[^2]: Not allowed for Guest, Reporter, Developer, Master, or Owner
|
||||||
|
|
||||||
|
## Group
|
||||||
|
|
||||||
|
Any user can remove themselves from a group, unless they are the last Owner of
|
||||||
|
the group. The following table depicts the various user permission levels in a
|
||||||
|
group.
|
||||||
|
|
||||||
|
| Action | Guest | Reporter | Developer | Master | Owner |
|
||||||
|
|-------------------------|-------|----------|-----------|--------|-------|
|
||||||
|
| Browse group | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Edit group | | | | | ✓ |
|
||||||
|
| Create project in group | | | | ✓ | ✓ |
|
||||||
|
| Manage group members | | | | | ✓ |
|
||||||
|
| Remove group | | | | | ✓ |
|
||||||
|
|
||||||
|
## External Users
|
||||||
|
|
||||||
|
In cases where it is desired that a user has access only to some internal or
|
||||||
|
private projects, there is the option of creating **External Users**. This
|
||||||
|
feature may be useful when for example a contractor is working on a given
|
||||||
|
project and should only have access to that project.
|
||||||
|
|
||||||
|
External users can only access projects to which they are explicitly granted
|
||||||
|
access, thus hiding all other internal or private ones from them. Access can be
|
||||||
|
granted by adding the user as member to the project or group.
|
||||||
|
|
||||||
|
They will, like usual users, receive a role in the project or group with all
|
||||||
|
the abilities that are mentioned in the table above. They cannot however create
|
||||||
|
groups or projects, and they have the same access as logged out users in all
|
||||||
|
other cases.
|
||||||
|
|
||||||
|
An administrator can flag a user as external [through the API](../api/users.md)
|
||||||
|
or by checking the checkbox on the admin panel. As an administrator, navigate
|
||||||
|
to **Admin > Users** to create a new user or edit an existing one. There, you
|
||||||
|
will find the option to flag the user as external.
|
||||||
|
|
||||||
|
By default new users are not set as external users. This behavior can be changed
|
||||||
|
by an administrator under **Admin > Application Settings**.
|
||||||
|
|
||||||
|
## GitLab CI
|
||||||
|
|
||||||
|
GitLab CI permissions rely on the role the user has in GitLab. There are four
|
||||||
|
permission levels it total:
|
||||||
|
|
||||||
|
- admin
|
||||||
|
- master
|
||||||
|
- developer
|
||||||
|
- guest/reporter
|
||||||
|
|
||||||
|
The admin user can perform any action on GitLab CI in scope of the GitLab
|
||||||
|
instance and project. In addition, all admins can use the admin interface under
|
||||||
|
`/admin/runners`.
|
||||||
|
|
||||||
|
| Action | Guest, Reporter | Developer | Master | Admin |
|
||||||
|
|---------------------------------------|-----------------|-------------|----------|--------|
|
||||||
|
| See commits and builds | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| Retry or cancel build | | ✓ | ✓ | ✓ |
|
||||||
|
| Remove project | | | ✓ | ✓ |
|
||||||
|
| Create project | | | ✓ | ✓ |
|
||||||
|
| Change project configuration | | | ✓ | ✓ |
|
||||||
|
| Add specific runners | | | ✓ | ✓ |
|
||||||
|
| Add shared runners | | | | ✓ |
|
||||||
|
| See events in the system | | | | ✓ |
|
||||||
|
| Admin interface | | | | ✓ |
|
|
@ -23,7 +23,7 @@ want to add.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Select the user and the [permission level](../../permissions/permissions.md)
|
Select the user and the [permission level](../../user/permissions.md)
|
||||||
that you'd like to give the user. Note that you can select more than one user.
|
that you'd like to give the user. Note that you can select more than one user.
|
||||||
|
|
||||||
![Give user permissions](img/add_user_give_permissions.png)
|
![Give user permissions](img/add_user_give_permissions.png)
|
||||||
|
|
|
@ -38,7 +38,7 @@ Forking a project is in most cases a two-step process.
|
||||||
---
|
---
|
||||||
|
|
||||||
After the forking is done, you can start working on the newly created
|
After the forking is done, you can start working on the newly created
|
||||||
repository. There, you will have full [Owner](../permissions/permissions.md)
|
repository. There, you will have full [Owner](../user/permissions.md)
|
||||||
access, so you can set it up as you please.
|
access, so you can set it up as you please.
|
||||||
|
|
||||||
## Merging upstream
|
## Merging upstream
|
||||||
|
|
|
@ -12,7 +12,7 @@ A protected branch does three simple things:
|
||||||
|
|
||||||
You can make any branch a protected branch. GitLab makes the master branch a protected branch by default.
|
You can make any branch a protected branch. GitLab makes the master branch a protected branch by default.
|
||||||
|
|
||||||
To protect a branch, user needs to have at least a Master permission level, see [permissions document](../permissions/permissions.md).
|
To protect a branch, user needs to have at least a Master permission level, see [permissions document](../user/permissions.md).
|
||||||
|
|
||||||
![protected branches page](protected_branches/protected_branches1.png)
|
![protected branches page](protected_branches/protected_branches1.png)
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ Feature: Project Merge Requests
|
||||||
Then The list should be sorted by "Oldest updated"
|
Then The list should be sorted by "Oldest updated"
|
||||||
|
|
||||||
@javascript
|
@javascript
|
||||||
Scenario: Visiting Merge Requests from a differente Project after sorting
|
Scenario: Visiting Merge Requests from a different Project after sorting
|
||||||
Given I visit project "Shop" merge requests page
|
Given I visit project "Shop" merge requests page
|
||||||
And I sort the list by "Oldest updated"
|
And I sort the list by "Oldest updated"
|
||||||
And I visit dashboard merge requests page
|
And I visit dashboard merge requests page
|
||||||
|
|
|
@ -207,6 +207,8 @@ module API
|
||||||
merge_request.subscribed?(options[:current_user])
|
merge_request.subscribed?(options[:current_user])
|
||||||
end
|
end
|
||||||
expose :user_notes_count
|
expose :user_notes_count
|
||||||
|
expose :should_remove_source_branch?, as: :should_remove_source_branch
|
||||||
|
expose :force_remove_source_branch?, as: :force_remove_source_branch
|
||||||
end
|
end
|
||||||
|
|
||||||
class MergeRequestChanges < MergeRequest
|
class MergeRequestChanges < MergeRequest
|
||||||
|
|
|
@ -17,7 +17,7 @@ module API
|
||||||
def current_user
|
def current_user
|
||||||
@current_user ||= (find_user_by_private_token || doorkeeper_guard)
|
@current_user ||= (find_user_by_private_token || doorkeeper_guard)
|
||||||
|
|
||||||
unless @current_user && Gitlab::UserAccess.allowed?(@current_user)
|
unless @current_user && Gitlab::UserAccess.new(@current_user).allowed?
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,10 @@ module Banzai
|
||||||
Renderer.render(text, context)
|
Renderer.render(text, context)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.cache_collection_render(texts_and_contexts)
|
||||||
|
Renderer.cache_collection_render(texts_and_contexts)
|
||||||
|
end
|
||||||
|
|
||||||
def self.render_result(text, context = {})
|
def self.render_result(text, context = {})
|
||||||
Renderer.render_result(text, context)
|
Renderer.render_result(text, context)
|
||||||
end
|
end
|
||||||
|
|
|
@ -39,9 +39,7 @@ module Banzai
|
||||||
|
|
||||||
# Renders the attribute of every given object.
|
# Renders the attribute of every given object.
|
||||||
def render_objects(objects, attribute)
|
def render_objects(objects, attribute)
|
||||||
objects.map do |object|
|
render_attributes(objects, attribute)
|
||||||
render_attribute(object, attribute)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Redacts the list of documents.
|
# Redacts the list of documents.
|
||||||
|
@ -64,16 +62,21 @@ module Banzai
|
||||||
context
|
context
|
||||||
end
|
end
|
||||||
|
|
||||||
# Renders the attribute of an object.
|
# Renders the attributes of a set of objects.
|
||||||
#
|
#
|
||||||
# Returns a `Nokogiri::HTML::Document`.
|
# Returns an Array of `Nokogiri::HTML::Document`.
|
||||||
def render_attribute(object, attribute)
|
def render_attributes(objects, attribute)
|
||||||
context = context_for(object, attribute)
|
strings_and_contexts = objects.map do |object|
|
||||||
|
context = context_for(object, attribute)
|
||||||
|
|
||||||
string = object.__send__(attribute)
|
string = object.__send__(attribute)
|
||||||
html = Banzai.render(string, context)
|
|
||||||
|
|
||||||
Banzai::Pipeline[:relative_link].to_document(html, context)
|
{ text: string, context: context }
|
||||||
|
end
|
||||||
|
|
||||||
|
Banzai.cache_collection_render(strings_and_contexts).each_with_index.map do |html, index|
|
||||||
|
Banzai::Pipeline[:relative_link].to_document(html, strings_and_contexts[index][:context])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def base_context
|
def base_context
|
||||||
|
|
|
@ -10,7 +10,7 @@ module Banzai
|
||||||
# requiring XHTML, such as Atom feeds, need to call `post_process` on the
|
# requiring XHTML, such as Atom feeds, need to call `post_process` on the
|
||||||
# result, providing the appropriate `pipeline` option.
|
# result, providing the appropriate `pipeline` option.
|
||||||
#
|
#
|
||||||
# markdown - Markdown String
|
# text - Markdown String
|
||||||
# context - Hash of context options passed to our HTML Pipeline
|
# context - Hash of context options passed to our HTML Pipeline
|
||||||
#
|
#
|
||||||
# Returns an HTML-safe String
|
# Returns an HTML-safe String
|
||||||
|
@ -29,6 +29,58 @@ module Banzai
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Perform multiple render from an Array of Markdown String into an
|
||||||
|
# Array of HTML-safe String of HTML.
|
||||||
|
#
|
||||||
|
# As the rendered Markdown String can be already cached read all the data
|
||||||
|
# from the cache using Rails.cache.read_multi operation. If the Markdown String
|
||||||
|
# is not in the cache or it's not cacheable (no cache_key entry is provided in
|
||||||
|
# the context) the Markdown String is rendered and stored in the cache so the
|
||||||
|
# next render call gets the rendered HTML-safe String from the cache.
|
||||||
|
#
|
||||||
|
# For further explanation see #render method comments.
|
||||||
|
#
|
||||||
|
# texts_and_contexts - An Array of Hashes that contains the Markdown String (:text)
|
||||||
|
# an options passed to our HTML Pipeline (:context)
|
||||||
|
#
|
||||||
|
# If on the :context you specify a :cache_key entry will be used to retrieve it
|
||||||
|
# and cache the result of rendering the Markdown String.
|
||||||
|
#
|
||||||
|
# Returns an Array containing HTML-safe String instances.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# texts_and_contexts
|
||||||
|
# => [{ text: '### Hello',
|
||||||
|
# context: { cache_key: [note, :note] } }]
|
||||||
|
def self.cache_collection_render(texts_and_contexts)
|
||||||
|
items_collection = texts_and_contexts.each_with_index do |item, index|
|
||||||
|
context = item[:context]
|
||||||
|
cache_key = full_cache_multi_key(context.delete(:cache_key), context[:pipeline])
|
||||||
|
|
||||||
|
item[:cache_key] = cache_key if cache_key
|
||||||
|
end
|
||||||
|
|
||||||
|
cacheable_items, non_cacheable_items = items_collection.partition { |item| item.key?(:cache_key) }
|
||||||
|
|
||||||
|
items_in_cache = []
|
||||||
|
items_not_in_cache = []
|
||||||
|
|
||||||
|
unless cacheable_items.empty?
|
||||||
|
items_in_cache = Rails.cache.read_multi(*cacheable_items.map { |item| item[:cache_key] })
|
||||||
|
items_not_in_cache = cacheable_items.reject do |item|
|
||||||
|
item[:rendered] = items_in_cache[item[:cache_key]]
|
||||||
|
items_in_cache.key?(item[:cache_key])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
(items_not_in_cache + non_cacheable_items).each do |item|
|
||||||
|
item[:rendered] = render(item[:text], item[:context])
|
||||||
|
Rails.cache.write(item[:cache_key], item[:rendered]) if item[:cache_key]
|
||||||
|
end
|
||||||
|
|
||||||
|
items_collection.map { |item| item[:rendered] }
|
||||||
|
end
|
||||||
|
|
||||||
def self.render_result(text, context = {})
|
def self.render_result(text, context = {})
|
||||||
text = Pipeline[:pre_process].to_html(text, context) if text
|
text = Pipeline[:pre_process].to_html(text, context) if text
|
||||||
|
|
||||||
|
@ -78,5 +130,13 @@ module Banzai
|
||||||
return unless cache_key
|
return unless cache_key
|
||||||
["banzai", *cache_key, pipeline_name || :full]
|
["banzai", *cache_key, pipeline_name || :full]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# To map Rails.cache.read_multi results we need to know the Rails.cache.expanded_key.
|
||||||
|
# Other option will be to generate stringified keys on our side and don't delegate to Rails.cache.expanded_key
|
||||||
|
# method.
|
||||||
|
def self.full_cache_multi_key(cache_key, pipeline_name)
|
||||||
|
return unless cache_key
|
||||||
|
Rails.cache.send(:expanded_key, full_cache_key(cache_key, pipeline_name))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,9 +14,10 @@ module Gitlab
|
||||||
OWNER = 50
|
OWNER = 50
|
||||||
|
|
||||||
# Branch protection settings
|
# Branch protection settings
|
||||||
PROTECTION_NONE = 0
|
PROTECTION_NONE = 0
|
||||||
PROTECTION_DEV_CAN_PUSH = 1
|
PROTECTION_DEV_CAN_PUSH = 1
|
||||||
PROTECTION_FULL = 2
|
PROTECTION_FULL = 2
|
||||||
|
PROTECTION_DEV_CAN_MERGE = 3
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def values
|
def values
|
||||||
|
@ -54,6 +55,7 @@ module Gitlab
|
||||||
def protection_options
|
def protection_options
|
||||||
{
|
{
|
||||||
"Not protected: Both developers and masters can push new commits, force push, or delete the branch." => PROTECTION_NONE,
|
"Not protected: Both developers and masters can push new commits, force push, or delete the branch." => PROTECTION_NONE,
|
||||||
|
"Protected against pushes: Developers cannot push new commits, but are allowed to accept merge requests to the branch." => PROTECTION_DEV_CAN_MERGE,
|
||||||
"Partially protected: Developers can push new commits, but cannot force push or delete the branch. Masters can do all of those." => PROTECTION_DEV_CAN_PUSH,
|
"Partially protected: Developers can push new commits, but cannot force push or delete the branch. Masters can do all of those." => PROTECTION_DEV_CAN_PUSH,
|
||||||
"Fully protected: Developers cannot push new commits, force push, or delete the branch. Only masters can do any of those." => PROTECTION_FULL,
|
"Fully protected: Developers cannot push new commits, force push, or delete the branch. Only masters can do any of those." => PROTECTION_FULL,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
module Gitlab
|
||||||
|
module Checks
|
||||||
|
class ChangeAccess
|
||||||
|
attr_reader :user_access, :project
|
||||||
|
|
||||||
|
def initialize(change, user_access:, project:)
|
||||||
|
@oldrev, @newrev, @ref = change.split(' ')
|
||||||
|
@branch_name = branch_name(@ref)
|
||||||
|
@user_access = user_access
|
||||||
|
@project = project
|
||||||
|
end
|
||||||
|
|
||||||
|
def exec
|
||||||
|
error = protected_branch_checks || tag_checks || push_checks
|
||||||
|
|
||||||
|
if error
|
||||||
|
GitAccessStatus.new(false, error)
|
||||||
|
else
|
||||||
|
GitAccessStatus.new(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def protected_branch_checks
|
||||||
|
return unless project.protected_branch?(@branch_name)
|
||||||
|
|
||||||
|
if forced_push? && user_access.cannot_do_action?(:force_push_code_to_protected_branches)
|
||||||
|
return "You are not allowed to force push code to a protected branch on this project."
|
||||||
|
elsif Gitlab::Git.blank_ref?(@newrev) && user_access.cannot_do_action?(:remove_protected_branches)
|
||||||
|
return "You are not allowed to delete protected branches from this project."
|
||||||
|
end
|
||||||
|
|
||||||
|
if matching_merge_request?
|
||||||
|
if user_access.can_merge_to_branch?(@branch_name) || user_access.can_push_to_branch?(@branch_name)
|
||||||
|
return
|
||||||
|
else
|
||||||
|
"You are not allowed to merge code into protected branches on this project."
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if user_access.can_push_to_branch?(@branch_name)
|
||||||
|
return
|
||||||
|
else
|
||||||
|
"You are not allowed to push code to protected branches on this project."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def tag_checks
|
||||||
|
tag_ref = tag_name(@ref)
|
||||||
|
|
||||||
|
if tag_ref && protected_tag?(tag_ref) && user_access.cannot_do_action?(:admin_project)
|
||||||
|
"You are not allowed to change existing tags on this project."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def push_checks
|
||||||
|
if user_access.cannot_do_action?(:push_code)
|
||||||
|
"You are not allowed to push code to this project."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def protected_tag?(tag_name)
|
||||||
|
project.repository.tag_exists?(tag_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def forced_push?
|
||||||
|
Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
|
||||||
|
end
|
||||||
|
|
||||||
|
def matching_merge_request?
|
||||||
|
Checks::MatchingMergeRequest.new(@newrev, @branch_name, @project).match?
|
||||||
|
end
|
||||||
|
|
||||||
|
def branch_name(ref)
|
||||||
|
ref = @ref.to_s
|
||||||
|
if Gitlab::Git.branch_ref?(ref)
|
||||||
|
Gitlab::Git.ref_name(ref)
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def tag_name(ref)
|
||||||
|
ref = @ref.to_s
|
||||||
|
if Gitlab::Git.tag_ref?(ref)
|
||||||
|
Gitlab::Git.ref_name(ref)
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,17 @@
|
||||||
|
module Gitlab
|
||||||
|
module Checks
|
||||||
|
class ForcePush
|
||||||
|
def self.force_push?(project, oldrev, newrev)
|
||||||
|
return false if project.empty_repo?
|
||||||
|
|
||||||
|
# Created or deleted branch
|
||||||
|
if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev)
|
||||||
|
false
|
||||||
|
else
|
||||||
|
missed_refs, _ = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} --git-dir=#{project.repository.path_to_repo} rev-list #{oldrev} ^#{newrev}))
|
||||||
|
missed_refs.split("\n").size > 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,18 @@
|
||||||
|
module Gitlab
|
||||||
|
module Checks
|
||||||
|
class MatchingMergeRequest
|
||||||
|
def initialize(newrev, branch_name, project)
|
||||||
|
@newrev = newrev
|
||||||
|
@branch_name = branch_name
|
||||||
|
@project = project
|
||||||
|
end
|
||||||
|
|
||||||
|
def match?
|
||||||
|
@project.merge_requests
|
||||||
|
.with_state(:locked)
|
||||||
|
.where(in_progress_merge_commit_sha: @newrev, target_branch: @branch_name)
|
||||||
|
.exists?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,16 +1,30 @@
|
||||||
module Gitlab
|
module Gitlab
|
||||||
module Diff
|
module Diff
|
||||||
class InlineDiff
|
class InlineDiff
|
||||||
|
# Regex to find a run of deleted lines followed by the same number of added lines
|
||||||
|
LINE_PAIRS_PATTERN = %r{
|
||||||
|
# Runs start at the beginning of the string (the first line) or after a space (for an unchanged line)
|
||||||
|
(?:\A|\s)
|
||||||
|
|
||||||
|
# This matches a number of `-`s followed by the same number of `+`s through recursion
|
||||||
|
(?<del_ins>
|
||||||
|
-
|
||||||
|
\g<del_ins>?
|
||||||
|
\+
|
||||||
|
)
|
||||||
|
|
||||||
|
# Runs end at the end of the string (the last line) or before a space (for an unchanged line)
|
||||||
|
(?=\s|\z)
|
||||||
|
}x.freeze
|
||||||
|
|
||||||
attr_accessor :old_line, :new_line, :offset
|
attr_accessor :old_line, :new_line, :offset
|
||||||
|
|
||||||
def self.for_lines(lines)
|
def self.for_lines(lines)
|
||||||
local_edit_indexes = self.find_local_edits(lines)
|
changed_line_pairs = self.find_changed_line_pairs(lines)
|
||||||
|
|
||||||
inline_diffs = []
|
inline_diffs = []
|
||||||
|
|
||||||
local_edit_indexes.each do |index|
|
changed_line_pairs.each do |old_index, new_index|
|
||||||
old_index = index
|
|
||||||
new_index = index + 1
|
|
||||||
old_line = lines[old_index]
|
old_line = lines[old_index]
|
||||||
new_line = lines[new_index]
|
new_line = lines[new_index]
|
||||||
|
|
||||||
|
@ -51,18 +65,28 @@ module Gitlab
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def self.find_local_edits(lines)
|
# Finds pairs of old/new line pairs that represent the same line that changed
|
||||||
line_prefixes = lines.map { |line| line.match(/\A([+-])/) ? $1 : ' ' }
|
def self.find_changed_line_pairs(lines)
|
||||||
joined_line_prefixes = " #{line_prefixes.join} "
|
# Prefixes of all diff lines, indicating their types
|
||||||
|
# For example: `" - + -+ ---+++ --+ -++"`
|
||||||
|
line_prefixes = lines.each_with_object("") { |line, s| s << line[0] }.gsub(/[^ +-]/, ' ')
|
||||||
|
|
||||||
offset = 0
|
changed_line_pairs = []
|
||||||
local_edit_indexes = []
|
line_prefixes.scan(LINE_PAIRS_PATTERN) do
|
||||||
while index = joined_line_prefixes.index(" -+ ", offset)
|
# For `"---+++"`, `begin_index == 0`, `end_index == 6`
|
||||||
local_edit_indexes << index
|
begin_index, end_index = Regexp.last_match.offset(:del_ins)
|
||||||
offset = index + 1
|
|
||||||
|
# For `"---+++"`, `changed_line_count == 3`
|
||||||
|
changed_line_count = (end_index - begin_index) / 2
|
||||||
|
|
||||||
|
halfway_index = begin_index + changed_line_count
|
||||||
|
(begin_index...halfway_index).each do |i|
|
||||||
|
# For `"---+++"`, index 1 maps to 1 + 3 = 4
|
||||||
|
changed_line_pairs << [i, i + changed_line_count]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local_edit_indexes
|
changed_line_pairs
|
||||||
end
|
end
|
||||||
|
|
||||||
def longest_common_prefix(a, b)
|
def longest_common_prefix(a, b)
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue