Merge branch 'master' into reference-pipeline-and-caching
This commit is contained in:
commit
740feeec77
30
CHANGELOG
30
CHANGELOG
|
|
@ -1,23 +1,40 @@
|
||||||
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.2.0 (unreleased)
|
v 8.2.0 (unreleased)
|
||||||
- Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu)
|
|
||||||
- Improved performance of replacing references in comments
|
- Improved performance of replacing references in comments
|
||||||
- Fix duplicate repositories in GitHub import page (Stan Hu)
|
|
||||||
- Redirect to a default path if HTTP_REFERER is not set (Stan Hu)
|
|
||||||
- Show last project commit to default branch on project home page
|
- Show last project commit to default branch on project home page
|
||||||
- Highlight comment based on anchor in URL
|
- Highlight comment based on anchor in URL
|
||||||
- Adds ability to remove the forked relationship from project settings screen. (Han Loong Liauw)
|
- Adds ability to remove the forked relationship from project settings screen. (Han Loong Liauw)
|
||||||
- Improved performance of sorting milestone issues
|
- Improved performance of sorting milestone issues
|
||||||
- Allow users to select the Files view as default project view (Cristian Bica)
|
- Allow users to select the Files view as default project view (Cristian Bica)
|
||||||
|
- Show "Empty Repository Page" for repository without branches (Artem V. Navrotskiy)
|
||||||
|
- Fix: Inability to reply to code comments in the MR view, if the MR comes from a fork
|
||||||
|
- Use git follow flag for commits page when retrieve history for file or directory
|
||||||
|
- Show merge request CI status on merge requests index page
|
||||||
|
- Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu)
|
||||||
|
|
||||||
v 8.1.0 (unreleased)
|
v 8.1.1
|
||||||
|
- Fix cloning Wiki repositories via HTTP (Stan Hu)
|
||||||
|
- Add migration to remove satellites directory
|
||||||
|
- Fix specific runners visibility
|
||||||
|
- Fix 500 when editing CI service
|
||||||
|
- Require CI jobs to be named
|
||||||
|
- Fix CSS for runner status
|
||||||
|
- Fix CI badge
|
||||||
|
- Allow developer to manage builds
|
||||||
|
|
||||||
|
v 8.1.0
|
||||||
|
- Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu)
|
||||||
|
- Fix duplicate repositories in GitHub import page (Stan Hu)
|
||||||
|
- Redirect to a default path if HTTP_REFERER is not set (Stan Hu)
|
||||||
- Send an email to admin email when a user is reported for spam (Jonathan Rochkind)
|
- Send an email to admin email when a user is reported for spam (Jonathan Rochkind)
|
||||||
- Show notifications button when user is member of group rather than project (Grzegorz Bizon)
|
- Show notifications button when user is member of group rather than project (Grzegorz Bizon)
|
||||||
- Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge.
|
- Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge.
|
||||||
- Fix nonatomic database update potentially causing project star counts to go negative (Stan Hu)
|
- Fix nonatomic database update potentially causing project star counts to go negative (Stan Hu)
|
||||||
|
- Don't show "Add README" link in an empty repository if user doesn't have access to push (Stan Hu)
|
||||||
- Fix error preventing displaying of commit data for a directory with a leading dot (Stan Hu)
|
- Fix error preventing displaying of commit data for a directory with a leading dot (Stan Hu)
|
||||||
- Speed up load times of issue detail pages by roughly 1.5x
|
- Speed up load times of issue detail pages by roughly 1.5x
|
||||||
|
- Fix CI rendering regressions
|
||||||
- If a merge request is to close an issue, show this on the issue page (Zeger-Jan van de Weg)
|
- If a merge request is to close an issue, show this on the issue page (Zeger-Jan van de Weg)
|
||||||
- Add a system note and update relevant merge requests when a branch is deleted or re-added (Stan Hu)
|
- Add a system note and update relevant merge requests when a branch is deleted or re-added (Stan Hu)
|
||||||
- Make diff file view easier to use on mobile screens (Stan Hu)
|
- Make diff file view easier to use on mobile screens (Stan Hu)
|
||||||
|
|
@ -27,8 +44,10 @@ v 8.1.0 (unreleased)
|
||||||
- Allow removing of project without confirmation when JavaScript is disabled (Stan Hu)
|
- Allow removing of project without confirmation when JavaScript is disabled (Stan Hu)
|
||||||
- Support filtering by "Any" milestone or issue and fix "No Milestone" and "No Label" filters (Stan Hu)
|
- Support filtering by "Any" milestone or issue and fix "No Milestone" and "No Label" filters (Stan Hu)
|
||||||
- Improved performance of the trending projects page
|
- Improved performance of the trending projects page
|
||||||
|
- Remove CI migration task
|
||||||
- Improved performance of finding projects by their namespace
|
- Improved performance of finding projects by their namespace
|
||||||
- Fix bug where transferring a project would result in stale commit links (Stan Hu)
|
- Fix bug where transferring a project would result in stale commit links (Stan Hu)
|
||||||
|
- Fix build trace updating
|
||||||
- Include full path of source and target branch names in New Merge Request page (Stan Hu)
|
- Include full path of source and target branch names in New Merge Request page (Stan Hu)
|
||||||
- Add user preference to view activities as default dashboard (Stan Hu)
|
- Add user preference to view activities as default dashboard (Stan Hu)
|
||||||
- Add option to admin area to sign in as a specific user (Pavel Forkert)
|
- Add option to admin area to sign in as a specific user (Pavel Forkert)
|
||||||
|
|
@ -71,6 +90,7 @@ v 8.1.0 (unreleased)
|
||||||
- Fix position of hamburger in header for smaller screens (Han Loong Liauw)
|
- Fix position of hamburger in header for smaller screens (Han Loong Liauw)
|
||||||
- Fix bug where Emojis in Markdown would truncate remaining text (Sakata Sinji)
|
- Fix bug where Emojis in Markdown would truncate remaining text (Sakata Sinji)
|
||||||
- Persist filters when sorting on admin user page (Jerry Lukins)
|
- Persist filters when sorting on admin user page (Jerry Lukins)
|
||||||
|
- Update style of snippets pages (Han Loong Liauw)
|
||||||
- Allow dashboard and group issues/MRs to be filtered by label
|
- Allow dashboard and group issues/MRs to be filtered by label
|
||||||
- Add spellcheck=false to certain input fields
|
- Add spellcheck=false to certain input fields
|
||||||
- Invalidate stored service password if the endpoint URL is changed
|
- Invalidate stored service password if the endpoint URL is changed
|
||||||
|
|
@ -83,11 +103,11 @@ v 8.1.0 (unreleased)
|
||||||
- Let gitlab-git-http-server generate and serve 'git archive' downloads
|
- Let gitlab-git-http-server generate and serve 'git archive' downloads
|
||||||
- Optimize query when filtering on issuables (Zeger-Jan van de Weg)
|
- Optimize query when filtering on issuables (Zeger-Jan van de Weg)
|
||||||
- Fix padding of outdated discussion item.
|
- Fix padding of outdated discussion item.
|
||||||
|
- Animate the logo on hover
|
||||||
|
|
||||||
v 8.0.5
|
v 8.0.5
|
||||||
- Correct lookup-by-email for LDAP logins
|
- Correct lookup-by-email for LDAP logins
|
||||||
- Fix loading spinner sometimes not being hidden on Merge Request tab switches
|
- Fix loading spinner sometimes not being hidden on Merge Request tab switches
|
||||||
- Animate the logo on hover
|
|
||||||
|
|
||||||
v 8.0.4
|
v 8.0.4
|
||||||
- Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu)
|
- Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu)
|
||||||
|
|
|
||||||
2
Gemfile
2
Gemfile
|
|
@ -197,7 +197,7 @@ gem 'bootstrap-sass', '~> 3.0'
|
||||||
gem 'font-awesome-rails', '~> 4.2'
|
gem 'font-awesome-rails', '~> 4.2'
|
||||||
gem 'gitlab_emoji', '~> 0.1'
|
gem 'gitlab_emoji', '~> 0.1'
|
||||||
gem 'gon', '~> 5.0.0'
|
gem 'gon', '~> 5.0.0'
|
||||||
gem 'jquery-atwho-rails', '~> 1.0.0'
|
gem 'jquery-atwho-rails', '~> 1.3.2'
|
||||||
gem 'jquery-rails', '~> 3.1.3'
|
gem 'jquery-rails', '~> 3.1.3'
|
||||||
gem 'jquery-scrollto-rails', '~> 1.4.3'
|
gem 'jquery-scrollto-rails', '~> 1.4.3'
|
||||||
gem 'jquery-ui-rails', '~> 4.2.1'
|
gem 'jquery-ui-rails', '~> 4.2.1'
|
||||||
|
|
|
||||||
|
|
@ -354,7 +354,7 @@ GEM
|
||||||
ice_nine (0.11.1)
|
ice_nine (0.11.1)
|
||||||
inflecto (0.0.2)
|
inflecto (0.0.2)
|
||||||
ipaddress (0.8.0)
|
ipaddress (0.8.0)
|
||||||
jquery-atwho-rails (1.0.1)
|
jquery-atwho-rails (1.3.2)
|
||||||
jquery-rails (3.1.3)
|
jquery-rails (3.1.3)
|
||||||
railties (>= 3.0, < 5.0)
|
railties (>= 3.0, < 5.0)
|
||||||
thor (>= 0.14, < 2.0)
|
thor (>= 0.14, < 2.0)
|
||||||
|
|
@ -840,7 +840,7 @@ DEPENDENCIES
|
||||||
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)
|
||||||
jquery-atwho-rails (~> 1.0.0)
|
jquery-atwho-rails (~> 1.3.2)
|
||||||
jquery-rails (~> 3.1.3)
|
jquery-rails (~> 3.1.3)
|
||||||
jquery-scrollto-rails (~> 1.4.3)
|
jquery-scrollto-rails (~> 1.4.3)
|
||||||
jquery-turbolinks (~> 2.0.1)
|
jquery-turbolinks (~> 2.0.1)
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -22,7 +22,7 @@ class CiBuild
|
||||||
# Only valid for runnig build when output changes during time
|
# Only valid for runnig build when output changes during time
|
||||||
#
|
#
|
||||||
CiBuild.interval = setInterval =>
|
CiBuild.interval = setInterval =>
|
||||||
if window.location.href is build_url
|
if window.location.href.split("#").first() is build_url
|
||||||
$.ajax
|
$.ajax
|
||||||
url: build_url
|
url: build_url
|
||||||
dataType: "json"
|
dataType: "json"
|
||||||
|
|
@ -31,7 +31,7 @@ class CiBuild
|
||||||
$('#build-trace code').html build.trace_html
|
$('#build-trace code').html build.trace_html
|
||||||
$('#build-trace code').append '<i class="fa fa-refresh fa-spin"/>'
|
$('#build-trace code').append '<i class="fa fa-refresh fa-spin"/>'
|
||||||
@checkAutoscroll()
|
@checkAutoscroll()
|
||||||
else
|
else if build.status != build_status
|
||||||
Turbolinks.visit build_url
|
Turbolinks.visit build_url
|
||||||
, 4000
|
, 4000
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
#= require clipboard
|
||||||
|
|
||||||
|
$ ->
|
||||||
|
clipboard = new Clipboard '.js-clipboard-trigger',
|
||||||
|
text: (trigger) ->
|
||||||
|
$target = $(trigger.nextElementSibling || trigger.previousElementSibling)
|
||||||
|
$target.data('clipboard-text') || $target.text().trim()
|
||||||
|
|
||||||
|
clipboard.on 'success', (e) ->
|
||||||
|
$(e.trigger).
|
||||||
|
tooltip(trigger: 'manual', placement: 'auto bottom', title: 'Copied!').
|
||||||
|
tooltip('show')
|
||||||
|
|
||||||
|
# Clear the selection and blur the trigger so it loses its border
|
||||||
|
e.clearSelection()
|
||||||
|
$(e.trigger).blur()
|
||||||
|
|
||||||
|
# Manually hide the tooltip after 1 second
|
||||||
|
setTimeout(->
|
||||||
|
$(e.trigger).tooltip('hide')
|
||||||
|
, 1000)
|
||||||
|
|
@ -162,10 +162,21 @@
|
||||||
border-color: #e7e9ed;
|
border-color: #e7e9ed;
|
||||||
width: 140px;
|
width: 140px;
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
font-weight: normal;
|
||||||
|
background-color: #eee;
|
||||||
|
color: #78a;
|
||||||
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
border-color: $gl-info;
|
border-color: $gl-info;
|
||||||
background: $gl-info;
|
background: $gl-info;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
color: $gl-info;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -147,14 +147,8 @@
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
background-color: #fff;
|
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
color: #78a;
|
color: #78a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fa-align {
|
|
||||||
top: 20px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -80,3 +80,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.issuable-filter-count {
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: -16px;
|
||||||
|
padding: 13px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cross-project-reference {
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.slead {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span, button {
|
||||||
|
background-color: $background-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,15 @@
|
||||||
|
|
||||||
#modal_merge_info .modal-dialog {
|
#modal_merge_info .modal-dialog {
|
||||||
width: 600px;
|
width: 600px;
|
||||||
|
|
||||||
|
.btn-clipboard {
|
||||||
|
@extend .pull-right;
|
||||||
|
|
||||||
|
margin-right: 18px;
|
||||||
|
margin-top: 5px;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mr-source-target {
|
.mr-source-target {
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-home-dropdown {
|
.project-home-dropdown {
|
||||||
margin: 11px 3px 0;
|
margin: 13px 0px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notifications-btn {
|
||||||
|
.fa-bell {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-angle-down {
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-home-desc {
|
.project-home-desc {
|
||||||
|
|
@ -85,6 +95,7 @@
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-group {
|
.input-group {
|
||||||
display: inline-table;
|
display: inline-table;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
@ -233,23 +244,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fa-fw {
|
i {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fa-bell {
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fa-angle-down {
|
|
||||||
margin-left: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-home-panel .project-home-dropdown {
|
|
||||||
margin: 13px 0px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-visibility-level-holder {
|
.project-visibility-level-holder {
|
||||||
.radio {
|
.radio {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
|
@ -544,5 +543,13 @@ pre.light-well {
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-show-readme .readme-holder {
|
.project-show-readme .readme-holder {
|
||||||
|
margin-left: -$gl-padding;
|
||||||
|
margin-right: -$gl-padding;
|
||||||
|
padding: ($gl-padding + 7px);
|
||||||
border-top: 0;
|
border-top: 0;
|
||||||
|
|
||||||
|
.edit-project-readme {
|
||||||
|
z-index: 100;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,34 @@
|
||||||
.ci-body {
|
.runner-state {
|
||||||
.runner-state {
|
padding: 6px 12px;
|
||||||
padding: 6px 12px;
|
margin-right: 10px;
|
||||||
margin-right: 10px;
|
color: #FFF;
|
||||||
color: #FFF;
|
|
||||||
|
|
||||||
&.runner-state-shared {
|
&.runner-state-shared {
|
||||||
background: #32b186;
|
background: #32b186;
|
||||||
}
|
|
||||||
&.runner-state-specific {
|
|
||||||
background: #3498db;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
&.runner-state-specific {
|
||||||
.runner-status-online {
|
background: #3498db;
|
||||||
color: green;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.runner-status-offline {
|
.runner-status-online {
|
||||||
color: gray;
|
color: green;
|
||||||
}
|
}
|
||||||
|
|
||||||
.runner-status-paused {
|
.runner-status-offline {
|
||||||
color: red;
|
color: gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
.runner {
|
.runner-status-paused {
|
||||||
.btn {
|
color: red;
|
||||||
padding: 1px 6px;
|
}
|
||||||
}
|
|
||||||
|
.runner {
|
||||||
h4 {
|
.btn {
|
||||||
font-weight: normal;
|
padding: 1px 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,3 @@
|
||||||
.my-snippets li:first-child {
|
|
||||||
h4 { margin-top: 0; }
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.snippet-form-holder .file-holder .file-title {
|
.snippet-form-holder .file-holder .file-title {
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
@ -30,3 +25,58 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.snippet-holder {
|
||||||
|
.snippet-details {
|
||||||
|
.page-title {
|
||||||
|
margin-top: -15px;
|
||||||
|
padding: 10px 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
color: #5c5d5e;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
.author {
|
||||||
|
color: #5c5d5e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.snippet-id {
|
||||||
|
color: #5c5d5e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.snippet-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 23px;
|
||||||
|
color: #313236;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $screen-md-max) {
|
||||||
|
.new-snippet-link {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $screen-sm-max) {
|
||||||
|
.creator,
|
||||||
|
.page-title .btn-close {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-holder {
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.snippet-box {
|
||||||
|
@include border-radius(2px);
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
padding: 10px $gl-padding;
|
||||||
|
font-weight: normal;
|
||||||
|
margin-right: 10px;
|
||||||
|
font-size: $gl-font-size;
|
||||||
|
border: 1px solid;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
tr {
|
tr {
|
||||||
> td, > th {
|
> td, > th {
|
||||||
line-height: 32px;
|
line-height: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,6 @@ class ApplicationController < ActionController::Base
|
||||||
project_path = "#{namespace}/#{id}"
|
project_path = "#{namespace}/#{id}"
|
||||||
@project = Project.find_with_namespace(project_path)
|
@project = Project.find_with_namespace(project_path)
|
||||||
|
|
||||||
|
|
||||||
if @project and can?(current_user, :read_project, @project)
|
if @project and can?(current_user, :read_project, @project)
|
||||||
if @project.path_with_namespace != project_path
|
if @project.path_with_namespace != project_path
|
||||||
redirect_to request.original_url.gsub(project_path, @project.path_with_namespace) and return
|
redirect_to request.original_url.gsub(project_path, @project.path_with_namespace) and return
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ module Ci
|
||||||
@projects = @projects.where(gitlab_id: @gl_projects.select(:id))
|
@projects = @projects.where(gitlab_id: @gl_projects.select(:id))
|
||||||
end
|
end
|
||||||
@projects = @projects.where("ci_projects.id NOT IN (?)", @runner.projects.pluck(:id)) if @runner.projects.any?
|
@projects = @projects.where("ci_projects.id NOT IN (?)", @runner.projects.pluck(:id)) if @runner.projects.any?
|
||||||
|
@projects = @projects.joins(:gl_project)
|
||||||
@projects = @projects.page(params[:page]).per(30)
|
@projects = @projects.page(params[:page]).per(30)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,6 @@ module Ci
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def authenticate_public_page!
|
|
||||||
unless project.public
|
|
||||||
authenticate_user!
|
|
||||||
|
|
||||||
return access_denied! unless can?(current_user, :read_project, gl_project)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def authenticate_token!
|
def authenticate_token!
|
||||||
unless project.valid_token?(params[:token])
|
unless project.valid_token?(params[:token])
|
||||||
return head(403)
|
return head(403)
|
||||||
|
|
|
||||||
|
|
@ -2,23 +2,24 @@ class Projects::BuildsController < Projects::ApplicationController
|
||||||
before_action :ci_project
|
before_action :ci_project
|
||||||
before_action :build, except: [:index, :cancel_all]
|
before_action :build, except: [:index, :cancel_all]
|
||||||
|
|
||||||
before_action :authorize_admin_project!, except: [:index, :show, :status]
|
before_action :authorize_manage_builds!, except: [:index, :show, :status]
|
||||||
|
|
||||||
layout "project"
|
layout "project"
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@scope = params[:scope]
|
@scope = params[:scope]
|
||||||
@all_builds = project.ci_builds
|
@all_builds = project.ci_builds
|
||||||
|
@builds = @all_builds.order('created_at DESC')
|
||||||
@builds =
|
@builds =
|
||||||
case @scope
|
case @scope
|
||||||
when 'all'
|
when 'all'
|
||||||
@all_builds
|
@builds
|
||||||
when 'finished'
|
when 'finished'
|
||||||
@all_builds.finished
|
@builds.finished
|
||||||
else
|
else
|
||||||
@all_builds.running_or_pending
|
@builds.running_or_pending.reverse_order
|
||||||
end
|
end
|
||||||
@builds = @builds.order('created_at DESC').page(params[:page]).per(30)
|
@builds = @builds.page(params[:page]).per(30)
|
||||||
end
|
end
|
||||||
|
|
||||||
def cancel_all
|
def cancel_all
|
||||||
|
|
@ -73,4 +74,10 @@ class Projects::BuildsController < Projects::ApplicationController
|
||||||
def build_path(build)
|
def build_path(build)
|
||||||
namespace_project_build_path(build.gl_project.namespace, build.gl_project, build)
|
namespace_project_build_path(build.gl_project.namespace, build.gl_project, build)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def authorize_manage_builds!
|
||||||
|
unless can?(current_user, :manage_builds, project)
|
||||||
|
return page_404
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,17 @@ class Projects::CiServicesController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
if @service.update_attributes(service_params)
|
if service.update_attributes(service_params)
|
||||||
redirect_to edit_namespace_project_ci_service_path(@project, @project.namespace, @service.to_param)
|
redirect_to edit_namespace_project_ci_service_path(@project.namespace, @project, service.to_param)
|
||||||
else
|
else
|
||||||
render 'edit'
|
render 'edit'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test
|
def test
|
||||||
last_build = @project.builds.last
|
last_build = @project.ci_builds.last
|
||||||
|
|
||||||
if @service.execute(last_build)
|
if service.execute(last_build)
|
||||||
message = { notice: 'We successfully tested the service' }
|
message = { notice: 'We successfully tested the service' }
|
||||||
else
|
else
|
||||||
message = { alert: 'We tried to test the service but error occurred' }
|
message = { alert: 'We tried to test the service but error occurred' }
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@
|
||||||
class Projects::CommitController < Projects::ApplicationController
|
class Projects::CommitController < Projects::ApplicationController
|
||||||
# Authorize
|
# Authorize
|
||||||
before_action :require_non_empty_project
|
before_action :require_non_empty_project
|
||||||
before_action :authorize_download_code!
|
before_action :authorize_download_code!, except: [:cancel_builds]
|
||||||
|
before_action :authorize_manage_builds!, only: [:cancel_builds]
|
||||||
before_action :commit
|
before_action :commit
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
|
@ -55,4 +56,12 @@ class Projects::CommitController < Projects::ApplicationController
|
||||||
def commit
|
def commit
|
||||||
@commit ||= @project.commit(params[:id])
|
@commit ||= @project.commit(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def authorize_manage_builds!
|
||||||
|
unless can?(current_user, :manage_builds, project)
|
||||||
|
return page_404
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ class Projects::CommitsController < Projects::ApplicationController
|
||||||
@limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
|
@limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
|
||||||
|
|
||||||
@commits = @repo.commits(@ref, @path, @limit, @offset)
|
@commits = @repo.commits(@ref, @path, @limit, @offset)
|
||||||
@note_counts = Note.where(commit_id: @commits.map(&:id)).
|
@note_counts = project.notes.where(commit_id: @commits.map(&:id)).
|
||||||
group(:commit_id).count
|
group(:commit_id).count
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,10 @@ class Projects::RunnersController < Projects::ApplicationController
|
||||||
layout 'project_settings'
|
layout 'project_settings'
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@runners = @ci_project.runners.order('id DESC')
|
@runners = @ci_project.runners.ordered
|
||||||
@specific_runners =
|
@specific_runners = current_user.ci_authorized_runners.
|
||||||
Ci::Runner.specific.includes(:runner_projects).
|
where.not(id: @ci_project.runners).
|
||||||
where(Ci::RunnerProject.table_name => { project_id: current_user.authorized_projects } ).
|
ordered.page(params[:page]).per(20)
|
||||||
where.not(id: @runners).order("#{Ci::Runner.table_name}.id DESC").page(params[:page]).per(20)
|
|
||||||
@shared_runners = Ci::Runner.shared.active
|
@shared_runners = Ci::Runner.shared.active
|
||||||
@shared_runners_count = @shared_runners.count(:all)
|
@shared_runners_count = @shared_runners.count(:all)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ class Projects::SnippetsController < Projects::ApplicationController
|
||||||
filter: :by_project,
|
filter: :by_project,
|
||||||
project: @project
|
project: @project
|
||||||
})
|
})
|
||||||
|
@snippets = @snippets.page(params[:page]).per(PER_PAGE)
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
|
|
|
||||||
|
|
@ -124,11 +124,7 @@ class ProjectsController < ApplicationController
|
||||||
::Projects::DestroyService.new(@project, current_user, {}).execute
|
::Projects::DestroyService.new(@project, current_user, {}).execute
|
||||||
flash[:alert] = "Project '#{@project.name}' was deleted."
|
flash[:alert] = "Project '#{@project.name}' was deleted."
|
||||||
|
|
||||||
if request.referer.include?('/admin')
|
redirect_back_or_default(default: dashboard_projects_path, options: {})
|
||||||
redirect_to admin_namespaces_projects_path
|
|
||||||
else
|
|
||||||
redirect_to dashboard_projects_path
|
|
||||||
end
|
|
||||||
rescue Projects::DestroyService::DestroyError => ex
|
rescue Projects::DestroyService::DestroyError => ex
|
||||||
redirect_to edit_project_path(@project), alert: ex.message
|
redirect_to edit_project_path(@project), alert: ex.message
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -42,4 +42,13 @@ module CiStatusHelper
|
||||||
|
|
||||||
icon(icon_name)
|
icon(icon_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def render_ci_status(ci_commit)
|
||||||
|
link_to ci_status_path(ci_commit),
|
||||||
|
class: "c#{ci_status_color(ci_commit)}",
|
||||||
|
title: "Build status: #{ci_commit.status}",
|
||||||
|
data: { toggle: 'tooltip', placement: 'left' } do
|
||||||
|
ci_status_icon(ci_commit)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
module ClipboardHelper
|
||||||
|
def clipboard_button
|
||||||
|
content_tag :button,
|
||||||
|
icon('clipboard'),
|
||||||
|
class: 'btn btn-xs btn-clipboard js-clipboard-trigger',
|
||||||
|
type: :button
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -110,22 +110,4 @@ module TabHelper
|
||||||
'active'
|
'active'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Use nav_tab for save controller/action but different params
|
|
||||||
def nav_tab(key, value, &block)
|
|
||||||
o = {}
|
|
||||||
o[:class] = ""
|
|
||||||
|
|
||||||
if value.nil?
|
|
||||||
o[:class] << " active" if params[key].blank?
|
|
||||||
else
|
|
||||||
o[:class] << " active" if params[key] == value
|
|
||||||
end
|
|
||||||
|
|
||||||
if block_given?
|
|
||||||
content_tag(:li, capture(&block), o)
|
|
||||||
else
|
|
||||||
content_tag(:li, nil, o)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@ module Ci
|
||||||
def ordered_by_last_commit_date
|
def ordered_by_last_commit_date
|
||||||
last_commit_subquery = "(SELECT gl_project_id, MAX(committed_at) committed_at FROM #{Ci::Commit.table_name} GROUP BY gl_project_id)"
|
last_commit_subquery = "(SELECT gl_project_id, MAX(committed_at) committed_at FROM #{Ci::Commit.table_name} GROUP BY gl_project_id)"
|
||||||
joins("LEFT JOIN #{last_commit_subquery} AS last_commit ON #{Ci::Project.table_name}.gitlab_id = last_commit.gl_project_id").
|
joins("LEFT JOIN #{last_commit_subquery} AS last_commit ON #{Ci::Project.table_name}.gitlab_id = last_commit.gl_project_id").
|
||||||
|
joins(:gl_project).
|
||||||
order("CASE WHEN last_commit.committed_at IS NULL THEN 1 ELSE 0 END, last_commit.committed_at DESC")
|
order("CASE WHEN last_commit.committed_at IS NULL THEN 1 ELSE 0 END, last_commit.committed_at DESC")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,5 @@ module Ci
|
||||||
def human_status
|
def human_status
|
||||||
status
|
status
|
||||||
end
|
end
|
||||||
|
|
||||||
def last_commit_for_ref(ref)
|
|
||||||
commits.where(ref: ref).last
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ module Ci
|
||||||
scope :active, ->() { where(active: true) }
|
scope :active, ->() { where(active: true) }
|
||||||
scope :paused, ->() { where(active: false) }
|
scope :paused, ->() { where(active: false) }
|
||||||
scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) }
|
scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) }
|
||||||
|
scope :ordered, ->() { order(id: :desc) }
|
||||||
|
|
||||||
acts_as_taggable
|
acts_as_taggable
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ class CommitStatus < ActiveRecord::Base
|
||||||
scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :ref)) }
|
scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :ref)) }
|
||||||
scope :ordered, -> { order(:ref, :stage_idx, :name) }
|
scope :ordered, -> { order(:ref, :stage_idx, :name) }
|
||||||
scope :for_ref, ->(ref) { where(ref: ref) }
|
scope :for_ref, ->(ref) { where(ref: ref) }
|
||||||
scope :running_or_pending, -> { where(status: [:running, :pending]) }
|
|
||||||
|
|
||||||
state_machine :status, initial: :pending do
|
state_machine :status, initial: :pending do
|
||||||
event :run do
|
event :run do
|
||||||
|
|
|
||||||
|
|
@ -159,11 +159,11 @@ class MergeRequest < ActiveRecord::Base
|
||||||
|
|
||||||
def last_commit
|
def last_commit
|
||||||
merge_request_diff ? merge_request_diff.last_commit : compare_commits.last
|
merge_request_diff ? merge_request_diff.last_commit : compare_commits.last
|
||||||
end
|
end
|
||||||
|
|
||||||
def first_commit
|
def first_commit
|
||||||
merge_request_diff ? merge_request_diff.first_commit : compare_commits.first
|
merge_request_diff ? merge_request_diff.first_commit : compare_commits.first
|
||||||
end
|
end
|
||||||
|
|
||||||
def last_commit_short_sha
|
def last_commit_short_sha
|
||||||
last_commit.short_id
|
last_commit.short_id
|
||||||
|
|
@ -257,7 +257,7 @@ class MergeRequest < ActiveRecord::Base
|
||||||
|
|
||||||
Note.where(
|
Note.where(
|
||||||
"(project_id = :target_project_id AND noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR" +
|
"(project_id = :target_project_id AND noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR" +
|
||||||
"(project_id = :source_project_id AND noteable_type = 'Commit' AND commit_id IN (:commit_ids))",
|
"((project_id = :source_project_id OR project_id = :target_project_id) AND noteable_type = 'Commit' AND commit_id IN (:commit_ids))",
|
||||||
mr_id: id,
|
mr_id: id,
|
||||||
commit_ids: commit_ids,
|
commit_ids: commit_ids,
|
||||||
target_project_id: target_project_id,
|
target_project_id: target_project_id,
|
||||||
|
|
@ -470,4 +470,10 @@ class MergeRequest < ActiveRecord::Base
|
||||||
unlock_mr if locked?
|
unlock_mr if locked?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ci_commit
|
||||||
|
if last_commit
|
||||||
|
source_project.ci_commit(last_commit.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -243,11 +243,12 @@ class Project < ActiveRecord::Base
|
||||||
# Use of unscoped ensures we're not secretly adding any ORDER BYs, which
|
# Use of unscoped ensures we're not secretly adding any ORDER BYs, which
|
||||||
# have a negative impact on performance (and aren't needed for this
|
# have a negative impact on performance (and aren't needed for this
|
||||||
# query).
|
# query).
|
||||||
unscoped.
|
projects = unscoped.
|
||||||
joins(:namespace).
|
joins(:namespace).
|
||||||
iwhere('namespaces.path' => namespace_path).
|
iwhere('namespaces.path' => namespace_path)
|
||||||
iwhere('projects.path' => project_path).
|
|
||||||
take
|
projects.where('projects.path' => project_path).take ||
|
||||||
|
projects.iwhere('projects.path' => project_path).take
|
||||||
end
|
end
|
||||||
|
|
||||||
def visibility_levels
|
def visibility_levels
|
||||||
|
|
@ -567,7 +568,7 @@ class Project < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def empty_repo?
|
def empty_repo?
|
||||||
!repository.exists? || repository.empty?
|
!repository.exists? || !repository.has_visible_content?
|
||||||
end
|
end
|
||||||
|
|
||||||
def repo
|
def repo
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,19 @@ class Repository
|
||||||
raw_repository.empty?
|
raw_repository.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Git repository can contains some hidden refs like:
|
||||||
|
# /refs/notes/*
|
||||||
|
# /refs/git-as-svn/*
|
||||||
|
# /refs/pulls/*
|
||||||
|
# This refs by default not visible in project page and not cloned to client side.
|
||||||
|
#
|
||||||
|
# This method return true if repository contains some content visible in project page.
|
||||||
|
#
|
||||||
|
def has_visible_content?
|
||||||
|
!raw_repository.branches.empty?
|
||||||
|
end
|
||||||
|
|
||||||
def commit(id = 'HEAD')
|
def commit(id = 'HEAD')
|
||||||
return nil unless raw_repository
|
return nil unless raw_repository
|
||||||
commit = Gitlab::Git::Commit.find(raw_repository, id)
|
commit = Gitlab::Git::Commit.find(raw_repository, id)
|
||||||
|
|
@ -54,13 +67,16 @@ class Repository
|
||||||
end
|
end
|
||||||
|
|
||||||
def commits(ref, path = nil, limit = nil, offset = nil, skip_merges = false)
|
def commits(ref, path = nil, limit = nil, offset = nil, skip_merges = false)
|
||||||
commits = Gitlab::Git::Commit.where(
|
options = {
|
||||||
repo: raw_repository,
|
repo: raw_repository,
|
||||||
ref: ref,
|
ref: ref,
|
||||||
path: path,
|
path: path,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
)
|
follow: path.present?
|
||||||
|
}
|
||||||
|
|
||||||
|
commits = Gitlab::Git::Commit.where(options)
|
||||||
commits = Commit.decorate(commits, @project) if commits.present?
|
commits = Commit.decorate(commits, @project) if commits.present?
|
||||||
commits
|
commits
|
||||||
end
|
end
|
||||||
|
|
@ -480,7 +496,7 @@ class Repository
|
||||||
|
|
||||||
def search_files(query, ref)
|
def search_files(query, ref)
|
||||||
offset = 2
|
offset = 2
|
||||||
args = %W(git grep -i -n --before-context #{offset} --after-context #{offset} #{query} #{ref || root_ref})
|
args = %W(git grep -i -n --before-context #{offset} --after-context #{offset} -e #{query} #{ref || root_ref})
|
||||||
Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/)
|
Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -401,15 +401,17 @@ class User < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def authorized_projects_id
|
||||||
|
@authorized_projects_id ||= begin
|
||||||
|
project_ids = personal_projects.pluck(:id)
|
||||||
|
project_ids.push(*groups_projects.pluck(:id))
|
||||||
|
project_ids.push(*projects.pluck(:id).uniq)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Projects user has access to
|
# Projects user has access to
|
||||||
def authorized_projects
|
def authorized_projects
|
||||||
@authorized_projects ||= begin
|
@authorized_projects ||= Project.where(id: authorized_projects_id)
|
||||||
project_ids = personal_projects.pluck(:id)
|
|
||||||
project_ids.push(*groups_projects.pluck(:id))
|
|
||||||
project_ids.push(*projects.pluck(:id).uniq)
|
|
||||||
Project.where(id: project_ids)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def owned_projects
|
def owned_projects
|
||||||
|
|
@ -768,11 +770,14 @@ class User < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def ci_authorized_projects
|
def ci_authorized_projects
|
||||||
@ci_authorized_projects ||= Ci::Project.where(gitlab_id: authorized_projects)
|
@ci_authorized_projects ||= Ci::Project.where(gitlab_id: authorized_projects_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def ci_authorized_runners
|
def ci_authorized_runners
|
||||||
Ci::Runner.specific.includes(:runner_projects).
|
@ci_authorized_runners ||= begin
|
||||||
where(ci_runner_projects: { project_id: ci_authorized_projects } )
|
runner_ids = Ci::RunnerProject.joins(:project).
|
||||||
|
where(ci_projects: { gitlab_id: authorized_projects_id }).select(:runner_id)
|
||||||
|
Ci::Runner.specific.where(id: runner_ids)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,15 @@
|
||||||
module Ci
|
module Ci
|
||||||
class ImageForBuildService
|
class ImageForBuildService
|
||||||
def execute(project, params)
|
def execute(project, params)
|
||||||
image_name =
|
sha = params[:sha]
|
||||||
if params[:sha]
|
sha ||=
|
||||||
commit = project.commits.find_by(sha: params[:sha])
|
if params[:ref]
|
||||||
image_for_commit(commit)
|
project.gl_project.commit(params[:ref]).try(:sha)
|
||||||
elsif params[:ref]
|
|
||||||
commit = project.last_commit_for_ref(params[:ref])
|
|
||||||
image_for_commit(commit)
|
|
||||||
else
|
|
||||||
'build-unknown.svg'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
commit = project.commits.ordered.find_by(sha: sha)
|
||||||
|
image_name = image_for_commit(commit)
|
||||||
|
|
||||||
image_path = Rails.root.join('public/ci', image_name)
|
image_path = Rails.root.join('public/ci', image_name)
|
||||||
|
|
||||||
OpenStruct.new(
|
OpenStruct.new(
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,19 @@ module MergeRequests
|
||||||
|
|
||||||
@oldrev, @newrev = oldrev, newrev
|
@oldrev, @newrev = oldrev, newrev
|
||||||
@branch_name = Gitlab::Git.ref_name(ref)
|
@branch_name = Gitlab::Git.ref_name(ref)
|
||||||
@fork_merge_requests = @project.fork_merge_requests.opened
|
|
||||||
@commits = []
|
|
||||||
|
|
||||||
# Leave a system note if a branch were deleted/added
|
find_new_commits
|
||||||
if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev)
|
reload_merge_requests
|
||||||
|
|
||||||
|
# Leave a system note if a branch was deleted/added
|
||||||
|
if branch_added? || branch_removed?
|
||||||
comment_mr_branch_presence_changed
|
comment_mr_branch_presence_changed
|
||||||
comment_mr_with_commits if @commits.present?
|
comment_mr_with_commits
|
||||||
else
|
else
|
||||||
@commits = @project.repository.commits_between(oldrev, newrev)
|
|
||||||
comment_mr_with_commits
|
comment_mr_with_commits
|
||||||
close_merge_requests
|
close_merge_requests
|
||||||
end
|
end
|
||||||
|
|
||||||
reload_merge_requests
|
|
||||||
execute_mr_web_hooks
|
execute_mr_web_hooks
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
@ -54,7 +53,7 @@ module MergeRequests
|
||||||
# Note: we should update merge requests from forks too
|
# Note: we should update merge requests from forks too
|
||||||
def reload_merge_requests
|
def reload_merge_requests
|
||||||
merge_requests = @project.merge_requests.opened.by_branch(@branch_name).to_a
|
merge_requests = @project.merge_requests.opened.by_branch(@branch_name).to_a
|
||||||
merge_requests += @fork_merge_requests.by_branch(@branch_name).to_a
|
merge_requests += fork_merge_requests.by_branch(@branch_name).to_a
|
||||||
merge_requests = filter_merge_requests(merge_requests)
|
merge_requests = filter_merge_requests(merge_requests)
|
||||||
|
|
||||||
merge_requests.each do |merge_request|
|
merge_requests.each do |merge_request|
|
||||||
|
|
@ -77,29 +76,37 @@ module MergeRequests
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add comment about branches being deleted or added to merge requests
|
def find_new_commits
|
||||||
def comment_mr_branch_presence_changed
|
if branch_added?
|
||||||
presence = Gitlab::Git.blank_ref?(@oldrev) ? :add : :delete
|
@commits = []
|
||||||
|
|
||||||
|
merge_request = merge_requests_for_source_branch.first
|
||||||
|
return unless merge_request
|
||||||
|
|
||||||
merge_requests_for_source_branch.each do |merge_request|
|
|
||||||
last_commit = merge_request.last_commit
|
last_commit = merge_request.last_commit
|
||||||
|
|
||||||
# Only look at changed commits in restore branch case
|
begin
|
||||||
unless Gitlab::Git.blank_ref?(@newrev)
|
# Since any number of commits could have been made to the restored branch,
|
||||||
begin
|
# find the common root to see what has been added.
|
||||||
# Since any number of commits could have been made to the restored branch,
|
common_ref = @project.repository.merge_base(last_commit.id, @newrev)
|
||||||
# find the common root to see what has been added.
|
# If the a commit no longer exists in this repo, gitlab_git throws
|
||||||
common_ref = @project.repository.merge_base(last_commit.id, @newrev)
|
# a Rugged::OdbError. This is fixed in https://gitlab.com/gitlab-org/gitlab_git/merge_requests/52
|
||||||
# If the a commit no longer exists in this repo, gitlab_git throws
|
@commits = @project.repository.commits_between(common_ref, @newrev) if common_ref
|
||||||
# a Rugged::OdbError. This is fixed in https://gitlab.com/gitlab-org/gitlab_git/merge_requests/52
|
rescue
|
||||||
@commits = @project.repository.commits_between(common_ref, @newrev) if common_ref
|
|
||||||
rescue
|
|
||||||
end
|
|
||||||
|
|
||||||
# Prevent system notes from seeing a blank SHA
|
|
||||||
@oldrev = nil
|
|
||||||
end
|
end
|
||||||
|
elsif branch_removed?
|
||||||
|
# No commits for a deleted branch.
|
||||||
|
@commits = []
|
||||||
|
else
|
||||||
|
@commits = @project.repository.commits_between(@oldrev, @newrev)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add comment about branches being deleted or added to merge requests
|
||||||
|
def comment_mr_branch_presence_changed
|
||||||
|
presence = branch_added? ? :add : :delete
|
||||||
|
|
||||||
|
merge_requests_for_source_branch.each do |merge_request|
|
||||||
SystemNoteService.change_branch_presence(
|
SystemNoteService.change_branch_presence(
|
||||||
merge_request, merge_request.project, @current_user,
|
merge_request, merge_request.project, @current_user,
|
||||||
:source, @branch_name, presence)
|
:source, @branch_name, presence)
|
||||||
|
|
@ -108,6 +115,8 @@ module MergeRequests
|
||||||
|
|
||||||
# Add comment about pushing new commits to merge requests
|
# Add comment about pushing new commits to merge requests
|
||||||
def comment_mr_with_commits
|
def comment_mr_with_commits
|
||||||
|
return unless @commits.present?
|
||||||
|
|
||||||
merge_requests_for_source_branch.each do |merge_request|
|
merge_requests_for_source_branch.each do |merge_request|
|
||||||
mr_commit_ids = Set.new(merge_request.commits.map(&:id))
|
mr_commit_ids = Set.new(merge_request.commits.map(&:id))
|
||||||
|
|
||||||
|
|
@ -135,9 +144,21 @@ module MergeRequests
|
||||||
def merge_requests_for_source_branch
|
def merge_requests_for_source_branch
|
||||||
@source_merge_requests ||= begin
|
@source_merge_requests ||= begin
|
||||||
merge_requests = @project.origin_merge_requests.opened.where(source_branch: @branch_name).to_a
|
merge_requests = @project.origin_merge_requests.opened.where(source_branch: @branch_name).to_a
|
||||||
merge_requests += @fork_merge_requests.where(source_branch: @branch_name).to_a
|
merge_requests += fork_merge_requests.where(source_branch: @branch_name).to_a
|
||||||
filter_merge_requests(merge_requests)
|
filter_merge_requests(merge_requests)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fork_merge_requests
|
||||||
|
@fork_merge_requests ||= @project.fork_merge_requests.opened
|
||||||
|
end
|
||||||
|
|
||||||
|
def branch_added?
|
||||||
|
Gitlab::Git.blank_ref?(@oldrev)
|
||||||
|
end
|
||||||
|
|
||||||
|
def branch_removed?
|
||||||
|
Gitlab::Git.blank_ref?(@newrev)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -327,7 +327,7 @@ class SystemNoteService
|
||||||
commit_ids = if count == 1
|
commit_ids = if count == 1
|
||||||
existing_commits.first.short_id
|
existing_commits.first.short_id
|
||||||
else
|
else
|
||||||
if oldrev
|
if oldrev && !Gitlab::Git.blank_ref?(oldrev)
|
||||||
"#{Commit.truncate_sha(oldrev)}...#{existing_commits.last.short_id}"
|
"#{Commit.truncate_sha(oldrev)}...#{existing_commits.last.short_id}"
|
||||||
else
|
else
|
||||||
"#{existing_commits.first.short_id}..#{existing_commits.last.short_id}"
|
"#{existing_commits.first.short_id}..#{existing_commits.last.short_id}"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
%p.lead
|
%p.lead
|
||||||
To register new runner visit #{link_to 'this page ', ci_runners_path}
|
To register a new runner visit #{link_to 'this page ', ci_runners_path}
|
||||||
|
|
||||||
.row
|
.row
|
||||||
.col-md-8
|
.col-md-8
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
%p.lead
|
%p.lead
|
||||||
%span To register new runner you should enter the following registration token. With this token the runner will request a unique runner token and use that for future communication.
|
%span To register a new runner you should enter the following registration token. With this token the runner will request a unique runner token and use that for future communication.
|
||||||
%code #{GitlabCi::REGISTRATION_TOKEN}
|
%code #{GitlabCi::REGISTRATION_TOKEN}
|
||||||
|
|
||||||
.bs-callout
|
.bs-callout
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
\- run builds from assigned projects
|
\- run builds from assigned projects
|
||||||
%li
|
%li
|
||||||
%span.label.label-danger paused
|
%span.label.label-danger paused
|
||||||
\- runner will not receive any new build
|
\- runner will not receive any new builds
|
||||||
|
|
||||||
.append-bottom-20.clearfix
|
.append-bottom-20.clearfix
|
||||||
.pull-left
|
.pull-left
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,13 @@
|
||||||
|
|
||||||
- if @runner.shared?
|
- if @runner.shared?
|
||||||
.bs-callout.bs-callout-success
|
.bs-callout.bs-callout-success
|
||||||
%h4 This runner will process build from ALL UNASSIGNED projects
|
%h4 This runner will process builds from ALL UNASSIGNED projects
|
||||||
%p
|
%p
|
||||||
If you want runners to build only specific projects, enable them in the table below.
|
If you want runners to build only specific projects, enable them in the table below.
|
||||||
Keep in mind that this is a one way transition.
|
Keep in mind that this is a one way transition.
|
||||||
- else
|
- else
|
||||||
.bs-callout.bs-callout-info
|
.bs-callout.bs-callout-info
|
||||||
%h4 This runner will process build only from ASSIGNED projects
|
%h4 This runner will process builds only from ASSIGNED projects
|
||||||
%p You can't make this a shared runner.
|
%p You can't make this a shared runner.
|
||||||
%hr
|
%hr
|
||||||
= form_for @runner, url: ci_admin_runner_path(@runner), html: { class: 'form-horizontal' } do |f|
|
= form_for @runner, url: ci_admin_runner_path(@runner), html: { class: 'form-horizontal' } do |f|
|
||||||
|
|
@ -53,13 +53,14 @@
|
||||||
%th
|
%th
|
||||||
- @runner.runner_projects.each do |runner_project|
|
- @runner.runner_projects.each do |runner_project|
|
||||||
- project = runner_project.project
|
- project = runner_project.project
|
||||||
%tr.alert-info
|
- if project.gl_project
|
||||||
%td
|
%tr.alert-info
|
||||||
%strong
|
%td
|
||||||
= project.name
|
%strong
|
||||||
%td
|
= project.name
|
||||||
.pull-right
|
%td
|
||||||
= link_to 'Disable', [:ci, :admin, project, runner_project], method: :delete, class: 'btn btn-danger btn-xs'
|
.pull-right
|
||||||
|
= link_to 'Disable', [:ci, :admin, project, runner_project], method: :delete, class: 'btn btn-danger btn-xs'
|
||||||
|
|
||||||
%table.table
|
%table.table
|
||||||
%thead
|
%thead
|
||||||
|
|
@ -103,21 +104,26 @@
|
||||||
%th Finished at
|
%th Finished at
|
||||||
|
|
||||||
- @builds.each do |build|
|
- @builds.each do |build|
|
||||||
|
- gl_project = build.gl_project
|
||||||
%tr.build
|
%tr.build
|
||||||
%td.id
|
%td.id
|
||||||
- gl_project = build.project.gl_project
|
- if gl_project
|
||||||
= link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do
|
= link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do
|
||||||
|
= build.id
|
||||||
|
- else
|
||||||
= build.id
|
= build.id
|
||||||
|
|
||||||
%td.status
|
%td.status
|
||||||
= ci_status_with_icon(build.status)
|
= ci_status_with_icon(build.status)
|
||||||
|
|
||||||
%td.status
|
%td.status
|
||||||
= build.project.name
|
- if gl_project
|
||||||
|
= gl_project.name_with_namespace
|
||||||
|
|
||||||
%td.build-link
|
%td.build-link
|
||||||
= link_to ci_status_path(build.commit) do
|
- if gl_project
|
||||||
%strong #{build.commit.short_sha}
|
= link_to ci_status_path(build.commit) do
|
||||||
|
%strong #{build.commit.short_sha}
|
||||||
|
|
||||||
%td.timestamp
|
%td.timestamp
|
||||||
- if build.finished_at
|
- if build.finished_at
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
%td #{stage.capitalize} Job - #{build[:name]}
|
%td #{stage.capitalize} Job - #{build[:name]}
|
||||||
%td
|
%td
|
||||||
%pre
|
%pre
|
||||||
= simple_format build[:script]
|
= simple_format build[:commands]
|
||||||
|
|
||||||
%br
|
%br
|
||||||
%b Tag list:
|
%b Tag list:
|
||||||
|
|
@ -28,6 +28,11 @@
|
||||||
%br
|
%br
|
||||||
%b Refs except:
|
%b Refs except:
|
||||||
= build[:except] && build[:except].join(", ")
|
= build[:except] && build[:except].join(", ")
|
||||||
|
%br
|
||||||
|
%b When:
|
||||||
|
= build[:when]
|
||||||
|
- if build[:allow_failure]
|
||||||
|
%b Allowed to fail
|
||||||
|
|
||||||
-else
|
-else
|
||||||
%p
|
%p
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
.login-block
|
.login-block
|
||||||
%h2 Login using GitLab account
|
%h2 Login using GitLab account
|
||||||
%p.light
|
%p.light
|
||||||
Make sure you have account on GitLab server
|
Make sure you have an account on the GitLab server
|
||||||
= link_to GitlabCi.config.gitlab_server.url, GitlabCi.config.gitlab_server.url, no_turbolink
|
= link_to GitlabCi.config.gitlab_server.url, GitlabCi.config.gitlab_server.url, no_turbolink
|
||||||
%hr
|
%hr
|
||||||
= link_to "Login with GitLab", auth_ci_user_sessions_path(state: params[:state]), no_turbolink.merge( class: 'btn btn-login btn-success' )
|
= link_to "Login with GitLab", auth_ci_user_sessions_path(state: params[:state]), no_turbolink.merge( class: 'btn btn-login btn-success' )
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,33 +6,29 @@
|
||||||
.gray-content-block
|
.gray-content-block
|
||||||
.pull-right
|
.pull-right
|
||||||
= link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do
|
= link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do
|
||||||
Add new snippet
|
= icon('plus')
|
||||||
|
New Snippet
|
||||||
|
|
||||||
.oneline
|
.btn-group.btn-group-next.snippet-scope-menu
|
||||||
Share code pastes with others out of git repository
|
= link_to dashboard_snippets_path, class: "btn btn-default #{"active" unless params[:scope]}" do
|
||||||
|
|
||||||
%ul.nav.nav-tabs.prepend-top-20
|
|
||||||
= nav_tab :scope, nil do
|
|
||||||
= link_to dashboard_snippets_path do
|
|
||||||
All
|
All
|
||||||
%span.badge
|
%span.badge
|
||||||
= current_user.snippets.count
|
= current_user.snippets.count
|
||||||
= nav_tab :scope, 'are_private' do
|
|
||||||
= link_to dashboard_snippets_path(scope: 'are_private') do
|
= link_to dashboard_snippets_path(scope: 'are_private'), class: "btn btn-default #{"active" if params[:scope] == "are_private"}" do
|
||||||
Private
|
Private
|
||||||
%span.badge
|
%span.badge
|
||||||
= current_user.snippets.are_private.count
|
= current_user.snippets.are_private.count
|
||||||
= nav_tab :scope, 'are_internal' do
|
|
||||||
= link_to dashboard_snippets_path(scope: 'are_internal') do
|
= link_to dashboard_snippets_path(scope: 'are_internal'), class: "btn btn-default #{"active" if params[:scope] == "are_internal"}" do
|
||||||
Internal
|
Internal
|
||||||
%span.badge
|
%span.badge
|
||||||
= current_user.snippets.are_internal.count
|
= current_user.snippets.are_internal.count
|
||||||
= nav_tab :scope, 'are_public' do
|
|
||||||
= link_to dashboard_snippets_path(scope: 'are_public') do
|
= link_to dashboard_snippets_path(scope: 'are_public'), class: "btn btn-default #{"active" if params[:scope] == "are_public"}" do
|
||||||
Public
|
Public
|
||||||
%span.badge
|
%span.badge
|
||||||
= current_user.snippets.are_public.count
|
= current_user.snippets.are_public.count
|
||||||
|
|
||||||
.my-snippets
|
= render 'snippets/snippets'
|
||||||
= render 'snippets/snippets'
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@
|
||||||
- if current_user
|
- if current_user
|
||||||
.pull-right
|
.pull-right
|
||||||
= link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do
|
= link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do
|
||||||
Add new snippet
|
= icon('plus')
|
||||||
|
New Snippet
|
||||||
|
|
||||||
.oneline
|
.oneline
|
||||||
Public snippets created by you and other users are listed here
|
Public snippets created by you and other users are listed here
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
- if readme = @repository.readme
|
- if readme = @repository.readme
|
||||||
%article.file-holder.readme-holder
|
%article.readme-holder
|
||||||
.file-title
|
.pull-right
|
||||||
= blob_icon readme.mode, readme.name
|
- if can?(current_user, :push_code, @project)
|
||||||
= link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)) do
|
= link_to icon('pencil'), namespace_project_edit_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)), class: 'light edit-project-readme'
|
||||||
%strong
|
|
||||||
= readme.name
|
|
||||||
.file-content.wiki
|
.file-content.wiki
|
||||||
= cache(readme_cache_key) do
|
= cache(readme_cache_key) do
|
||||||
= render_readme(readme)
|
= render_readme(readme)
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@
|
||||||
|
|
||||||
- if @builds.present?
|
- if @builds.present?
|
||||||
.build-widget
|
.build-widget
|
||||||
%h4.title #{pluralize(@builds.count, "other build")} for #{@build.short_sha}:
|
%h4.title #{pluralize(@builds.count(:id), "other build")} for #{@build.short_sha}:
|
||||||
%table.table.builds
|
%table.table.builds
|
||||||
- @builds.each_with_index do |build, i|
|
- @builds.each_with_index do |build, i|
|
||||||
%tr.build
|
%tr.build
|
||||||
|
|
@ -175,4 +175,4 @@
|
||||||
|
|
||||||
|
|
||||||
:javascript
|
:javascript
|
||||||
new CiBuild("#{namespace_project_build_path(@project.namespace, @project, @build)}", "#{@build.status}")
|
new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{@build.status}")
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
= hidden_field_tag :notification_id, @membership.id
|
= hidden_field_tag :notification_id, @membership.id
|
||||||
= hidden_field_tag :notification_level
|
= hidden_field_tag :notification_level
|
||||||
%span.dropdown
|
%span.dropdown
|
||||||
%a.dropdown-new.btn.btn-new#notifications-button{href: '#', "data-toggle" => "dropdown"}
|
%a.dropdown-new.btn.notifications-btn#notifications-button{href: '#', "data-toggle" => "dropdown"}
|
||||||
= icon('bell')
|
= icon('bell')
|
||||||
= notification_label(@membership)
|
= notification_label(@membership)
|
||||||
= icon('angle-down')
|
= icon('angle-down')
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
= notification_list_item(level, @membership)
|
= notification_list_item(level, @membership)
|
||||||
|
|
||||||
- when GroupMember
|
- when GroupMember
|
||||||
.btn.btn-new.disabled.has_tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."}
|
.btn.disabled.notifications-btn.has_tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."}
|
||||||
= icon('bell')
|
= icon('bell')
|
||||||
= notification_label(@membership)
|
= notification_label(@membership)
|
||||||
= icon('angle-down')
|
= icon('angle-down')
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,4 @@
|
||||||
You can add Specific runner for this project on Runners page
|
You can add Specific runner for this project on Runners page
|
||||||
|
|
||||||
- if current_user.admin
|
- if current_user.admin
|
||||||
or add Shared runner for whole application in admin are.
|
or add Shared runner for whole application in admin area.
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,10 @@
|
||||||
|
|
||||||
.pull-right
|
.pull-right
|
||||||
- if ci_commit
|
- if ci_commit
|
||||||
= link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}" do
|
= render_ci_status(ci_commit)
|
||||||
= ci_status_icon(ci_commit)
|
|
||||||
|
|
||||||
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
|
= clipboard_button
|
||||||
|
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id", data: {clipboard_text: commit.id}
|
||||||
|
|
||||||
.notes_count
|
.notes_count
|
||||||
- if note_count > 0
|
- if note_count > 0
|
||||||
|
|
|
||||||
|
|
@ -2,53 +2,56 @@
|
||||||
- if current_user && can?(current_user, :download_code, @project)
|
- if current_user && can?(current_user, :download_code, @project)
|
||||||
= render 'shared/no_ssh'
|
= render 'shared/no_ssh'
|
||||||
= render 'shared/no_password'
|
= render 'shared/no_password'
|
||||||
|
|
||||||
= render "home_panel"
|
= render "home_panel"
|
||||||
|
|
||||||
.gray-content-block.center
|
.gray-content-block.center
|
||||||
%h3.page-title
|
%h3.page-title
|
||||||
The repository for this project is empty
|
The repository for this project is empty
|
||||||
%p
|
- if can?(current_user, :download_code, @project)
|
||||||
If you already have files you can push them using command line instructions below.
|
%p
|
||||||
%br
|
If you already have files you can push them using command line instructions below.
|
||||||
Otherwise you can start with
|
%br
|
||||||
= link_to "adding README", new_readme_path, class: 'underlined-link'
|
- if can?(current_user, :push_code, @project)
|
||||||
file to this project.
|
Otherwise you can start with
|
||||||
|
= link_to "adding README", new_readme_path, class: 'underlined-link'
|
||||||
|
file to this project.
|
||||||
|
|
||||||
.prepend-top-20
|
- if can?(current_user, :download_code, @project)
|
||||||
.empty_wrapper
|
.prepend-top-20
|
||||||
%h3.page-title-empty
|
.empty_wrapper
|
||||||
Command line instructions
|
%h3.page-title-empty
|
||||||
%div.git-empty
|
Command line instructions
|
||||||
%fieldset
|
%div.git-empty
|
||||||
%h5 Git global setup
|
%fieldset
|
||||||
%pre.light-well
|
%h5 Git global setup
|
||||||
:preserve
|
%pre.light-well
|
||||||
git config --global user.name "#{h git_user_name}"
|
:preserve
|
||||||
git config --global user.email "#{h git_user_email}"
|
git config --global user.name "#{h git_user_name}"
|
||||||
|
git config --global user.email "#{h git_user_email}"
|
||||||
|
|
||||||
%fieldset
|
%fieldset
|
||||||
%h5 Create a new repository
|
%h5 Create a new repository
|
||||||
%pre.light-well
|
%pre.light-well
|
||||||
:preserve
|
:preserve
|
||||||
git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')}
|
git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')}
|
||||||
cd #{h @project.path}
|
cd #{h @project.path}
|
||||||
touch README.md
|
touch README.md
|
||||||
git add README.md
|
git add README.md
|
||||||
git commit -m "add README"
|
git commit -m "add README"
|
||||||
git push -u origin master
|
git push -u origin master
|
||||||
|
|
||||||
%fieldset
|
%fieldset
|
||||||
%h5 Existing folder or Git repository
|
%h5 Existing folder or Git repository
|
||||||
%pre.light-well
|
%pre.light-well
|
||||||
:preserve
|
:preserve
|
||||||
cd existing_folder
|
cd existing_folder
|
||||||
git init
|
git init
|
||||||
git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')}
|
git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')}
|
||||||
git add .
|
git add .
|
||||||
git commit
|
git commit
|
||||||
git push -u origin master
|
git push -u origin master
|
||||||
|
|
||||||
- if can? current_user, :remove_project, @project
|
- if can? current_user, :remove_project, @project
|
||||||
.prepend-top-20
|
.prepend-top-20
|
||||||
= link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
|
= link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,10 @@
|
||||||
- @participants.each do |participant|
|
- @participants.each do |participant|
|
||||||
= link_to_member(@project, participant, name: false, size: 24)
|
= link_to_member(@project, participant, name: false, size: 24)
|
||||||
.col-md-3
|
.col-md-3
|
||||||
%span.slead.has_tooltip{title: 'Cross-project reference'}
|
.input-group.cross-project-reference
|
||||||
= cross_project_reference(@project, @issue)
|
%span.slead.has_tooltip{title: 'Cross-project reference'}
|
||||||
|
= cross_project_reference(@project, @issue)
|
||||||
|
= clipboard_button
|
||||||
|
|
||||||
.row
|
.row
|
||||||
%section.col-md-9
|
%section.col-md-9
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@
|
||||||
.nothing-here-block No issues to show
|
.nothing-here-block No issues to show
|
||||||
|
|
||||||
- if @issues.present?
|
- if @issues.present?
|
||||||
.pull-right
|
.issuable-filter-count
|
||||||
%span.issue_counter #{@issues.total_count}
|
%span.pull-right
|
||||||
issues for this filter
|
= @issues.total_count
|
||||||
|
issues for this filter
|
||||||
|
|
||||||
= paginate @issues, theme: "gitlab"
|
= paginate @issues, theme: "gitlab"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
- ci_commit = merge_request.ci_commit
|
||||||
%li{ class: mr_css_classes(merge_request) }
|
%li{ class: mr_css_classes(merge_request) }
|
||||||
.merge-request-title
|
.merge-request-title
|
||||||
%span.merge-request-title-text
|
%span.merge-request-title-text
|
||||||
|
|
@ -6,6 +7,8 @@
|
||||||
- merge_request.labels.each do |label|
|
- merge_request.labels.each do |label|
|
||||||
= link_to_label(label, project: merge_request.project)
|
= link_to_label(label, project: merge_request.project)
|
||||||
.pull-right.light
|
.pull-right.light
|
||||||
|
- if ci_commit
|
||||||
|
= render_ci_status(ci_commit)
|
||||||
- if merge_request.merged?
|
- if merge_request.merged?
|
||||||
%span
|
%span
|
||||||
%i.fa.fa-check
|
%i.fa.fa-check
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,10 @@
|
||||||
.nothing-here-block No merge requests to show
|
.nothing-here-block No merge requests to show
|
||||||
|
|
||||||
- if @merge_requests.present?
|
- if @merge_requests.present?
|
||||||
.pull-right
|
.issuable-filter-count
|
||||||
%span.cgray.pull-right #{@merge_requests.total_count} merge requests for this filter
|
%span.pull-right
|
||||||
|
= @merge_requests.total_count
|
||||||
|
merge requests for this filter
|
||||||
|
|
||||||
= paginate @merge_requests, theme: "gitlab"
|
= paginate @merge_requests, theme: "gitlab"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,12 @@
|
||||||
.modal-content
|
.modal-content
|
||||||
.modal-header
|
.modal-header
|
||||||
%a.close{href: "#", "data-dismiss" => "modal"} ×
|
%a.close{href: "#", "data-dismiss" => "modal"} ×
|
||||||
%h3 Check out, review and merge locally
|
%h3 Check out, review, and merge locally
|
||||||
.modal-body
|
.modal-body
|
||||||
%p
|
%p
|
||||||
%strong Step 1.
|
%strong Step 1.
|
||||||
Fetch and check out the branch for this merge request
|
Fetch and check out the branch for this merge request
|
||||||
|
= clipboard_button
|
||||||
%pre.dark
|
%pre.dark
|
||||||
- if @merge_request.for_fork?
|
- if @merge_request.for_fork?
|
||||||
:preserve
|
:preserve
|
||||||
|
|
@ -24,6 +25,7 @@
|
||||||
%p
|
%p
|
||||||
%strong Step 3.
|
%strong Step 3.
|
||||||
Merge the branch and fix any conflicts that come up
|
Merge the branch and fix any conflicts that come up
|
||||||
|
= clipboard_button
|
||||||
%pre.dark
|
%pre.dark
|
||||||
- if @merge_request.for_fork?
|
- if @merge_request.for_fork?
|
||||||
:preserve
|
:preserve
|
||||||
|
|
@ -36,6 +38,7 @@
|
||||||
%p
|
%p
|
||||||
%strong Step 4.
|
%strong Step 4.
|
||||||
Push the result of the merge to GitLab
|
Push the result of the merge to GitLab
|
||||||
|
= clipboard_button
|
||||||
%pre.dark
|
%pre.dark
|
||||||
:preserve
|
:preserve
|
||||||
git push origin #{h @merge_request.target_branch}
|
git push origin #{h @merge_request.target_branch}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
- ci_commit = @merge_request.source_project.ci_commit(@merge_request.source_sha)
|
- ci_commit = @merge_request.ci_commit
|
||||||
- if ci_commit
|
- if ci_commit
|
||||||
- status = ci_commit.status
|
- status = ci_commit.status
|
||||||
.mr-widget-heading
|
.mr-widget-heading
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do
|
||||||
|
= icon('plus')
|
||||||
|
New Snippet
|
||||||
|
- if can?(current_user, :admin_project_snippet, @snippet)
|
||||||
|
= link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' do
|
||||||
|
= icon('trash-o')
|
||||||
|
Delete
|
||||||
|
- if can?(current_user, :update_project_snippet, @snippet)
|
||||||
|
= link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do
|
||||||
|
= icon('pencil-square-o')
|
||||||
|
Edit
|
||||||
|
|
@ -1,17 +1,13 @@
|
||||||
- page_title "Snippets"
|
- page_title "Snippets"
|
||||||
= render "header_title"
|
= render "header_title"
|
||||||
|
|
||||||
%h3.page-title
|
.gray-content-block.top-block
|
||||||
Snippets
|
.pull-right
|
||||||
- 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 pull-right", title: "New Snippet" do
|
= icon('plus')
|
||||||
Add new snippet
|
New Snippet
|
||||||
|
|
||||||
%p.light
|
.oneline
|
||||||
Share code pastes with others out of git repository
|
Share code pastes with others out of git repository
|
||||||
|
|
||||||
%ul.bordered-list
|
= render 'snippets/snippets'
|
||||||
= render partial: "shared/snippets/snippet", collection: @snippets
|
|
||||||
- if @snippets.empty?
|
|
||||||
%li
|
|
||||||
.nothing-here-block Nothing here.
|
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,18 @@
|
||||||
- page_title @snippet.title, "Snippets"
|
- page_title @snippet.title, "Snippets"
|
||||||
= render "header_title"
|
= render "header_title"
|
||||||
|
|
||||||
%h3.page-title
|
.snippet-holder
|
||||||
= @snippet.title
|
= render 'shared/snippets/header'
|
||||||
|
|
||||||
.pull-right
|
%article.file-holder
|
||||||
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do
|
.file-title
|
||||||
Add new snippet
|
= blob_icon 0, @snippet.file_name
|
||||||
|
%strong
|
||||||
|
= @snippet.file_name
|
||||||
|
.file-actions.hidden-xs
|
||||||
|
.btn-group.tree-btn-group
|
||||||
|
= link_to 'Raw', raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank"
|
||||||
|
|
||||||
%hr
|
= render 'shared/snippets/blob'
|
||||||
|
|
||||||
.append-bottom-20
|
%div#notes= render "projects/notes/notes_with_form"
|
||||||
.pull-right
|
|
||||||
= "##{@snippet.id}"
|
|
||||||
%span.light
|
|
||||||
by
|
|
||||||
= link_to user_path(@snippet.author) do
|
|
||||||
= image_tag avatar_icon(@snippet.author_email), class: "avatar avatar-inline s16"
|
|
||||||
= @snippet.author_name
|
|
||||||
|
|
||||||
.back-link
|
|
||||||
= link_to namespace_project_snippets_path(@project.namespace, @project) do
|
|
||||||
← project snippets
|
|
||||||
|
|
||||||
.file-holder
|
|
||||||
.file-title
|
|
||||||
%i.fa.fa-file
|
|
||||||
%strong
|
|
||||||
= @snippet.file_name
|
|
||||||
.file-actions
|
|
||||||
.btn-group
|
|
||||||
- if can?(current_user, :update_project_snippet, @snippet)
|
|
||||||
= link_to "edit", edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", title: 'Edit Snippet'
|
|
||||||
= link_to "raw", raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank"
|
|
||||||
- if can?(current_user, :admin_project_snippet, @snippet)
|
|
||||||
= link_to "remove", namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-sm btn-remove", title: 'Delete Snippet'
|
|
||||||
= render 'shared/snippets/blob'
|
|
||||||
|
|
||||||
%div#notes= render "projects/notes/notes_with_form"
|
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,7 @@
|
||||||
.project-controls
|
.project-controls
|
||||||
- if ci && !project.empty_repo? && project.commit
|
- if ci && !project.empty_repo? && project.commit
|
||||||
- if ci_commit = project.ci_commit(project.commit.sha)
|
- if ci_commit = project.ci_commit(project.commit.sha)
|
||||||
= link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}",
|
= render_ci_status(ci_commit)
|
||||||
title: "Build status: #{ci_commit.status}", data: {toggle: 'tooltip', placement: 'left'} do
|
|
||||||
= ci_status_icon(ci_commit)
|
|
||||||
|
|
||||||
- if stars
|
- if stars
|
||||||
%span
|
%span
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
.snippet-details
|
||||||
|
.page-title
|
||||||
|
.snippet-box{class: visibility_level_color(@snippet.visibility_level)}
|
||||||
|
= visibility_level_icon(@snippet.visibility_level)
|
||||||
|
= visibility_level_label(@snippet.visibility_level)
|
||||||
|
%span.snippet-id Snippet ##{@snippet.id}
|
||||||
|
%span.creator
|
||||||
|
· created by #{link_to_member(@project, @snippet.author, size: 24)}
|
||||||
|
·
|
||||||
|
= time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago')
|
||||||
|
- if @snippet.updated_at != @snippet.created_at
|
||||||
|
%span
|
||||||
|
·
|
||||||
|
= icon('edit', title: 'edited')
|
||||||
|
= time_ago_with_tooltip(@snippet.updated_at, placement: 'bottom', html_class: 'snippet_edited_ago')
|
||||||
|
|
||||||
|
.pull-right
|
||||||
|
- if @snippet.project_id?
|
||||||
|
= render "projects/snippets/actions"
|
||||||
|
- else
|
||||||
|
= render "snippets/actions"
|
||||||
|
.gray-content-block.middle-block
|
||||||
|
%h2.snippet-title
|
||||||
|
= gfm escape_once(@snippet.title)
|
||||||
|
|
@ -18,4 +18,3 @@
|
||||||
= image_tag avatar_icon(snippet.author_email), class: "avatar s24", alt: ''
|
= image_tag avatar_icon(snippet.author_email), class: "avatar s24", alt: ''
|
||||||
= snippet.author_name
|
= snippet.author_name
|
||||||
authored #{time_ago_with_tooltip(snippet.created_at)}
|
authored #{time_ago_with_tooltip(snippet.created_at)}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
= link_to new_snippet_path, class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do
|
||||||
|
= icon('plus')
|
||||||
|
New Snippet
|
||||||
|
- if can?(current_user, :admin_personal_snippet, @snippet)
|
||||||
|
= link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' do
|
||||||
|
= icon('trash-o')
|
||||||
|
Delete
|
||||||
|
- if can?(current_user, :update_personal_snippet, @snippet)
|
||||||
|
= link_to edit_snippet_path(@snippet), class: "btn btn-grouped snippable-edit" do
|
||||||
|
= icon('pencil-square-o')
|
||||||
|
Edit
|
||||||
|
|
@ -1,41 +1,14 @@
|
||||||
- page_title @snippet.title, "Snippets"
|
- page_title @snippet.title, "Snippets"
|
||||||
%h4.page-title
|
|
||||||
= @snippet.title
|
|
||||||
|
|
||||||
- if @snippet.private?
|
.snippet-holder
|
||||||
%span.label.label-success
|
= render 'shared/snippets/header'
|
||||||
%i.fa.fa-lock
|
|
||||||
private
|
|
||||||
|
|
||||||
.pull-right
|
%article.file-holder
|
||||||
= link_to new_snippet_path, class: "btn btn-new btn-sm", title: "New Snippet" do
|
.file-title
|
||||||
Add new snippet
|
= blob_icon 0, @snippet.file_name
|
||||||
|
%strong
|
||||||
.append-bottom-10.prepend-top-10
|
= @snippet.file_name
|
||||||
.pull-right
|
.file-actions.hidden-xs
|
||||||
%span.light
|
.btn-group.tree-btn-group
|
||||||
created by
|
= link_to 'Raw', raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank"
|
||||||
= link_to user_snippets_path(@snippet.author) do
|
= render 'shared/snippets/blob'
|
||||||
= @snippet.author_name
|
|
||||||
|
|
||||||
.back-link
|
|
||||||
- if @snippet.author == current_user
|
|
||||||
= link_to dashboard_snippets_path do
|
|
||||||
← your snippets
|
|
||||||
- else
|
|
||||||
= link_to explore_snippets_path do
|
|
||||||
← explore snippets
|
|
||||||
|
|
||||||
.file-holder
|
|
||||||
.file-title
|
|
||||||
%i.fa.fa-file
|
|
||||||
%strong
|
|
||||||
= @snippet.file_name
|
|
||||||
.file-actions
|
|
||||||
.btn-group
|
|
||||||
- if can?(current_user, :update_personal_snippet, @snippet)
|
|
||||||
= link_to "edit", edit_snippet_path(@snippet), class: "btn btn-sm", title: 'Edit Snippet'
|
|
||||||
= link_to "raw", raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank"
|
|
||||||
- if can?(current_user, :admin_personal_snippet, @snippet)
|
|
||||||
= link_to "remove", snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-sm btn-remove", title: 'Delete Snippet'
|
|
||||||
= render 'shared/snippets/blob'
|
|
||||||
|
|
|
||||||
|
|
@ -318,10 +318,12 @@ production: &base
|
||||||
# ==========================
|
# ==========================
|
||||||
|
|
||||||
# GitLab Satellites
|
# GitLab Satellites
|
||||||
|
#
|
||||||
|
# Note for maintainers: keep the satellites.path setting until GitLab 9.0 at
|
||||||
|
# least. This setting is fed to 'rm -rf' in
|
||||||
|
# db/migrate/20151023144219_remove_satellites.rb
|
||||||
satellites:
|
satellites:
|
||||||
# Relative paths are relative to Rails.root (default: tmp/repo_satellites/)
|
|
||||||
path: /home/git/gitlab-satellites/
|
path: /home/git/gitlab-satellites/
|
||||||
timeout: 30
|
|
||||||
|
|
||||||
## Backup settings
|
## Backup settings
|
||||||
backup:
|
backup:
|
||||||
|
|
|
||||||
|
|
@ -242,9 +242,11 @@ Settings.git['max_size'] ||= 20971520 # 20.megabytes
|
||||||
Settings.git['bin_path'] ||= '/usr/bin/git'
|
Settings.git['bin_path'] ||= '/usr/bin/git'
|
||||||
Settings.git['timeout'] ||= 10
|
Settings.git['timeout'] ||= 10
|
||||||
|
|
||||||
|
# Important: keep the satellites.path setting until GitLab 9.0 at
|
||||||
|
# least. This setting is fed to 'rm -rf' in
|
||||||
|
# db/migrate/20151023144219_remove_satellites.rb
|
||||||
Settings['satellites'] ||= Settingslogic.new({})
|
Settings['satellites'] ||= Settingslogic.new({})
|
||||||
Settings.satellites['path'] = File.expand_path(Settings.satellites['path'] || "tmp/repo_satellites/", Rails.root)
|
Settings.satellites['path'] = File.expand_path(Settings.satellites['path'] || "tmp/repo_satellites/", Rails.root)
|
||||||
Settings.satellites['timeout'] ||= 30
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Extra customization
|
# Extra customization
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
class FailBuildWithEmptyName < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
execute("UPDATE ci_builds SET status='failed' WHERE (name IS NULL OR name='') AND status='pending'")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
require 'fileutils'
|
||||||
|
|
||||||
|
class RemoveSatellites < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
satellites = Gitlab.config['satellites']
|
||||||
|
return if satellites.nil?
|
||||||
|
|
||||||
|
satellites_path = satellites['path']
|
||||||
|
return if satellites_path.nil?
|
||||||
|
|
||||||
|
FileUtils.rm_rf(satellites_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
# Do nothing
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
class AddProjectPathIndex < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
add_index :projects, :path
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_index :projects, :path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -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: 20151020173906) do
|
ActiveRecord::Schema.define(version: 20151026182941) 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"
|
||||||
|
|
@ -624,6 +624,7 @@ ActiveRecord::Schema.define(version: 20151020173906) do
|
||||||
add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree
|
add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree
|
||||||
add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree
|
add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree
|
||||||
add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree
|
add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree
|
||||||
|
add_index "projects", ["path"], name: "index_projects_on_path", using: :btree
|
||||||
add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree
|
add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree
|
||||||
|
|
||||||
create_table "protected_branches", force: true do |t|
|
create_table "protected_branches", force: true do |t|
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ you need to set MYSQL_ALLOW_EMPTY_PASSWORD.
|
||||||
- mysql
|
- mysql
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
|
||||||
```
|
```
|
||||||
|
|
||||||
For other possible configuration variables check the
|
For other possible configuration variables check the
|
||||||
|
|
|
||||||
|
|
@ -346,11 +346,6 @@ The `secrets.yml` file stores encryption keys for sessions and secure variables.
|
||||||
Backup `secrets.yml` someplace safe, but don't store it in the same place as your database backups.
|
Backup `secrets.yml` someplace safe, but don't store it in the same place as your database backups.
|
||||||
Otherwise your secrets are exposed if one of your backups is compromised.
|
Otherwise your secrets are exposed if one of your backups is compromised.
|
||||||
|
|
||||||
### Install schedules
|
|
||||||
|
|
||||||
# Setup schedules
|
|
||||||
sudo -u gitlab_ci -H bundle exec whenever -w RAILS_ENV=production
|
|
||||||
|
|
||||||
### Install Init Script
|
### Install Init Script
|
||||||
|
|
||||||
Download the init script (will be `/etc/init.d/gitlab`):
|
Download the init script (will be `/etc/init.d/gitlab`):
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
|
||||||
```
|
```
|
||||||
|
|
||||||
Also you can choose what should be backed up by adding environment variable SKIP. Available options: db,
|
Also you can choose what should be backed up by adding environment variable SKIP. Available options: db,
|
||||||
uploads (attachments), repositories. Use a comma to specify several options at the same time.
|
uploads (attachments), repositories, builds(CI build output logs). Use a comma to specify several options at the same time.
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo gitlab-rake gitlab:backup:create SKIP=db,uploads
|
sudo gitlab-rake gitlab:backup:create SKIP=db,uploads
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ After getting used to these three steps the branching model becomes the challeng
|
||||||
Since many organizations new to git have no conventions how to work with it, it can quickly become a mess.
|
Since many organizations new to git have no conventions how to work with it, it can quickly become a mess.
|
||||||
The biggest problem they run into is that many long running branches that each contain part of the changes are around.
|
The biggest problem they run into is that many long running branches that each contain part of the changes are around.
|
||||||
People have a hard time figuring out which branch they should develop on or deploy to production.
|
People have a hard time figuring out which branch they should develop on or deploy to production.
|
||||||
Frequently the reaction to this problem is to adopt a standardized pattern such as [git flow](http://nvie.com/posts/a-successful-git-branching-model/) and [GitHub flow](http://scottchacon.com/2011/08/31/github-flow.html)
|
Frequently the reaction to this problem is to adopt a standardized pattern such as [git flow](http://nvie.com/posts/a-successful-git-branching-model/) and [GitHub flow](http://scottchacon.com/2011/08/31/github-flow.html).
|
||||||
We think there is still room for improvement and will detail a set of practices we call GitLab flow.
|
We think there is still room for improvement and will detail a set of practices we call GitLab flow.
|
||||||
|
|
||||||
## Git flow and its problems
|
## Git flow and its problems
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,12 @@ Feature: Project Merge Requests
|
||||||
Then I should see "Bug NS-04" in merge requests
|
Then I should see "Bug NS-04" in merge requests
|
||||||
And I should not see "Feature NS-03" in merge requests
|
And I should not see "Feature NS-03" in merge requests
|
||||||
|
|
||||||
|
Scenario: I should see CI status for merge requests
|
||||||
|
Given project "Shop" have "Bug NS-05" open merge request with diffs inside
|
||||||
|
Given "Bug NS-05" has CI status
|
||||||
|
When I visit project "Shop" merge requests page
|
||||||
|
Then I should see merge request "Bug NS-05" with CI status
|
||||||
|
|
||||||
Scenario: I should see rejected merge requests
|
Scenario: I should see rejected merge requests
|
||||||
Given I click link "Closed"
|
Given I click link "Closed"
|
||||||
Then I should see "Feature NS-03" in merge requests
|
Then I should see "Feature NS-03" in merge requests
|
||||||
|
|
|
||||||
|
|
@ -30,5 +30,5 @@ Feature: Project Snippets
|
||||||
|
|
||||||
Scenario: I destroy "Snippet one"
|
Scenario: I destroy "Snippet one"
|
||||||
Given I visit snippet page "Snippet one"
|
Given I visit snippet page "Snippet one"
|
||||||
And I click link "Remove Snippet"
|
And I click link "Delete"
|
||||||
Then I should not see "Snippet one" in snippets
|
Then I should not see "Snippet one" in snippets
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ Feature: Snippets
|
||||||
|
|
||||||
Scenario: I destroy "Personal snippet one"
|
Scenario: I destroy "Personal snippet one"
|
||||||
Given I visit snippet page "Personal snippet one"
|
Given I visit snippet page "Personal snippet one"
|
||||||
And I click link "Destroy"
|
And I click link "Delete"
|
||||||
Then I should not see "Personal snippet one" in snippets
|
Then I should not see "Personal snippet one" in snippets
|
||||||
|
|
||||||
Scenario: I create new internal snippet
|
Scenario: I create new internal snippet
|
||||||
|
|
|
||||||
|
|
@ -338,6 +338,19 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
|
||||||
expect(page).to have_content('diff --git')
|
expect(page).to have_content('diff --git')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
step '"Bug NS-05" has CI status' do
|
||||||
|
project = merge_request.source_project
|
||||||
|
project.enable_ci
|
||||||
|
ci_commit = create :ci_commit, gl_project: project, sha: merge_request.last_commit.id
|
||||||
|
create :ci_build, commit: ci_commit
|
||||||
|
end
|
||||||
|
|
||||||
|
step 'I should see merge request "Bug NS-05" with CI status' do
|
||||||
|
page.within ".mr-list" do
|
||||||
|
expect(page).to have_link "Build status: pending"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def merge_request
|
def merge_request
|
||||||
@merge_request ||= MergeRequest.find_by!(title: "Bug NS-05")
|
@merge_request ||= MergeRequest.find_by!(title: "Bug NS-05")
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps
|
||||||
end
|
end
|
||||||
|
|
||||||
step 'I click link "New Snippet"' do
|
step 'I click link "New Snippet"' do
|
||||||
click_link "Add new snippet"
|
click_link "New Snippet"
|
||||||
end
|
end
|
||||||
|
|
||||||
step 'I click link "Snippet one"' do
|
step 'I click link "Snippet one"' do
|
||||||
|
|
@ -42,13 +42,13 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps
|
||||||
end
|
end
|
||||||
|
|
||||||
step 'I click link "Edit"' do
|
step 'I click link "Edit"' do
|
||||||
page.within ".file-title" do
|
page.within ".page-title" do
|
||||||
click_link "Edit"
|
click_link "Edit"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
step 'I click link "Remove Snippet"' do
|
step 'I click link "Delete"' do
|
||||||
click_link "remove"
|
click_link "Delete"
|
||||||
end
|
end
|
||||||
|
|
||||||
step 'I submit new snippet "Snippet three"' do
|
step 'I submit new snippet "Snippet three"' do
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,13 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps
|
||||||
end
|
end
|
||||||
|
|
||||||
step 'I click link "Edit"' do
|
step 'I click link "Edit"' do
|
||||||
page.within ".file-title" do
|
page.within ".page-title" do
|
||||||
click_link "Edit"
|
click_link "Edit"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
step 'I click link "Destroy"' do
|
step 'I click link "Delete"' do
|
||||||
click_link "remove"
|
click_link "Delete"
|
||||||
end
|
end
|
||||||
|
|
||||||
step 'I submit new snippet "Personal snippet three"' do
|
step 'I submit new snippet "Personal snippet three"' do
|
||||||
|
|
|
||||||
|
|
@ -32,19 +32,19 @@ class Spinach::Features::SnippetsUser < Spinach::FeatureSteps
|
||||||
end
|
end
|
||||||
|
|
||||||
step 'I click "Internal" filter' do
|
step 'I click "Internal" filter' do
|
||||||
page.within('.nav-tabs') do
|
page.within('.snippet-scope-menu') do
|
||||||
click_link "Internal"
|
click_link "Internal"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
step 'I click "Private" filter' do
|
step 'I click "Private" filter' do
|
||||||
page.within('.nav-tabs') do
|
page.within('.snippet-scope-menu') do
|
||||||
click_link "Private"
|
click_link "Private"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
step 'I click "Public" filter' do
|
step 'I click "Public" filter' do
|
||||||
page.within('.nav-tabs') do
|
page.within('.snippet-scope-menu') do
|
||||||
click_link "Public"
|
click_link "Public"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ module API
|
||||||
format :json
|
format :json
|
||||||
content_type :txt, "text/plain"
|
content_type :txt, "text/plain"
|
||||||
|
|
||||||
helpers APIHelpers
|
helpers Helpers
|
||||||
|
|
||||||
mount Groups
|
mount Groups
|
||||||
mount GroupMembers
|
mount GroupMembers
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
module API
|
module API
|
||||||
module APIHelpers
|
module Helpers
|
||||||
PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN"
|
PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN"
|
||||||
PRIVATE_TOKEN_PARAM = :private_token
|
PRIVATE_TOKEN_PARAM = :private_token
|
||||||
SUDO_HEADER ="HTTP_SUDO"
|
SUDO_HEADER ="HTTP_SUDO"
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ module Ci
|
||||||
format :json
|
format :json
|
||||||
|
|
||||||
helpers Helpers
|
helpers Helpers
|
||||||
helpers ::API::APIHelpers
|
helpers ::API::Helpers
|
||||||
|
|
||||||
mount Builds
|
mount Builds
|
||||||
mount Commits
|
mount Commits
|
||||||
|
|
|
||||||
|
|
@ -139,66 +139,74 @@ module Ci
|
||||||
end
|
end
|
||||||
|
|
||||||
@jobs.each do |name, job|
|
@jobs.each do |name, job|
|
||||||
validate_job!("#{name} job", job)
|
validate_job!(name, job)
|
||||||
end
|
end
|
||||||
|
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_job!(name, job)
|
def validate_job!(name, job)
|
||||||
|
if name.blank? || !validate_string(name)
|
||||||
|
raise ValidationError, "job name should be non-empty string"
|
||||||
|
end
|
||||||
|
|
||||||
job.keys.each do |key|
|
job.keys.each do |key|
|
||||||
unless ALLOWED_JOB_KEYS.include? key
|
unless ALLOWED_JOB_KEYS.include? key
|
||||||
raise ValidationError, "#{name}: unknown parameter #{key}"
|
raise ValidationError, "#{name} job: unknown parameter #{key}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if !job[:script].is_a?(String) && !validate_array_of_strings(job[:script])
|
if !validate_string(job[:script]) && !validate_array_of_strings(job[:script])
|
||||||
raise ValidationError, "#{name}: script should be a string or an array of a strings"
|
raise ValidationError, "#{name} job: script should be a string or an array of a strings"
|
||||||
end
|
end
|
||||||
|
|
||||||
if job[:stage]
|
if job[:stage]
|
||||||
unless job[:stage].is_a?(String) && job[:stage].in?(stages)
|
unless job[:stage].is_a?(String) && job[:stage].in?(stages)
|
||||||
raise ValidationError, "#{name}: stage parameter should be #{stages.join(", ")}"
|
raise ValidationError, "#{name} job: stage parameter should be #{stages.join(", ")}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if job[:image] && !job[:image].is_a?(String)
|
if job[:image] && !validate_string(job[:image])
|
||||||
raise ValidationError, "#{name}: image should be a string"
|
raise ValidationError, "#{name} job: image should be a string"
|
||||||
end
|
end
|
||||||
|
|
||||||
if job[:services] && !validate_array_of_strings(job[:services])
|
if job[:services] && !validate_array_of_strings(job[:services])
|
||||||
raise ValidationError, "#{name}: services should be an array of strings"
|
raise ValidationError, "#{name} job: services should be an array of strings"
|
||||||
end
|
end
|
||||||
|
|
||||||
if job[:tags] && !validate_array_of_strings(job[:tags])
|
if job[:tags] && !validate_array_of_strings(job[:tags])
|
||||||
raise ValidationError, "#{name}: tags parameter should be an array of strings"
|
raise ValidationError, "#{name} job: tags parameter should be an array of strings"
|
||||||
end
|
end
|
||||||
|
|
||||||
if job[:only] && !validate_array_of_strings(job[:only])
|
if job[:only] && !validate_array_of_strings(job[:only])
|
||||||
raise ValidationError, "#{name}: only parameter should be an array of strings"
|
raise ValidationError, "#{name} job: only parameter should be an array of strings"
|
||||||
end
|
end
|
||||||
|
|
||||||
if job[:except] && !validate_array_of_strings(job[:except])
|
if job[:except] && !validate_array_of_strings(job[:except])
|
||||||
raise ValidationError, "#{name}: except parameter should be an array of strings"
|
raise ValidationError, "#{name} job: except parameter should be an array of strings"
|
||||||
end
|
end
|
||||||
|
|
||||||
if job[:allow_failure] && !job[:allow_failure].in?([true, false])
|
if job[:allow_failure] && !job[:allow_failure].in?([true, false])
|
||||||
raise ValidationError, "#{name}: allow_failure parameter should be an boolean"
|
raise ValidationError, "#{name} job: allow_failure parameter should be an boolean"
|
||||||
end
|
end
|
||||||
|
|
||||||
if job[:when] && !job[:when].in?(%w(on_success on_failure always))
|
if job[:when] && !job[:when].in?(%w(on_success on_failure always))
|
||||||
raise ValidationError, "#{name}: when parameter should be on_success, on_failure or always"
|
raise ValidationError, "#{name} job: when parameter should be on_success, on_failure or always"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def validate_array_of_strings(values)
|
def validate_array_of_strings(values)
|
||||||
values.is_a?(Array) && values.all? {|tag| tag.is_a?(String)}
|
values.is_a?(Array) && values.all? { |value| validate_string(value) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_variables(variables)
|
def validate_variables(variables)
|
||||||
variables.is_a?(Hash) && variables.all? {|key, value| key.is_a?(Symbol) && value.is_a?(String)}
|
variables.is_a?(Hash) && variables.all? { |key, value| validate_string(key) && validate_string(value) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_string(value)
|
||||||
|
value.is_a?(String) || value.is_a?(Symbol)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
module Ci
|
|
||||||
module Migrate
|
|
||||||
class Builds
|
|
||||||
attr_reader :app_builds_dir, :backup_builds_tarball, :backup_dir
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
@app_builds_dir = Settings.gitlab_ci.builds_path
|
|
||||||
@backup_dir = Gitlab.config.backup.path
|
|
||||||
@backup_builds_tarball = File.join(backup_dir, 'builds/builds.tar.gz')
|
|
||||||
end
|
|
||||||
|
|
||||||
def restore
|
|
||||||
backup_existing_builds_dir
|
|
||||||
|
|
||||||
FileUtils.mkdir_p(app_builds_dir, mode: 0700)
|
|
||||||
unless system('tar', '-C', app_builds_dir, '-zxf', backup_builds_tarball)
|
|
||||||
abort 'Restore failed'.red
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def backup_existing_builds_dir
|
|
||||||
timestamped_builds_path = File.join(app_builds_dir, '..', "builds.#{Time.now.to_i}")
|
|
||||||
if File.exists?(app_builds_dir)
|
|
||||||
FileUtils.mv(app_builds_dir, File.expand_path(timestamped_builds_path))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
require 'yaml'
|
|
||||||
|
|
||||||
module Ci
|
|
||||||
module Migrate
|
|
||||||
class Database
|
|
||||||
attr_reader :config
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
@config = YAML.load_file(File.join(Rails.root, 'config', 'database.yml'))[Rails.env]
|
|
||||||
end
|
|
||||||
|
|
||||||
def restore
|
|
||||||
decompress_rd, decompress_wr = IO.pipe
|
|
||||||
decompress_pid = spawn(*%W(gzip -cd), out: decompress_wr, in: db_file_name)
|
|
||||||
decompress_wr.close
|
|
||||||
|
|
||||||
restore_pid = case config["adapter"]
|
|
||||||
when /^mysql/ then
|
|
||||||
$progress.print "Restoring MySQL database #{config['database']} ... "
|
|
||||||
# Workaround warnings from MySQL 5.6 about passwords on cmd line
|
|
||||||
ENV['MYSQL_PWD'] = config["password"].to_s if config["password"]
|
|
||||||
spawn('mysql', *mysql_args, config['database'], in: decompress_rd)
|
|
||||||
when "postgresql" then
|
|
||||||
$progress.print "Restoring PostgreSQL database #{config['database']} ... "
|
|
||||||
pg_env
|
|
||||||
spawn('psql', config['database'], in: decompress_rd)
|
|
||||||
end
|
|
||||||
decompress_rd.close
|
|
||||||
|
|
||||||
success = [decompress_pid, restore_pid].all? { |pid| Process.waitpid(pid); $?.success? }
|
|
||||||
abort 'Restore failed' unless success
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def db_file_name
|
|
||||||
File.join(Gitlab.config.backup.path, 'db', 'database.sql.gz')
|
|
||||||
end
|
|
||||||
|
|
||||||
def mysql_args
|
|
||||||
args = {
|
|
||||||
'host' => '--host',
|
|
||||||
'port' => '--port',
|
|
||||||
'socket' => '--socket',
|
|
||||||
'username' => '--user',
|
|
||||||
'encoding' => '--default-character-set'
|
|
||||||
}
|
|
||||||
args.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact
|
|
||||||
end
|
|
||||||
|
|
||||||
def pg_env
|
|
||||||
ENV['PGUSER'] = config["username"] if config["username"]
|
|
||||||
ENV['PGHOST'] = config["host"] if config["host"]
|
|
||||||
ENV['PGPORT'] = config["port"].to_s if config["port"]
|
|
||||||
ENV['PGPASSWORD'] = config["password"].to_s if config["password"]
|
|
||||||
end
|
|
||||||
|
|
||||||
def report_success(success)
|
|
||||||
if success
|
|
||||||
puts '[DONE]'.green
|
|
||||||
else
|
|
||||||
puts '[FAILED]'.red
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
module Ci
|
|
||||||
module Migrate
|
|
||||||
class Manager
|
|
||||||
CI_IMPORT_PREFIX = '8.0' # Only allow imports from CI 8.0.x
|
|
||||||
|
|
||||||
def cleanup
|
|
||||||
$progress.print "Deleting tmp directories ... "
|
|
||||||
|
|
||||||
backup_contents.each do |dir|
|
|
||||||
next unless File.exist?(File.join(Gitlab.config.backup.path, dir))
|
|
||||||
|
|
||||||
if FileUtils.rm_rf(File.join(Gitlab.config.backup.path, dir))
|
|
||||||
$progress.puts "done".green
|
|
||||||
else
|
|
||||||
puts "deleting tmp directory '#{dir}' failed".red
|
|
||||||
abort 'Backup failed'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def unpack
|
|
||||||
Dir.chdir(Gitlab.config.backup.path)
|
|
||||||
|
|
||||||
# check for existing backups in the backup dir
|
|
||||||
file_list = Dir.glob("*_gitlab_ci_backup.tar").each.map { |f| f.split(/_/).first.to_i }
|
|
||||||
puts "no backups found" if file_list.count == 0
|
|
||||||
|
|
||||||
if file_list.count > 1 && ENV["BACKUP"].nil?
|
|
||||||
puts "Found more than one backup, please specify which one you want to restore:"
|
|
||||||
puts "rake gitlab:backup:restore BACKUP=timestamp_of_backup"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
tar_file = ENV["BACKUP"].nil? ? File.join("#{file_list.first}_gitlab_ci_backup.tar") : File.join(ENV["BACKUP"] + "_gitlab_ci_backup.tar")
|
|
||||||
|
|
||||||
unless File.exists?(tar_file)
|
|
||||||
puts "The specified CI backup doesn't exist!"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
$progress.print "Unpacking backup ... "
|
|
||||||
|
|
||||||
unless Kernel.system(*%W(tar -xf #{tar_file}))
|
|
||||||
puts "unpacking backup failed".red
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
$progress.puts "done".green
|
|
||||||
end
|
|
||||||
|
|
||||||
ENV["VERSION"] = "#{settings[:db_version]}" if settings[:db_version].to_i > 0
|
|
||||||
|
|
||||||
# restoring mismatching backups can lead to unexpected problems
|
|
||||||
if !settings[:gitlab_version].start_with?(CI_IMPORT_PREFIX)
|
|
||||||
puts "GitLab CI version mismatch:".red
|
|
||||||
puts " Your current GitLab CI version (#{GitlabCi::VERSION}) differs from the GitLab CI (#{settings[:gitlab_version]}) version in the backup!".red
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def backup_contents
|
|
||||||
["db", "builds", "backup_information.yml"]
|
|
||||||
end
|
|
||||||
|
|
||||||
def settings
|
|
||||||
@settings ||= YAML.load_file("backup_information.yml")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue