Commit 7c7b664c authored by Douwe Maan's avatar Douwe Maan

Merge branch 'master' into flevour/gitlab-ce-project-path-insensitive-lookup

parents 333463dd 1ca11993
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.0.0 (unreleased) v 8.1.0 (unreleased)
- 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)
- Show CI status on all pages where commits list is rendered
- Automatically enable CI when push .gitlab-ci.yml file to repository
- Move CI charts to project graphs area
- Fix cases where Markdown did not render links in activity feed (Stan Hu)
- Add first and last to pagination (Zeger-Jan van de Weg)
- Show CI status on commit page
- Show CI status on Your projects page and Starred projects page
- Remove "Continuous Integration" page from dashboard
- Add notes and SSL verification entries to hook APIs (Ben Boeckel)
- Fix grammar in admin area "labels" .nothing-here-block when no labels exist.
- Move CI runners page to project settings area
- Move CI variables page to project settings area
- Move CI triggers page to project settings area
- Move CI project settings page to CE project settings area
v 8.0.3
- Fix URL shown in Slack notifications
- Fix bug where projects would appear to be stuck in the forked import state (Stan Hu)
- Fix Error 500 in creating merge requests with > 1000 diffs (Stan Hu)
v 8.0.2
- Fix default avatar not rendering in network graph (Stan Hu)
- Skip check_initd_configured_correctly on omnibus installs
- Prevent double-prefixing of help page paths
- Clarify confirmation text on user deletion
- Make commit graphs responsive to window width changes (Stan Hu)
- Fix top margin for sign-in button on public pages
- Fix LDAP attribute mapping
- Remove git refs used internally by GitLab from network graph (Stan Hu)
- Use standard Markdown font in Markdown preview instead of fixed-width font (Stan Hu)
- Fix Reply by email for non-UTF-8 messages.
- Add option to use StartTLS with Reply by email IMAP server.
- Allow AWS S3 Server-Side Encryption with Amazon S3-Managed Keys for backups (Paul Beattie)
v 8.0.1
- Remove git refs used internally by GitLab from network graph (Stan Hu)
- Improve CI migration procedure and documentation
v 8.0.0
- Fix Markdown links not showing up in dashboard activity feed (Stan Hu)
- Remove milestones from merge requests when milestones are deleted (Stan Hu)
- Fix HTML link that was improperly escaped in new user e-mail (Stan Hu) - Fix HTML link that was improperly escaped in new user e-mail (Stan Hu)
- Fix broken sort in merge request API (Stan Hu) - Fix broken sort in merge request API (Stan Hu)
- Bump rouge to 1.10.1 to remove warning noise and fix other syntax highlighting bugs (Stan Hu) - Bump rouge to 1.10.1 to remove warning noise and fix other syntax highlighting bugs (Stan Hu)
...@@ -42,6 +85,9 @@ v 8.0.0 (unreleased) ...@@ -42,6 +85,9 @@ v 8.0.0 (unreleased)
- Retrieving oauth token with LDAP credentials - Retrieving oauth token with LDAP credentials
- Load Application settings from running database unless env var USE_DB=false - Load Application settings from running database unless env var USE_DB=false
- Added Drone CI integration (Kirill Zaitsev) - Added Drone CI integration (Kirill Zaitsev)
- Allow developers to retry builds
- Hide advanced project options for non-admin users
- Fail builds if no .gitlab-ci.yml is found
- Refactored service API and added automatically service docs generator (Kirill Zaitsev) - Refactored service API and added automatically service docs generator (Kirill Zaitsev)
- Added web_url key project hook_attrs (Kirill Zaitsev) - Added web_url key project hook_attrs (Kirill Zaitsev)
- Add ability to get user information by ID of an SSH key via the API - Add ability to get user information by ID of an SSH key via the API
...@@ -49,6 +95,7 @@ v 8.0.0 (unreleased) ...@@ -49,6 +95,7 @@ v 8.0.0 (unreleased)
- Add support for Crowd - Add support for Crowd
- Global Labels that are available to all projects - Global Labels that are available to all projects
- Fix highlighting of deleted lines in diffs. - Fix highlighting of deleted lines in diffs.
- Project notification level can be set on the project page itself
- Added service API endpoint to retrieve service parameters (Petheő Bence) - Added service API endpoint to retrieve service parameters (Petheő Bence)
- Add FogBugz project import (Jared Szechy) - Add FogBugz project import (Jared Szechy)
- Sort users autocomplete lists by user (Allister Antosik) - Sort users autocomplete lists by user (Allister Antosik)
...@@ -56,6 +103,7 @@ v 8.0.0 (unreleased) ...@@ -56,6 +103,7 @@ v 8.0.0 (unreleased)
- Add ability to add custom text to the help page (Jeroen van Baarsen) - Add ability to add custom text to the help page (Jeroen van Baarsen)
- Add pg_schema to backup config - Add pg_schema to backup config
- Redirect from incorrectly cased group or project path to correct one (Francesco Levorato) - Redirect from incorrectly cased group or project path to correct one (Francesco Levorato)
- Removed API calls from CE to CI
v 7.14.3 v 7.14.3
- No changes - No changes
......
This diff is collapsed.
...@@ -22,7 +22,7 @@ gem "mysql2", '~> 0.3.16', group: :mysql ...@@ -22,7 +22,7 @@ gem "mysql2", '~> 0.3.16', group: :mysql
gem "pg", '~> 0.18.2', group: :postgres gem "pg", '~> 0.18.2', group: :postgres
# Authentication libraries # Authentication libraries
gem "devise", '~> 3.2.4' gem "devise", '~> 3.5.2'
gem "devise-async", '~> 0.9.0' gem "devise-async", '~> 0.9.0'
gem 'omniauth', "~> 1.2.2" gem 'omniauth', "~> 1.2.2"
gem 'omniauth-google-oauth2', '~> 0.2.5' gem 'omniauth-google-oauth2', '~> 0.2.5'
...@@ -38,7 +38,7 @@ gem 'omniauth_crowd' ...@@ -38,7 +38,7 @@ gem 'omniauth_crowd'
gem "rack-oauth2", "~> 1.0.5" gem "rack-oauth2", "~> 1.0.5"
# Two-factor authentication # Two-factor authentication
gem 'devise-two-factor', '~> 1.0.1' gem 'devise-two-factor', '~> 2.0.0'
gem 'rqrcode-rails3', '~> 0.1.7' gem 'rqrcode-rails3', '~> 0.1.7'
gem 'attr_encrypted', '~> 1.3.4' gem 'attr_encrypted', '~> 1.3.4'
...@@ -77,7 +77,7 @@ gem "stamp", '~> 0.5.0' ...@@ -77,7 +77,7 @@ gem "stamp", '~> 0.5.0'
gem 'enumerize', '~> 0.7.0' gem 'enumerize', '~> 0.7.0'
# Pagination # Pagination
gem "kaminari", "~> 0.15.1" gem "kaminari", "~> 0.16.3"
# HAML # HAML
gem "haml-rails", '~> 0.5.3' gem "haml-rails", '~> 0.5.3'
...@@ -121,6 +121,8 @@ end ...@@ -121,6 +121,8 @@ end
# State machine # State machine
gem "state_machine", '~> 1.2.0' gem "state_machine", '~> 1.2.0'
# Run events after state machine commits
gem 'after_commit_queue'
# Issue tags # Issue tags
gem 'acts-as-taggable-on', '~> 3.4' gem 'acts-as-taggable-on', '~> 3.4'
...@@ -283,10 +285,11 @@ group :production do ...@@ -283,10 +285,11 @@ group :production do
end end
gem "newrelic_rpm", '~> 3.9.4.245' gem "newrelic_rpm", '~> 3.9.4.245'
gem 'newrelic-grape'
gem 'octokit', '~> 3.7.0' gem 'octokit', '~> 3.7.0'
gem "mail_room", "~> 0.5.1" gem "mail_room", "~> 0.5.2"
gem 'email_reply_parser', '~> 0.5.8' gem 'email_reply_parser', '~> 0.5.8'
......
...@@ -42,6 +42,8 @@ GEM ...@@ -42,6 +42,8 @@ GEM
acts-as-taggable-on (3.5.0) acts-as-taggable-on (3.5.0)
activerecord (>= 3.2, < 5) activerecord (>= 3.2, < 5)
addressable (2.3.8) addressable (2.3.8)
after_commit_queue (1.1.0)
rails (>= 3.0)
annotate (2.6.10) annotate (2.6.10)
activerecord (>= 3.2, <= 4.3) activerecord (>= 3.2, <= 4.3)
rake (~> 10.4) rake (~> 10.4)
...@@ -136,21 +138,21 @@ GEM ...@@ -136,21 +138,21 @@ GEM
activerecord (>= 3.2.0, < 5.0) activerecord (>= 3.2.0, < 5.0)
descendants_tracker (0.0.4) descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
devise (3.2.4) devise (3.5.2)
bcrypt (~> 3.0) bcrypt (~> 3.0)
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
railties (>= 3.2.6, < 5) railties (>= 3.2.6, < 5)
responders
thread_safe (~> 0.1) thread_safe (~> 0.1)
warden (~> 1.2.3) warden (~> 1.2.3)
devise-async (0.9.0) devise-async (0.9.0)
devise (~> 3.2) devise (~> 3.2)
devise-two-factor (1.0.2) devise-two-factor (2.0.0)
activemodel
activesupport activesupport
attr_encrypted (~> 1.3.2) attr_encrypted (~> 1.3.2)
devise (>= 3.2.4, < 3.5) devise (~> 3.5.0)
railties railties
rotp (< 2) rotp (~> 2)
diff-lcs (1.2.5) diff-lcs (1.2.5)
diffy (3.0.7) diffy (3.0.7)
docile (1.1.5) docile (1.1.5)
...@@ -367,7 +369,7 @@ GEM ...@@ -367,7 +369,7 @@ GEM
railties (>= 3.2.16) railties (>= 3.2.16)
json (1.8.3) json (1.8.3)
jwt (1.5.1) jwt (1.5.1)
kaminari (0.15.1) kaminari (0.16.3)
actionpack (>= 3.0.0) actionpack (>= 3.0.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
kgio (2.9.3) kgio (2.9.3)
...@@ -384,7 +386,7 @@ GEM ...@@ -384,7 +386,7 @@ GEM
systemu (~> 2.6.2) systemu (~> 2.6.2)
mail (2.6.3) mail (2.6.3)
mime-types (>= 1.16, < 3) mime-types (>= 1.16, < 3)
mail_room (0.5.1) mail_room (0.5.2)
method_source (0.8.2) method_source (0.8.2)
mime-types (1.25.1) mime-types (1.25.1)
mimemagic (0.3.0) mimemagic (0.3.0)
...@@ -402,6 +404,9 @@ GEM ...@@ -402,6 +404,9 @@ GEM
net-ssh (>= 2.6.5) net-ssh (>= 2.6.5)
net-ssh (2.9.2) net-ssh (2.9.2)
netrc (0.10.3) netrc (0.10.3)
newrelic-grape (2.0.0)
grape
newrelic_rpm
newrelic_rpm (3.9.4.245) newrelic_rpm (3.9.4.245)
nokogiri (1.6.6.2) nokogiri (1.6.6.2)
mini_portile (~> 0.6.0) mini_portile (~> 0.6.0)
...@@ -558,12 +563,14 @@ GEM ...@@ -558,12 +563,14 @@ GEM
request_store (1.2.0) request_store (1.2.0)
rerun (0.10.0) rerun (0.10.0)
listen (~> 2.7, >= 2.7.3) listen (~> 2.7, >= 2.7.3)
responders (1.1.2)
railties (>= 3.2, < 4.2)
rest-client (1.8.0) rest-client (1.8.0)
http-cookie (>= 1.0.2, < 2.0) http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 3.0) mime-types (>= 1.16, < 3.0)
netrc (~> 0.7) netrc (~> 0.7)
rinku (1.7.3) rinku (1.7.3)
rotp (1.6.1) rotp (2.1.1)
rouge (1.10.1) rouge (1.10.1)
rqrcode (0.7.0) rqrcode (0.7.0)
chunky_png chunky_png
...@@ -782,6 +789,7 @@ DEPENDENCIES ...@@ -782,6 +789,7 @@ DEPENDENCIES
activerecord-session_store (~> 0.1.0) activerecord-session_store (~> 0.1.0)
acts-as-taggable-on (~> 3.4) acts-as-taggable-on (~> 3.4)
addressable (~> 2.3.8) addressable (~> 2.3.8)
after_commit_queue
annotate (~> 2.6.0) annotate (~> 2.6.0)
asana (~> 0.0.6) asana (~> 0.0.6)
asciidoctor (~> 1.5.2) asciidoctor (~> 1.5.2)
...@@ -806,9 +814,9 @@ DEPENDENCIES ...@@ -806,9 +814,9 @@ DEPENDENCIES
d3_rails (~> 3.5.5) d3_rails (~> 3.5.5)
database_cleaner (~> 1.4.0) database_cleaner (~> 1.4.0)
default_value_for (~> 3.0.0) default_value_for (~> 3.0.0)
devise (~> 3.2.4) devise (~> 3.5.2)
devise-async (~> 0.9.0) devise-async (~> 0.9.0)
devise-two-factor (~> 1.0.1) devise-two-factor (~> 2.0.0)
diffy (~> 3.0.3) diffy (~> 3.0.3)
doorkeeper (~> 2.1.3) doorkeeper (~> 2.1.3)
dropzonejs-rails (~> 0.7.1) dropzonejs-rails (~> 0.7.1)
...@@ -844,13 +852,14 @@ DEPENDENCIES ...@@ -844,13 +852,14 @@ DEPENDENCIES
jquery-scrollto-rails (~> 1.4.3) jquery-scrollto-rails (~> 1.4.3)
jquery-turbolinks (~> 2.0.1) jquery-turbolinks (~> 2.0.1)
jquery-ui-rails (~> 4.2.1) jquery-ui-rails (~> 4.2.1)
kaminari (~> 0.15.1) kaminari (~> 0.16.3)
letter_opener (~> 1.1.2) letter_opener (~> 1.1.2)
mail_room (~> 0.5.1) mail_room (~> 0.5.2)
minitest (~> 5.7.0) minitest (~> 5.7.0)
mousetrap-rails (~> 1.4.6) mousetrap-rails (~> 1.4.6)
mysql2 (~> 0.3.16) mysql2 (~> 0.3.16)
nested_form (~> 0.3.2) nested_form (~> 0.3.2)
newrelic-grape
newrelic_rpm (~> 3.9.4.245) newrelic_rpm (~> 3.9.4.245)
nprogress-rails (~> 0.1.2.3) nprogress-rails (~> 0.1.2.3)
oauth2 (~> 1.0.0) oauth2 (~> 1.0.0)
......
8.0.0.pre 8.1.0.pre
@CiPager =
init: (@url, @limit = 0, preload, @disable = false) ->
if preload
@offset = 0
@getItems()
else
@offset = @limit
@initLoadMore()
getItems: ->
$(".loading").show()
$.ajax
type: "GET"
url: @url
data: "limit=" + @limit + "&offset=" + @offset
complete: =>
$(".loading").hide()
success: (data) =>
CiPager.append(data.count, data.html)
dataType: "json"
append: (count, html) ->
if count > 1
$(".content-list").append html
if count == @limit
@offset += count
else
@disable = true
initLoadMore: ->
$(document).unbind('scroll')
$(document).endlessScroll
bottomPixels: 400
fireDelay: 1000
fireOnce: true
ceaseFire: ->
CiPager.disable
callback: (i) =>
unless $(".loading").is(':visible')
$(".loading").show()
CiPager.getItems()
...@@ -11,12 +11,13 @@ class @IssuableContext ...@@ -11,12 +11,13 @@ class @IssuableContext
$(this).submit() $(this).submit()
$('.issuable-details').waitForImages -> $('.issuable-details').waitForImages ->
$('.issuable-affix').on 'affix.bs.affix', ->
$(@).width($(@).outerWidth())
.on 'affixed-top.bs.affix affixed-bottom.bs.affix', ->
$(@).width('')
$('.issuable-affix').affix offset: $('.issuable-affix').affix offset:
top: -> top: ->
@top = ($('.issuable-affix').offset().top - 70) @top = ($('.issuable-affix').offset().top - 70)
bottom: -> bottom: ->
@bottom = $('.footer').outerHeight(true) @bottom = $('.footer').outerHeight(true)
$('.issuable-affix').on 'affix.bs.affix', ->
$(@).width($(@).outerWidth())
.on 'affixed-top.bs.affix affixed-bottom.bs.affix', ->
$(@).width('')
...@@ -24,3 +24,19 @@ class @Project ...@@ -24,3 +24,19 @@ class @Project
$.cookie('hide_no_password_message', 'false', { path: path }) $.cookie('hide_no_password_message', 'false', { path: path })
$(@).parents('.no-password-message').remove() $(@).parents('.no-password-message').remove()
e.preventDefault() e.preventDefault()
$('.update-notification').on 'click', (e) ->
e.preventDefault()
notification_level = $(@).data 'notification-level'
$('#notification_level').val(notification_level)
$('#notification-form').submit()
label = null
switch notification_level
when 0 then label = ' Disabled '
when 1 then label = ' Participating '
when 2 then label = ' Watching '
when 3 then label = ' Global '
when 4 then label = ' On Mention '
$('#notifications-button').empty().append("<i class='fa fa-bell'></i>" + label + "<i class='fa fa-angle-down'></i>")
$(@).parents('ul').find('li.active').removeClass 'active'
$(@).parent().addClass 'active'
\ No newline at end of file
...@@ -65,20 +65,20 @@ $legend-color: $text-color; ...@@ -65,20 +65,20 @@ $legend-color: $text-color;
// //
//## //##
$pagination-color: #fff; $pagination-color: $gl-gray;
$pagination-bg: $brand-success; $pagination-bg: $background-color;
$pagination-border: transparent; $pagination-border: transparent;
$pagination-hover-color: #fff; $pagination-hover-color: #fff;
$pagination-hover-bg: darken($brand-success, 15%); $pagination-hover-bg: $brand-info;
$pagination-hover-border: transparent; $pagination-hover-border: transparent;
$pagination-active-color: #fff; $pagination-active-color: #fff;
$pagination-active-bg: darken($brand-success, 15%); $pagination-active-bg: $brand-info;
$pagination-active-border: transparent; $pagination-active-border: transparent;
$pagination-disabled-color: #b4bcc2; $pagination-disabled-color: #fff;
$pagination-disabled-bg: lighten($brand-success, 15%); $pagination-disabled-bg: lighten($brand-info, 15%);
$pagination-disabled-border: transparent; $pagination-disabled-border: transparent;
......
...@@ -93,46 +93,88 @@ ...@@ -93,46 +93,88 @@
} }
h1 { h1 {
margin-top: 45px; font-size: 1.3em;
font-size: 2.5em; font-weight: 600;
margin: 24px 0 12px 0;
padding: 0 0 10px 0;
border-bottom: 1px solid #e7e9ed;
color: #313236;
} }
h2 { h2 {
margin-top: 40px; font-size: 1.2em;
font-size: 2em; font-weight: 600;
margin: 24px 0 12px 0;
color: #313236;
} }
h3 { h3 {
margin-top: 35px; margin: 24px 0 12px 0;
font-size: 1.5em; font-size: 1.25em;
} }
h4 { h4 {
margin-top: 30px; margin: 24px 0 12px 0;
font-size: 1.2em; font-size: 1.1em;
}
h5 {
margin: 24px 0 12px 0;
font-size: 1em;
}
h6 {
margin: 24px 0 12px 0;
font-size: 0.90em;
} }
blockquote { blockquote {
color: #888; padding: 8px 21px;
margin: 12px 0 12px;
border-left: 3px solid #e7e9ed;
}
blockquote p {
color: #7f8fa4 !important;
font-size: 15px; font-size: 15px;
line-height: 1.5; line-height: 1.5;
} }
p {
color:#5c5d5e;
margin:6px 0 0 0;
}
table { table {
@extend .table; @extend .table;
@extend .table-bordered; @extend .table-bordered;
margin: 12px 0 12px 0;
color: #5c5d5e;
th { th {
background: #EEE; background: #f8fafc;
} }
} }
pre {
margin: 12px 0 12px 0 !important;
background-color: #f8fafc !important;
font-size: 13px !important;
color: #5b6169 !important;
line-height: 1.6em !important;
@include border-radius(2px);
}
p > code { p > code {
font-size: inherit;
font-weight: inherit; font-weight: inherit;
} }
ul {
color: #5c5d5e;
}
li { li {
line-height: 1.5; line-height: 1.6em;
} }
a[href*="/uploads/"], a[href*="storage.googleapis.com/google-code-attachments/"] { a[href*="/uploads/"], a[href*="storage.googleapis.com/google-code-attachments/"] {
...@@ -152,6 +194,7 @@ ...@@ -152,6 +194,7 @@
} }
} }
@mixin str-truncated($max_width: 82%) { @mixin str-truncated($max_width: 82%) {
display: inline-block; display: inline-block;
overflow: hidden; overflow: hidden;
...@@ -183,7 +226,7 @@ ...@@ -183,7 +226,7 @@
&.active { &.active {
background: #f9f9f9; background: #f9f9f9;
a { a {
font-weight: bold; font-weight: 600;
} }
} }
...@@ -251,3 +294,8 @@ ...@@ -251,3 +294,8 @@
} }
} }
} }
.fa-align {
top: 20px;
position: relative;
}
...@@ -12,8 +12,8 @@ $sidebar_width: 230px; ...@@ -12,8 +12,8 @@ $sidebar_width: 230px;
$avatar_radius: 50%; $avatar_radius: 50%;
$code_font_size: 13px; $code_font_size: 13px;
$code_line_height: 1.5; $code_line_height: 1.5;
$border-color: #E7E9ED; $border-color: #dce0e6;
$background-color: #F8FAFC; $background-color: #F7F8FA;
$header-height: 58px; $header-height: 58px;
$fixed-layout-width: 1200px; $fixed-layout-width: 1200px;
$gl-gray: #7f8fa4; $gl-gray: #7f8fa4;
......
...@@ -13,31 +13,6 @@ ...@@ -13,31 +13,6 @@
.builds, .builds,
.projects-table { .projects-table {
.alert-success {
background-color: #6fc995;
border-color: #5bba83;
}
.alert-danger {
background-color: #eb897f;
border-color: #d4776e;
}
.alert-info {
background-color: #3498db;
border-color: #2e8ece;
}
.alert-warning {
background-color: #EB974E;
border-color: #E87E04;
}
.alert-disabled {
background: $background-color;
border-color: $border-color;
}
.light { .light {
border-color: $border-color; border-color: $border-color;
} }
...@@ -47,8 +22,8 @@ ...@@ -47,8 +22,8 @@
} }
td { td {
color: $gl-gray;
vertical-align: middle !important; vertical-align: middle !important;
border-color: inherit !important;
a { a {
font-weight: normal; font-weight: normal;
...@@ -58,23 +33,16 @@ ...@@ -58,23 +33,16 @@
} }
.commit-info { .commit-info {
font-size: 14px;
.attr-name { .attr-name {
font-weight: 300;
color: #666;
margin-right: 5px; margin-right: 5px;
} }
pre.commit-message { pre.commit-message {
font-size: 14px;
background: none; background: none;
padding: 0; padding: 0;
margin: 0; margin: 0;
border: none; border: none;
margin: 20px 0; margin: 20px 0;
border-bottom: 1px solid #EEE;
padding-bottom: 20px;
border-radius: 0; border-radius: 0;
} }
} }
......
.ci-status {
padding: 2px 7px;
margin-right: 5px;
border: 1px solid #EEE;
white-space: nowrap;
@include border-radius(4px);
&:hover {
text-decoration: none;
}
&.ci-failed {
color: $gl-danger;
border-color: $gl-danger;
}
&.ci-success {
color: $gl-success;
border-color: $gl-success;
}
&.ci-info {
color: $gl-info;
border-color: $gl-info;
}
&.ci-disabled {
color: $gl-gray;
border-color: $gl-gray;
}
&.ci-pending,
&.ci-running {
color: $gl-warning;
border-color: $gl-warning;
}
}
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
&.s48 { width: 48px; height: 48px; margin-right: 10px; } &.s48 { width: 48px; height: 48px; margin-right: 10px; }
&.s60 { width: 60px; height: 60px; margin-right: 12px; } &.s60 { width: 60px; height: 60px; margin-right: 12px; }
&.s90 { width: 90px; height: 90px; margin-right: 15px; } &.s90 { width: 90px; height: 90px; margin-right: 15px; }
&.s110 { width: 110px; height: 110px; margin-right: 15px; }
&.s140 { width: 140px; height: 140px; margin-right: 20px; } &.s140 { width: 140px; height: 140px; margin-right: 20px; }
&.s160 { width: 160px; height: 160px; margin-right: 20px; } &.s160 { width: 160px; height: 160px; margin-right: 20px; }
} }
...@@ -42,6 +43,7 @@ ...@@ -42,6 +43,7 @@
&.s32 { font-size: 22px; line-height: 32px; } &.s32 { font-size: 22px; line-height: 32px; }
&.s60 { font-size: 32px; line-height: 60px; } &.s60 { font-size: 32px; line-height: 60px; }
&.s90 { font-size: 36px; line-height: 90px; } &.s90 { font-size: 36px; line-height: 90px; }
&.s110 { font-size: 40px; line-height: 112px; font-weight: 300; }
&.s140 { font-size: 72px; line-height: 140px; } &.s140 { font-size: 72px; line-height: 140px; }
&.s160 { font-size: 96px; line-height: 160px; } &.s160 { font-size: 96px; line-height: 160px; }
} }
...@@ -20,11 +20,11 @@ ...@@ -20,11 +20,11 @@
.gray-content-block { .gray-content-block {
margin: -$gl-padding; margin: -$gl-padding;
background-color: #f8fafc; background-color: $background-color;
padding: $gl-padding; padding: $gl-padding;
margin-bottom: 0px; margin-bottom: 0px;
border-top: 1px solid #e7e9ed; border-top: 1px solid $border-color;
border-bottom: 1px solid #e7e9ed; border-bottom: 1px solid $border-color;
color: $gl-gray; color: $gl-gray;
&.top-block { &.top-block {
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
&.footer-block { &.footer-block {
margin-top: 0; margin-top: 0;
border-bottom: none;
margin-bottom: -$gl-padding; margin-bottom: -$gl-padding;
} }
......
body {
text-rendering: geometricPrecision;
}
.btn { .btn {
@extend .btn-default; @extend .btn-default;
...@@ -88,3 +91,138 @@ ...@@ -88,3 +91,138 @@
} }
} }
} }
@mixin btn-info {
@include border-radius(2px);
border-width: 1px;
border-style: solid;
text-transform: uppercase;
font-size: 13px;
font-weight: 600;
line-height: 18px;
padding: 11px 16px;
letter-spacing: .4px;
&:hover {
border-width: 1px;
border-style: solid;
}
&:focus {
border-width: 1px;
border-style: solid;
}
&:active {
@include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12));
border-width: 1px;
border-style: solid;
}
}
@mixin btn-middle {
@include border-radius(2px);
border-width: 1px;
border-style: solid;
text-transform: uppercase;
font-size: 13px;
font-weight: 600;
line-height: 18px;
padding: 11px 24px;
letter-spacing: .4px;
&:hover {
border-width: 1px;
border-style: solid;
}
&:focus {
border-width: 1px;
border-style: solid;
}
&:active {
@include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12));
border-width: 1px;
border-style: solid;
}
}
@mixin btn-green {
background-color: #28b061;
border: 1px solid #26a65c;
color: #fff;
&:hover {
background-color: #26ab5d;
border: 1px solid #229954;
color: #fff;
}
&:focus {
background-color: #26ab5d;
border: 1px solid #229954;
color: #fff;
}
&:active {
@include box-shadow (inset 0 0 4px rgba(0, 0, 0, 0.12));
background-color: #23a158 !important;
border: 1px solid #229954 !important;
color: #fff !important;
}
}
/*Butons*/
@mixin bnt-project {
background-color: #f0f2f5;
border-color: #dce0e5;
color: #313236;
&:hover {
border-color:#dce0e5;
background-color: #ebeef2;
color: #313236;
}
&:focus {
border-color: #dce0e5;
background-color: #ebeef2;
color: #313236;
}
&:active {
@include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12));
color: #313236 !important;
border-color: #c6cacf !important;
background-color: #e4e7ed !important;
}
}
@mixin btn-remove {
background-color: #f72e60;
border-color: #ee295a;
&:hover {
background-color: #e82757;
border-color: #e32555;
}
&:focus {
background-color: #e82757;
border-color: #e32555;
}
&:active {
@include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12));
background-color: #d42450 !important;
border-color: #e12554 !important;
}
}
\ No newline at end of file
/*
* Callouts from Bootstrap3 docs
*
* Not quite alerts, but custom and helpful notes for folks reading the docs.
* Requires a base and modifier class.
*/
/* Common styles for all types */
.bs-callout {
margin: 20px 0;
padding: 20px;
border-left: 3px solid #eee;
color: #666;
background: #f9f9f9;
}
.bs-callout h4 {
margin-top: 0;
margin-bottom: 5px;
}
.bs-callout p:last-child {
margin-bottom: 0;
}
/* Variations */
.bs-callout-danger {
background-color: #fdf7f7;
border-color: #eed3d7;
color: #b94a48;
}
.bs-callout-warning {
background-color: #faf8f0;
border-color: #faebcc;
color: #8a6d3b;
}
.bs-callout-info {
background-color: #f4f8fa;
border-color: #bce8f1;
color: #34789a;
}
.bs-callout-success {
background-color: #dff0d8;
border-color: #5cA64d;
color: #3c763d;
}
...@@ -302,7 +302,7 @@ table { ...@@ -302,7 +302,7 @@ table {
} }
.btn-sign-in { .btn-sign-in {
margin-top: 15px; margin-top: 8px;
text-shadow: none; text-shadow: none;
} }
...@@ -313,7 +313,7 @@ table { ...@@ -313,7 +313,7 @@ table {
} }
.wiki .highlight, .note-body .highlight { .wiki .highlight, .note-body .highlight {
margin-bottom: 9px; margin: 12px 0 12px 0;
} }
.wiki .code { .wiki .code {
......
...@@ -26,7 +26,6 @@ header { ...@@ -26,7 +26,6 @@ header {
min-height: $header-height; min-height: $header-height;
background-color: #fff; background-color: #fff;
border: none; border: none;
border-bottom: 1px solid #EEE;
.container-fluid { .container-fluid {
width: 100% !important; width: 100% !important;
......
.gl-pagination {
border-top: 1px solid $border-color;
background-color: $background-color;
margin: -$gl-padding;
margin-top: 0;
.pagination {
padding: 0;
margin: 0;
display: block;
li.first,
li.last,
li.next,
li.prev {
> a {
color: $link-color;
&:hover {
color: #fff;
}
}
}
li > a,
li > span {
border: none;
margin: 0;
@include border-radius(0 !important);
padding: 13px 19px;
border-right: 1px solid $border-color;
}
}
}
...@@ -21,12 +21,11 @@ ...@@ -21,12 +21,11 @@
min-height: 100vh; min-height: 100vh;
width: 100%; width: 100%;
padding: 20px; padding: 20px;
background: #f1f4f8; background: #EAEBEC;
.container-fluid { .container-fluid {
background: #FFF; background: #FFF;
padding: $gl-padding; padding: $gl-padding;
border: 1px solid #e7e9ed;
min-height: 90vh; min-height: 90vh;
&.container-blank { &.container-blank {
......
...@@ -2,11 +2,24 @@ ...@@ -2,11 +2,24 @@
* Headers * Headers
* *
*/ */
body {
text-rendering:optimizeLegibility;
-webkit-text-shadow: rgba(255,255,255,0.01) 0 0 1px;
}
.page-title { .page-title {
margin-top: 0px; margin-top: 0px;
line-height: 1.5; line-height: 1.3;
font-weight: normal; font-size: 1.25em;
margin-bottom: 5px; font-weight: 600;
}
.page-title-empty {
margin-top: 0px;
line-height: 1.3;
font-size: 1.25em;
font-weight: 600;
margin: 12px 7px 12px 7px;
} }
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {
...@@ -55,6 +68,7 @@ a > code { ...@@ -55,6 +68,7 @@ a > code {
@include md-typography; @include md-typography;
word-wrap: break-word; word-wrap: break-word;
padding: 7px;
/* Link to current header. */ /* Link to current header. */
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {
...@@ -83,12 +97,19 @@ a > code { ...@@ -83,12 +97,19 @@ a > code {
} }
} }
ul { ul,ol {
padding: 0; padding: 0;
margin: 0 0 9px 25px !important; margin: 6px 0 6px 18px !important;
}
ol {
color: #5c5d5e;
} }
} }
.md-area {
@include md-typography;
}
.md { .md {
@include md-typography; @include md-typography;
} }
...@@ -101,6 +122,9 @@ textarea.js-gfm-input { ...@@ -101,6 +122,9 @@ textarea.js-gfm-input {
font-family: $monospace_font; font-family: $monospace_font;
} }
.md-preview {
}
.strikethrough { .strikethrough {
text-decoration: line-through; text-decoration: line-through;
} }
\ No newline at end of file
/* https://github.com/aahan/pygments-github-style */ /* https://github.com/aahan/pygments-github-style */
pre.code.highlight.white, pre.code.highlight.white,
.code.white { .code.white {
background-color: #f8fafc;
background-color: #fff; font-size: 13px;
color: #333; color: #5b6169;
line-height: 1.6em;
.line-numbers, .line-numbers,
.line-numbers a { .line-numbers a {
......
...@@ -10,3 +10,9 @@ ...@@ -10,3 +10,9 @@
.milestone-row { .milestone-row {
@include str-truncated(90%); @include str-truncated(90%);
} }
.dashboard .side .panel .panel-heading .input-group {
.form-control {
height: 42px;
}
}
\ No newline at end of file
...@@ -109,7 +109,7 @@ ...@@ -109,7 +109,7 @@
.note-edit-form { .note-edit-form {
display: none; display: none;
font-size: 13px; font-size: 15px;
.form-actions { .form-actions {
padding-left: 20px; padding-left: 20px;
......
.alert_holder {
margin: -16px;
.alert-link {
font-weight: normal;
}
}
.no-ssh-key-message {
background-color: #f28d35;
margin-bottom: 16px;
}
.new_project, .new_project,
.edit_project { .edit_project {
fieldset.features { fieldset.features {
...@@ -19,44 +30,47 @@ ...@@ -19,44 +30,47 @@
background: #f7f8fa; background: #f7f8fa;
margin: -$gl-padding; margin: -$gl-padding;
padding: $gl-padding; padding: $gl-padding;
padding-top: 40px; padding: 44px 0 17px 0;
.project-identicon-holder { .project-identicon-holder {
margin-bottom: 15px; margin-bottom: 16px;
.avatar, .identicon { .avatar, .identicon {
margin: 0 auto; margin: 0 auto;
float: none; float: none;
} }
.identicon { .identicon {
@include border-radius(50%); @include border-radius(50%);
} }
} }
.project-home-dropdown { .project-home-dropdown {
margin: 11px 3px 0; margin: 11px 3px 0;
} }
.project-home-desc { .project-home-desc {
h1 { h1 {
color: #313236;
margin: 0; margin: 0;
margin-bottom: 10px; margin-bottom: 6px;
font-size: 23px; font-size: 23px;
font-weight: normal; font-weight: normal;
} }
p { p {
color: #7f8fa4; color: #5c5d5e;
} }
} }
.git-clone-holder { .git-clone-holder {
max-width: 600px; max-width: 498px;
margin: 20px auto;
.form-control { .form-control {
background: #FFF; background: #FFF;
font-size: 14px;
height: 42px;
margin-left: -1px;
} }
} }
...@@ -66,30 +80,37 @@ ...@@ -66,30 +80,37 @@
color: inherit; color: inherit;
} }
} }
.input-group {
display: inline-table;
position: relative;
top: 17px;
margin-bottom: 44px;
}
.project-repo-buttons { .project-repo-buttons {
margin-top: $gl-padding; margin-top: 12px;
margin-bottom: 25px; margin-bottom: 0px;
.btn { .btn {
@extend .btn-info; @include bnt-project;
@include btn-info;
text-transform: uppercase;
font-size: 15px;
line-height: 20px;
padding: 8px 14px;
border-radius: 3px;
margin-left: 10px;
.count { .count {
padding-left: 7px;
display: inline-block; display: inline-block;
margin-left: 7px;
} }
} }
} }
} }
.split-one {
display: inline-table;
margin-right: 12px;
a {
margin: -1px !important;
}
}
.git-clone-holder { .git-clone-holder {
.project-home-dropdown + & { .project-home-dropdown + & {
margin-right: 45px; margin-right: 45px;
...@@ -99,23 +120,132 @@ ...@@ -99,23 +120,132 @@
cursor: auto; cursor: auto;
@extend .monospace; @extend .monospace;
background: #FAFAFA; background: #FAFAFA;
width: 100%; width: 101%;
} }
.input-group-addon { .input-group-addon {
background: #FAFAFA; background: #f7f8fa;
&.git-protocols { &.git-protocols {
padding: 0; padding: 0;
border: none; border: none;
.input-group-btn:last-child > .btn { .input-group-btn:last-child > .btn {
@include border-radius-right(0); @include border-radius-right(0);
border-left: 1px solid #c6cacf;
margin-left: -2px !important;
} }
} }
} }
} }
.projects-search-form {
.input-group .form-control {
height: 42px;
}
}
.input-group-btn {
.btn {
@include bnt-project;
@include btn-middle;
&:hover {
outline: none;
}
&:focus {
outline: none;
}
&:active {
outline: none;
}
}
.active {
@include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12));
border: 1px solid #c6cacf !important;
background-color: #e4e7ed !important;
}
.btn-green {
@include btn-green
}
}
.split-repo-buttons {
display: inline-table;
margin: 0 12px 0 12px;
.btn{
@include bnt-project;
@include btn-info;
}
.dropdown-toggle {
margin: -5px;
}
}
#notification-form {
margin-left: 5px;
}
.dropdown-new {
margin-left: -5px;
}
.open > .dropdown-new.btn {
@include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12));
border: 1px solid #c6cacf !important;
background-color: #e4e7ed !important;
text-transform: uppercase;
color: #313236 !important;
font-size: 13px;
font-weight: 600;
}
.dropdown-menu {
@include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px);
@include border-radius (0px);
border: none;
padding: 16px 0;
font-size: 14px;
font-weight: 100;
li a {
color: #5f697a;
line-height: 30px;
&:hover {
background-color: #3084bb !important;
}
}
.fa-fw {
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;
...@@ -232,15 +362,28 @@ table.table.protected-branches-list tr.no-border { ...@@ -232,15 +362,28 @@ table.table.protected-branches-list tr.no-border {
.project-stats { .project-stats {
text-align: center; text-align: center;
margin-top: 0; margin-top: 15px;
margin-bottom: 0; margin-bottom: 0;
padding-top: 5px; padding-top: 10px;
padding-bottom: 0; padding-bottom: 4px;
ul.nav-pills { ul.nav-pills {
display:inline-block; display:inline-block;
} }
.nav-pills li {
display:inline;
}
.nav > li > a {
@include btn-info;
@include bnt-project;
background-color: transparent;
border: 1px solid #f7f8fa;
margin-left: 12px;
}
li { li {
display:inline; display:inline;
} }
...@@ -251,11 +394,11 @@ table.table.protected-branches-list tr.no-border { ...@@ -251,11 +394,11 @@ table.table.protected-branches-list tr.no-border {
} }
li.missing a { li.missing a {
color: #bbb; color: #5a6069;
border: 1px dashed #ccc; border: 1px dashed #dce0e5;
&:hover { &:hover {
background-color: #FAFAFA; background-color: #f0f2f5;
} }
} }
} }
...@@ -273,9 +416,37 @@ pre.light-well { ...@@ -273,9 +416,37 @@ pre.light-well {
border-bottom: 1px solid #e7e9ed; border-bottom: 1px solid #e7e9ed;
} }
.git-empty {
margin: 0 7px 0 7px;
h5 {
color: #5c5d5e;
}
.light-well {
@include border-radius (2px);
color: #5b6169;
font-size: 13px;
line-height: 1.6em;
}
}
.prepend-top-20 {
margin-top: 20px;
.btn-remove {
@include btn-middle;
@include btn-remove;
float: left !important;
}
}
/* /*
* Projects list rendered on dashboard and user page * Projects list rendered on dashboard and user page
*/ */
.projects-list { .projects-list {
@include basic-list; @include basic-list;
...@@ -297,9 +468,15 @@ pre.light-well { ...@@ -297,9 +468,15 @@ pre.light-well {
color: #4c4e54; color: #4c4e54;
} }
.pull-right.light { .project-controls {
float: right;
color: $gl-gray;
line-height: 45px; line-height: 45px;
color: #7f8fa4; color: #7f8fa4;
a:hover {
text-decoration: none;
}
} }
.project-description { .project-description {
...@@ -329,3 +506,8 @@ pre.light-well { ...@@ -329,3 +506,8 @@ pre.light-well {
margin-top: -1px; margin-top: -1px;
} }
} }
.inline-form {
display: inline-block;
}
...@@ -56,13 +56,19 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -56,13 +56,19 @@ class Admin::UsersController < Admin::ApplicationController
end end
def confirm def confirm
if user.confirm! if user.confirm
redirect_to :back, notice: "Successfully confirmed" redirect_to :back, notice: "Successfully confirmed"
else else
redirect_to :back, alert: "Error occurred. User was not confirmed" redirect_to :back, alert: "Error occurred. User was not confirmed"
end end
end end
def login_as
sign_in(user)
flash[:alert] = "Logged in as #{user.username}"
redirect_to root_path
end
def disable_two_factor def disable_two_factor
user.disable_two_factor! user.disable_two_factor!
redirect_to admin_user_path(user), redirect_to admin_user_path(user),
......
...@@ -38,7 +38,7 @@ module Ci ...@@ -38,7 +38,7 @@ module Ci
end end
def authorize_manage_builds! def authorize_manage_builds!
unless can?(current_user, :admin_project, gl_project) unless can?(current_user, :manage_builds, gl_project)
return page_404 return page_404
end end
end end
......
module Ci
class ChartsController < Ci::ApplicationController
before_action :authenticate_user!
before_action :project
before_action :authorize_access_project!
before_action :authorize_manage_project!
layout 'ci/project'
def show
@charts = {}
@charts[:week] = Ci::Charts::WeekChart.new(@project)
@charts[:month] = Ci::Charts::MonthChart.new(@project)
@charts[:year] = Ci::Charts::YearChart.new(@project)
@charts[:build_times] = Ci::Charts::BuildTime.new(@project)
end
protected
def project
@project = Ci::Project.find(params[:project_id])
end
end
end
module Ci module Ci
class ProjectsController < Ci::ApplicationController class ProjectsController < Ci::ApplicationController
PROJECTS_BATCH = 100 before_action :authenticate_user!, except: [:build, :badge, :show]
before_action :authenticate_user!, except: [:build, :badge, :index, :show]
before_action :authenticate_public_page!, only: :show before_action :authenticate_public_page!, only: :show
before_action :project, only: [:build, :integration, :show, :badge, :edit, :update, :destroy, :toggle_shared_runners, :dumped_yaml] before_action :project, only: [:build, :show, :badge, :toggle_shared_runners, :dumped_yaml]
before_action :authorize_access_project!, except: [:build, :badge, :index, :show, :new, :create, :disabled] before_action :authorize_access_project!, except: [:build, :badge, :show, :new, :disabled]
before_action :authorize_manage_project!, only: [:edit, :integration, :update, :destroy, :toggle_shared_runners, :dumped_yaml] before_action :authorize_manage_project!, only: [:toggle_shared_runners, :dumped_yaml]
before_action :authenticate_token!, only: [:build] before_action :authenticate_token!, only: [:build]
before_action :no_cache, only: [:badge] before_action :no_cache, only: [:badge]
skip_before_action :check_enable_flag!, only: [:disabled] skip_before_action :check_enable_flag!, only: [:disabled]
...@@ -17,26 +15,6 @@ module Ci ...@@ -17,26 +15,6 @@ module Ci
def disabled def disabled
end end
def index
@limit, @offset = (params[:limit] || PROJECTS_BATCH).to_i, (params[:offset] || 0).to_i
@page = @offset == 0 ? 1 : (@offset / @limit + 1)
if current_user
@projects = ProjectListBuilder.new.execute(current_user, params[:search])
@projects = @projects.page(@page).per(@limit)
@total_count = @projects.size
end
respond_to do |format|
format.json do
pager_json("ci/projects/index", @total_count)
end
format.html
end
end
def show def show
@ref = params[:ref] @ref = params[:ref]
...@@ -45,57 +23,6 @@ module Ci ...@@ -45,57 +23,6 @@ module Ci
@commits = @commits.page(params[:page]).per(20) @commits = @commits.page(params[:page]).per(20)
end end
def integration
end
def create
project_data = OpenStruct.new(JSON.parse(params["project"]))
unless can?(current_user, :admin_project, ::Project.find(project_data.id))
return redirect_to ci_root_path, alert: 'You have to have at least master role to enable CI for this project'
end
@project = Ci::CreateProjectService.new.execute(current_user, project_data, ci_project_url(":project_id"))
if @project.persisted?
redirect_to ci_project_path(@project, show_guide: true), notice: 'Project was successfully created.'
else
redirect_to :back, alert: 'Cannot save project'
end
end
def edit
end
def update
if project.update_attributes(project_params)
Ci::EventService.new.change_project_settings(current_user, project)
redirect_to :back, notice: 'Project was successfully updated.'
else
render action: "edit"
end
end
def destroy
project.gl_project.gitlab_ci_service.update_attributes(active: false)
project.destroy
Ci::EventService.new.remove_project(current_user, project)
redirect_to ci_projects_url
end
def build
@commit = Ci::CreateCommitService.new.execute(@project, params.dup)
if @commit && @commit.valid?
head 201
else
head 400
end
end
# Project status badge # Project status badge
# Image with build status for sha or ref # Image with build status for sha or ref
def badge def badge
...@@ -106,7 +33,8 @@ module Ci ...@@ -106,7 +33,8 @@ module Ci
def toggle_shared_runners def toggle_shared_runners
project.toggle!(:shared_runners_enabled) project.toggle!(:shared_runners_enabled)
redirect_to :back
redirect_to namespace_project_runners_path(project.gl_project.namespace, project.gl_project)
end end
def dumped_yaml def dumped_yaml
...@@ -124,12 +52,5 @@ module Ci ...@@ -124,12 +52,5 @@ module Ci
response.headers["Pragma"] = "no-cache" response.headers["Pragma"] = "no-cache"
response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT" response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
end end
def project_params
params.require(:project).permit(:path, :timeout, :timeout_in_minutes, :default_ref, :always_build,
:polling_interval, :public, :ssh_url_to_repo, :allow_git_fetch, :email_recipients,
:email_add_pusher, :email_only_broken_builds, :coverage_regex, :shared_runners_enabled, :token,
{ variables_attributes: [:id, :key, :value, :_destroy] })
end
end end
end end
...@@ -11,10 +11,12 @@ module Ci ...@@ -11,10 +11,12 @@ module Ci
return head(403) unless current_user.ci_authorized_runners.include?(@runner) return head(403) unless current_user.ci_authorized_runners.include?(@runner)
path = runners_path(@project.gl_project)
if @runner.assign_to(project, current_user) if @runner.assign_to(project, current_user)
redirect_to ci_project_runners_path(project) redirect_to path
else else
redirect_to ci_project_runners_path(project), alert: 'Failed adding runner to project' redirect_to path, alert: 'Failed adding runner to project'
end end
end end
...@@ -22,7 +24,7 @@ module Ci ...@@ -22,7 +24,7 @@ module Ci
runner_project = project.runner_projects.find(params[:id]) runner_project = project.runner_projects.find(params[:id])
runner_project.destroy runner_project.destroy
redirect_to ci_project_runners_path(project) redirect_to runners_path(@project.gl_project)
end end
private private
......
module Ci
class RunnersController < Ci::ApplicationController
before_action :authenticate_user!
before_action :project
before_action :set_runner, only: [:edit, :update, :destroy, :pause, :resume, :show]
before_action :authorize_access_project!
before_action :authorize_manage_project!
layout 'ci/project'
def index
@runners = @project.runners.order('id DESC')
@specific_runners =
Ci::Runner.specific.includes(:runner_projects).
where(Ci::RunnerProject.table_name => { project_id: current_user.authorized_projects } ).
where.not(id: @runners).order("#{Ci::Runner.table_name}.id DESC").page(params[:page]).per(20)
@shared_runners = Ci::Runner.shared.active
@shared_runners_count = @shared_runners.count(:all)
end
def edit
end
def update
if @runner.update_attributes(runner_params)
redirect_to edit_ci_project_runner_path(@project, @runner), notice: 'Runner was successfully updated.'
else
redirect_to edit_ci_project_runner_path(@project, @runner), alert: 'Runner was not updated.'
end
end
def destroy
if @runner.only_for?(@project)
@runner.destroy
end
redirect_to ci_project_runners_path(@project)
end
def resume
if @runner.update_attributes(active: true)
redirect_to ci_project_runners_path(@project, @runner), notice: 'Runner was successfully updated.'
else
redirect_to ci_project_runners_path(@project, @runner), alert: 'Runner was not updated.'
end
end
def pause
if @runner.update_attributes(active: false)
redirect_to ci_project_runners_path(@project, @runner), notice: 'Runner was successfully updated.'
else
redirect_to ci_project_runners_path(@project, @runner), alert: 'Runner was not updated.'
end
end
def show
end
protected
def project
@project = Ci::Project.find(params[:project_id])
end
def set_runner
@runner ||= @project.runners.find(params[:id])
end
def runner_params
params.require(:runner).permit(:description, :tag_list, :contacted_at, :active)
end
end
end
module Ci
class TriggersController < Ci::ApplicationController
before_action :authenticate_user!
before_action :project
before_action :authorize_access_project!
before_action :authorize_manage_project!
layout 'ci/project'
def index
@triggers = @project.triggers
@trigger = Ci::Trigger.new
end
def create
@trigger = @project.triggers.new
@trigger.save
if @trigger.valid?
redirect_to ci_project_triggers_path(@project)
else
@triggers = @project.triggers.select(&:persisted?)
render :index
end
end
def destroy
trigger.destroy
redirect_to ci_project_triggers_path(@project)
end
private
def trigger
@trigger ||= @project.triggers.find(params[:id])
end
def project
@project = Ci::Project.find(params[:project_id])
end
end
end
module Ci
class VariablesController < Ci::ApplicationController
before_action :authenticate_user!
before_action :project
before_action :authorize_access_project!
before_action :authorize_manage_project!
layout 'ci/project'
def show
end
def update
if project.update_attributes(project_params)
Ci::EventService.new.change_project_settings(current_user, project)
redirect_to ci_project_variables_path(project), notice: 'Variables were successfully updated.'
else
render action: 'show'
end
end
private
def project
@project ||= Ci::Project.find(params[:project_id])
end
def project_params
params.require(:project).permit({ variables_attributes: [:id, :key, :value, :_destroy] })
end
end
end
...@@ -4,6 +4,11 @@ class HelpController < ApplicationController ...@@ -4,6 +4,11 @@ class HelpController < ApplicationController
layout 'help' layout 'help'
def index def index
@help_index = File.read(Rails.root.join('doc', 'README.md'))
# Prefix Markdown links with `help/` unless they already have been
# See http://rubular.com/r/nwwhzH6Z8X
@help_index.gsub!(/(\]\()(?!help\/)([^\)\(]+)(\))/, '\1help/\2\3')
end end
def show def show
......
...@@ -9,7 +9,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController ...@@ -9,7 +9,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
end end
def create def create
if current_user.valid_otp?(params[:pin_code]) if current_user.validate_and_consume_otp!(params[:pin_code])
current_user.two_factor_enabled = true current_user.two_factor_enabled = true
@codes = current_user.generate_otp_backup_codes! @codes = current_user.generate_otp_backup_codes!
current_user.save! current_user.save!
......
...@@ -25,4 +25,14 @@ class Projects::ApplicationController < ApplicationController ...@@ -25,4 +25,14 @@ class Projects::ApplicationController < ApplicationController
) )
end end
end end
private
def ci_enabled
return render_404 unless @project.gitlab_ci?
end
def ci_project
@ci_project ||= @project.ensure_gitlab_ci_project
end
end end
class Projects::CiSettingsController < Projects::ApplicationController
before_action :ci_project
before_action :authorize_admin_project!
layout "project_settings"
def edit
end
def update
if ci_project.update_attributes(project_params)
Ci::EventService.new.change_project_settings(current_user, ci_project)
redirect_to edit_namespace_project_ci_settings_path(project.namespace, project), notice: 'Project was successfully updated.'
else
render action: "edit"
end
end
def destroy
ci_project.destroy
Ci::EventService.new.remove_project(current_user, ci_project)
project.gitlab_ci_service.update_attributes(active: false)
redirect_to project_path(project), notice: "CI was disabled for this project"
end
protected
def project_params
params.require(:project).permit(:path, :timeout, :timeout_in_minutes, :default_ref, :always_build,
:polling_interval, :public, :ssh_url_to_repo, :allow_git_fetch, :email_recipients,
:email_add_pusher, :email_only_broken_builds, :coverage_regex, :shared_runners_enabled, :token,
{ variables_attributes: [:id, :key, :value, :_destroy] })
end
end
...@@ -22,6 +22,8 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -22,6 +22,8 @@ class Projects::CommitController < Projects::ApplicationController
commit_id: @commit.id commit_id: @commit.id
} }
@ci_commit = project.ci_commit(commit.sha)
respond_to do |format| respond_to do |format|
format.html format.html
format.diff { render text: @commit.to_diff } format.diff { render text: @commit.to_diff }
......
...@@ -16,10 +16,12 @@ class Projects::CompareController < Projects::ApplicationController ...@@ -16,10 +16,12 @@ class Projects::CompareController < Projects::ApplicationController
compare_result = CompareService.new. compare_result = CompareService.new.
execute(@project, head_ref, @project, base_ref) execute(@project, head_ref, @project, base_ref)
@commits = compare_result.commits if compare_result
@diffs = compare_result.diffs @commits = compare_result.commits
@commit = @commits.last @diffs = compare_result.diffs
@line_notes = [] @commit = @commits.last
@line_notes = []
end
end end
def create def create
......
...@@ -5,6 +5,7 @@ class Projects::GraphsController < Projects::ApplicationController ...@@ -5,6 +5,7 @@ class Projects::GraphsController < Projects::ApplicationController
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :assign_ref_vars before_action :assign_ref_vars
before_action :authorize_download_code! before_action :authorize_download_code!
before_action :ci_enabled, only: :ci
def show def show
respond_to do |format| respond_to do |format|
...@@ -23,6 +24,16 @@ class Projects::GraphsController < Projects::ApplicationController ...@@ -23,6 +24,16 @@ class Projects::GraphsController < Projects::ApplicationController
@commits_per_month = @commits_graph.commits_per_month @commits_per_month = @commits_graph.commits_per_month
end end
def ci
ci_project = @project.gitlab_ci_project
@charts = {}
@charts[:week] = Ci::Charts::WeekChart.new(ci_project)
@charts[:month] = Ci::Charts::MonthChart.new(ci_project)
@charts[:year] = Ci::Charts::YearChart.new(ci_project)
@charts[:build_times] = Ci::Charts::BuildTime.new(ci_project)
end
private private
def fetch_graph def fetch_graph
......
...@@ -7,6 +7,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -7,6 +7,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits] before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits]
before_action :validates_merge_request, only: [:show, :diffs, :commits] before_action :validates_merge_request, only: [:show, :diffs, :commits]
before_action :define_show_vars, only: [:show, :diffs, :commits] before_action :define_show_vars, only: [:show, :diffs, :commits]
before_action :ensure_ref_fetched, only: [:show, :commits, :diffs]
# Allow read any merge_request # Allow read any merge_request
before_action :authorize_read_merge_request! before_action :authorize_read_merge_request!
...@@ -257,8 +258,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -257,8 +258,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@commits = @merge_request.commits @commits = @merge_request.commits
@merge_request_diff = @merge_request.merge_request_diff @merge_request_diff = @merge_request.merge_request_diff
@source_branch = @merge_request.source_project.repository.find_branch(@merge_request.source_branch).try(:name)
if @merge_request.locked_long_ago? if @merge_request.locked_long_ago?
@merge_request.unlock_mr @merge_request.unlock_mr
@merge_request.close @merge_request.close
...@@ -277,4 +277,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -277,4 +277,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
:state_event, :description, :task_num, label_ids: [] :state_event, :description, :task_num, label_ids: []
) )
end end
# Make sure merge requests created before 8.0
# have head file in refs/merge-requests/
def ensure_ref_fetched
@merge_request.ensure_ref_fetched
end
end end
class Projects::RunnersController < Projects::ApplicationController
before_action :ci_project
before_action :set_runner, only: [:edit, :update, :destroy, :pause, :resume, :show]
before_action :authorize_admin_project!
layout 'project_settings'
def index
@runners = @ci_project.runners.order('id DESC')
@specific_runners =
Ci::Runner.specific.includes(:runner_projects).
where(Ci::RunnerProject.table_name => { project_id: current_user.authorized_projects } ).
where.not(id: @runners).order("#{Ci::Runner.table_name}.id DESC").page(params[:page]).per(20)
@shared_runners = Ci::Runner.shared.active
@shared_runners_count = @shared_runners.count(:all)
end
def edit
end
def update
if @runner.update_attributes(runner_params)
redirect_to runner_path(@runner), notice: 'Runner was successfully updated.'
else
redirect_to runner_path(@runner), alert: 'Runner was not updated.'
end
end
def destroy
if @runner.only_for?(@ci_project)
@runner.destroy
end
redirect_to runners_path(@project)
end
def resume
if @runner.update_attributes(active: true)
redirect_to runner_path(@runner), notice: 'Runner was successfully updated.'
else
redirect_to runner_path(@runner), alert: 'Runner was not updated.'
end
end
def pause
if @runner.update_attributes(active: false)
redirect_to runner_path(@runner), notice: 'Runner was successfully updated.'
else
redirect_to runner_path(@runner), alert: 'Runner was not updated.'
end
end
def show
end
protected
def set_runner
@runner ||= @ci_project.runners.find(params[:id])
end
def runner_params
params.require(:runner).permit(:description, :tag_list, :contacted_at, :active)
end
end
class Projects::TriggersController < Projects::ApplicationController
before_action :ci_project
before_action :authorize_admin_project!
layout 'project_settings'
def index
@triggers = @ci_project.triggers
@trigger = Ci::Trigger.new
end
def create
@trigger = @ci_project.triggers.new
@trigger.save
if @trigger.valid?
redirect_to namespace_project_triggers_path(@project.namespace, @project)
else
@triggers = @ci_project.triggers.select(&:persisted?)
render :index
end
end
def destroy
trigger.destroy
redirect_to namespace_project_triggers_path(@project.namespace, @project)
end
private
def trigger
@trigger ||= @ci_project.triggers.find(params[:id])
end
end
class Projects::VariablesController < Projects::ApplicationController
before_action :ci_project
before_action :authorize_admin_project!
layout 'project_settings'
def show
end
def update
if ci_project.update_attributes(project_params)
Ci::EventService.new.change_project_settings(current_user, ci_project)
redirect_to namespace_project_variables_path(project.namespace, project), notice: 'Variables were successfully updated.'
else
render action: 'show'
end
end
private
def project_params
params.require(:project).permit({ variables_attributes: [:id, :key, :value, :_destroy] })
end
end
...@@ -86,6 +86,10 @@ class ProjectsController < ApplicationController ...@@ -86,6 +86,10 @@ class ProjectsController < ApplicationController
if @project.empty_repo? if @project.empty_repo?
render 'projects/empty' render 'projects/empty'
else else
if current_user
@membership = @project.project_member_by_id(current_user.id)
end
render :show render :show
end end
else else
......
...@@ -22,6 +22,10 @@ class RootController < Dashboard::ProjectsController ...@@ -22,6 +22,10 @@ class RootController < Dashboard::ProjectsController
when 'stars' when 'stars'
flash.keep flash.keep
redirect_to starred_dashboard_projects_path redirect_to starred_dashboard_projects_path
when 'project_activity'
redirect_to activity_dashboard_path
when 'starred_project_activity'
redirect_to activity_dashboard_path(filter: 'starred')
else else
return return
end end
......
...@@ -99,7 +99,7 @@ class SessionsController < Devise::SessionsController ...@@ -99,7 +99,7 @@ class SessionsController < Devise::SessionsController
end end
def valid_otp_attempt?(user) def valid_otp_attempt?(user)
user.valid_otp?(user_params[:otp_attempt]) || user.validate_and_consume_otp!(user_params[:otp_attempt]) ||
user.invalidate_otp_backup_code!(user_params[:otp_attempt]) user.invalidate_otp_backup_code!(user_params[:otp_attempt])
end end
......
module AuthHelper module AuthHelper
PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2).freeze PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2).freeze
FORM_BASED_PROVIDERS = [/\Aldap/, 'kerberos', 'crowd'].freeze FORM_BASED_PROVIDERS = [/\Aldap/, 'crowd'].freeze
def ldap_enabled? def ldap_enabled?
Gitlab.config.ldap.enabled Gitlab.config.ldap.enabled
......
module BuildsHelper
def build_ref_link build
gitlab_ref_link build.project, build.ref
end
def build_compare_link build
gitlab_compare_link build.project, build.commit.short_before_sha, build.short_sha
end
def build_commit_link build
gitlab_commit_link build.project, build.short_sha
end
def build_url(build)
ci_project_build_url(build.project, build)
end
end
module Ci
module ApplicationHelper
def loader_html
image_tag 'ci/loader.gif', alt: 'Loading'
end
def date_from_to(from, to)
"#{from.to_s(:short)} - #{to.to_s(:short)}"
end
def duration_in_words(finished_at, started_at)
if finished_at && started_at
interval_in_seconds = finished_at.to_i - started_at.to_i
elsif started_at
interval_in_seconds = Time.now.to_i - started_at.to_i
end
time_interval_in_words(interval_in_seconds)
end
def time_interval_in_words(interval_in_seconds)
minutes = interval_in_seconds / 60
seconds = interval_in_seconds - minutes * 60
if minutes >= 1
"#{pluralize(minutes, "minute")} #{pluralize(seconds, "second")}"
else
"#{pluralize(seconds, "second")}"
end
end
end
end
module Ci
module BuildsHelper
def build_ref_link build
gitlab_ref_link build.project, build.ref
end
def build_compare_link build
gitlab_compare_link build.project, build.commit.short_before_sha, build.short_sha
end
def build_commit_link build
gitlab_commit_link build.project, build.short_sha
end
def build_url(build)
ci_project_build_url(build.project, build)
end
def build_status_alert_class(build)
if build.success?
'alert-success'
elsif build.failed?
'alert-danger'
elsif build.canceled?
'alert-disabled'
else
'alert-warning'
end
end
def build_icon_css_class(build)
if build.success?
'fa-circle cgreen'
elsif build.failed?
'fa-circle cred'
else
'fa-circle light'
end
end
end
end
module Ci module Ci
module CommitsHelper module CommitsHelper
def commit_status_alert_class(commit)
return 'alert-info' unless commit
case commit.status
when 'success'
'alert-success'
when 'failed', 'canceled'
'alert-danger'
when 'skipped'
'alert-disabled'
else
'alert-warning'
end
end
def ci_commit_path(commit) def ci_commit_path(commit)
ci_project_ref_commits_path(commit.project, commit.ref, commit.sha) ci_project_ref_commits_path(commit.project, commit.ref, commit.sha)
end end
......
...@@ -27,9 +27,9 @@ module Ci ...@@ -27,9 +27,9 @@ module Ci
commits = project.commits commits = project.commits
if commits.any? && commits.last.push_data[:ci_yaml_file] if commits.any? && commits.last.push_data[:ci_yaml_file]
"#{@project.gitlab_url}/edit/master/.gitlab-ci.yml" "#{project.gitlab_url}/edit/master/.gitlab-ci.yml"
else else
"#{@project.gitlab_url}/new/master" "#{project.gitlab_url}/new/master"
end end
end end
end end
......
module Ci
module IconsHelper
def boolean_to_icon(value)
if value.to_s == "true"
content_tag :i, nil, class: 'fa fa-circle cgreen'
else
content_tag :i, nil, class: 'fa fa-power-off clgray'
end
end
end
end
module Ci
module RoutesHelper
class Base
include Gitlab::Application.routes.url_helpers
def default_url_options
{
host: Settings.gitlab['host'],
protocol: Settings.gitlab['https'] ? "https" : "http",
port: Settings.gitlab['port']
}
end
end
def url_helpers
@url_helpers ||= Base.new
end
def self.method_missing(method, *args, &block)
@url_helpers ||= Base.new
if @url_helpers.respond_to?(method)
@url_helpers.send(method, *args, &block)
else
super method, *args, &block
end
end
end
end
module Ci
module RunnersHelper
def runner_status_icon(runner)
unless runner.contacted_at
return content_tag :i, nil,
class: "fa fa-warning-sign",
title: "New runner. Has not connected yet"
end
status =
if runner.active?
runner.contacted_at > 3.hour.ago ? :online : :offline
else
:paused
end
content_tag :i, nil,
class: "fa fa-circle runner-status-#{status}",
title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago"
end
end
end
module Ci
module TriggersHelper
def ci_build_trigger_url(project_id, ref_name)
"#{Settings.gitlab_ci.url}/ci/api/v1/projects/#{project_id}/refs/#{ref_name}/trigger"
end
end
end
module Ci
module UserHelper
def user_avatar_url(user = nil, size = nil, default = 'identicon')
size = 40 if size.nil? || size <= 0
if user.blank? || user.avatar_url.blank?
'ci/no_avatar.png'
elsif /^(http(s?):\/\/(www|secure)\.gravatar\.com\/avatar\/(\w*))/ =~ user.avatar_url
Regexp.last_match[0] + "?s=#{size}&d=#{default}"
else
user.avatar_url
end
end
end
end
module CiStatusHelper
def ci_status_path(ci_commit)
ci_project_ref_commits_path(ci_commit.project, ci_commit.ref, ci_commit)
end
def ci_status_icon(ci_commit)
ci_icon_for_status(ci_commit.status)
end
def ci_status_color(ci_commit)
case ci_commit.status
when 'success'
'green'
when 'failed'
'red'
when 'running', 'pending'
'yellow'
else
'gray'
end
end
def ci_status_with_icon(status)
content_tag :span, class: "ci-status ci-#{status}" do
ci_icon_for_status(status) + '&nbsp;'.html_safe + status
end
end
def ci_icon_for_status(status)
icon_name =
case status
when 'success'
'check'
when 'failed'
'close'
when 'running', 'pending'
'clock-o'
else
'circle'
end
icon(icon_name)
end
end
...@@ -135,7 +135,7 @@ module CommitsHelper ...@@ -135,7 +135,7 @@ module CommitsHelper
# size: size of the avatar image in px # size: size of the avatar image in px
def commit_person_link(commit, options = {}) def commit_person_link(commit, options = {})
user = commit.send(options[:source]) user = commit.send(options[:source])
source_name = clean(commit.send "#{options[:source]}_name".to_sym) source_name = clean(commit.send "#{options[:source]}_name".to_sym)
source_email = clean(commit.send "#{options[:source]}_email".to_sym) source_email = clean(commit.send "#{options[:source]}_email".to_sym)
......
...@@ -45,7 +45,7 @@ module GitlabMarkdownHelper ...@@ -45,7 +45,7 @@ module GitlabMarkdownHelper
end end
def markdown(text, context = {}) def markdown(text, context = {})
context.merge!( context.reverse_merge!(
current_user: current_user, current_user: current_user,
path: @path, path: @path,
project: @project, project: @project,
...@@ -59,7 +59,7 @@ module GitlabMarkdownHelper ...@@ -59,7 +59,7 @@ module GitlabMarkdownHelper
# TODO (rspeicher): Remove all usages of this helper and just call `markdown` # TODO (rspeicher): Remove all usages of this helper and just call `markdown`
# with a custom pipeline depending on the content being rendered # with a custom pipeline depending on the content being rendered
def gfm(text, options = {}) def gfm(text, options = {})
options.merge!( options.reverse_merge!(
current_user: current_user, current_user: current_user,
path: @path, path: @path,
project: @project, project: @project,
...@@ -165,7 +165,7 @@ module GitlabMarkdownHelper ...@@ -165,7 +165,7 @@ module GitlabMarkdownHelper
# and return true. Otherwise return false. # and return true. Otherwise return false.
def truncate_if_block(node, truncated) def truncate_if_block(node, truncated)
if node.element? && node.description.block? && !truncated if node.element? && node.description.block? && !truncated
node.content = "#{node.content}..." if node.next_sibling node.inner_html = "#{node.inner_html}..." if node.next_sibling
true true
else else
truncated truncated
......
...@@ -33,6 +33,14 @@ module GitlabRoutingHelper ...@@ -33,6 +33,14 @@ module GitlabRoutingHelper
edit_namespace_project_path(project.namespace, project, *args) edit_namespace_project_path(project.namespace, project, *args)
end end
def runners_path(project, *args)
namespace_project_runners_path(project.namespace, project, *args)
end
def runner_path(runner, *args)
namespace_project_runner_path(@project.namespace, @project, runner, *args)
end
def issue_path(entity, *args) def issue_path(entity, *args)
namespace_project_issue_path(entity.project.namespace, entity.project, entity, *args) namespace_project_issue_path(entity.project.namespace, entity.project, entity, *args)
end end
......
module GraphHelper module GraphHelper
def get_refs(repo, commit) def get_refs(repo, commit)
refs = "" refs = ""
refs << commit.ref_names(repo).join(' ') # Commit::ref_names already strips the refs/XXX from important refs (e.g. refs/heads/XXX)
# so anything leftover is internally used by GitLab
commit_refs = commit.ref_names(repo).reject{ |name| name.starts_with?('refs/') }
refs << commit_refs.join(' ')
# append note count # append note count
refs << "[#{@graph.notes[commit.id]}]" if @graph.notes[commit.id] > 0 refs << "[#{@graph.notes[commit.id]}]" if @graph.notes[commit.id] > 0
......
...@@ -12,4 +12,49 @@ module NotificationsHelper ...@@ -12,4 +12,49 @@ module NotificationsHelper
icon('circle-o', class: 'ns-default') icon('circle-o', class: 'ns-default')
end end
end end
def notification_list_item(notification_level, user_membership)
case notification_level
when Notification::N_DISABLED
content_tag(:li, class: active_level_for(user_membership, Notification::N_DISABLED)) do
link_to '#', class: 'update-notification', data: { notification_level: Notification::N_DISABLED } do
icon('microphone-slash fw', text: 'Disabled')
end
end
when Notification::N_PARTICIPATING
content_tag(:li, class: active_level_for(user_membership, Notification::N_PARTICIPATING)) do
link_to '#', class: 'update-notification', data: { notification_level: Notification::N_PARTICIPATING } do
icon('volume-up fw', text: 'Participate')
end
end
when Notification::N_WATCH
content_tag(:li, class: active_level_for(user_membership, Notification::N_WATCH)) do
link_to '#', class: 'update-notification', data: { notification_level: Notification::N_WATCH } do
icon('eye fw', text: 'Watch')
end
end
when Notification::N_MENTION
content_tag(:li, class: active_level_for(user_membership, Notification::N_MENTION)) do
link_to '#', class: 'update-notification', data: { notification_level: Notification::N_MENTION } do
icon('at fw', text: 'On mention')
end
end
when Notification::N_GLOBAL
content_tag(:li, class: active_level_for(user_membership, Notification::N_GLOBAL)) do
link_to '#', class: 'update-notification', data: { notification_level: Notification::N_GLOBAL } do
icon('globe fw', text: 'Global')
end
end
else
# do nothing
end
end
def notification_label(user_membership)
Notification.new(user_membership).to_s
end
def active_level_for(user_membership, level)
'active' if user_membership.notification_level == level
end
end end
...@@ -3,7 +3,9 @@ module PreferencesHelper ...@@ -3,7 +3,9 @@ module PreferencesHelper
# Maps `dashboard` values to more user-friendly option text # Maps `dashboard` values to more user-friendly option text
DASHBOARD_CHOICES = { DASHBOARD_CHOICES = {
projects: 'Your Projects (default)', projects: 'Your Projects (default)',
stars: 'Starred Projects' stars: 'Starred Projects',
project_activity: "Your Projects' Activity",
starred_project_activity: "Starred Projects' Activity"
}.with_indifferent_access.freeze }.with_indifferent_access.freeze
# Returns an Array usable by a select field for more user-friendly option text # Returns an Array usable by a select field for more user-friendly option text
......
...@@ -156,8 +156,8 @@ module ProjectsHelper ...@@ -156,8 +156,8 @@ module ProjectsHelper
end end
end end
def repository_size(project = nil) def repository_size(project = @project)
"#{(project || @project).repository_size} MB" "#{project.repository_size} MB"
rescue rescue
# In order to prevent 500 error # In order to prevent 500 error
# when application cannot allocate memory # when application cannot allocate memory
......
module RunnersHelper
def runner_status_icon(runner)
unless runner.contacted_at
return content_tag :i, nil,
class: "fa fa-warning-sign",
title: "New runner. Has not connected yet"
end
status =
if runner.active?
runner.contacted_at > 3.hour.ago ? :online : :offline
else
:paused
end
content_tag :i, nil,
class: "fa fa-circle runner-status-#{status}",
title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago"
end
end
module TimeHelper
def duration_in_words(finished_at, started_at)
if finished_at && started_at
interval_in_seconds = finished_at.to_i - started_at.to_i
elsif started_at
interval_in_seconds = Time.now.to_i - started_at.to_i
end
time_interval_in_words(interval_in_seconds)
end
def time_interval_in_words(interval_in_seconds)
minutes = interval_in_seconds / 60
seconds = interval_in_seconds - minutes * 60
if minutes >= 1
"#{pluralize(minutes, "minute")} #{pluralize(seconds, "second")}"
else
"#{pluralize(seconds, "second")}"
end
end
def date_from_to(from, to)
"#{from.to_s(:short)} - #{to.to_s(:short)}"
end
end
module TriggersHelper
def ci_build_trigger_url(project_id, ref_name)
"#{Settings.gitlab_ci.url}/ci/api/v1/projects/#{project_id}/refs/#{ref_name}/trigger"
end
end
module VersionCheckHelper module VersionCheckHelper
def version_status_badge def version_status_badge
if Rails.env.production? if Rails.env.production? && current_application_settings.version_check_enabled
image_tag VersionCheck.new.url image_tag VersionCheck.new.url
end end
end end
......
...@@ -2,7 +2,6 @@ module Ci ...@@ -2,7 +2,6 @@ module Ci
class Notify < ActionMailer::Base class Notify < ActionMailer::Base
include Ci::Emails::Builds include Ci::Emails::Builds
add_template_helper Ci::ApplicationHelper
add_template_helper Ci::GitlabHelper add_template_helper Ci::GitlabHelper
default_url_options[:host] = Gitlab.config.gitlab.host default_url_options[:host] = Gitlab.config.gitlab.host
......
...@@ -12,7 +12,7 @@ module Emails ...@@ -12,7 +12,7 @@ module Emails
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@commit.title} (#{@commit.short_id})")) subject: subject("#{@commit.title} (#{@commit.short_id})"))
SentNotification.record(@commit, recipient_id, reply_key) SentNotification.record_note(@note, recipient_id, reply_key)
end end
def note_issue_email(recipient_id, note_id) def note_issue_email(recipient_id, note_id)
...@@ -27,7 +27,7 @@ module Emails ...@@ -27,7 +27,7 @@ module Emails
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})")) subject: subject("#{@issue.title} (##{@issue.iid})"))
SentNotification.record(@issue, recipient_id, reply_key) SentNotification.record_note(@note, recipient_id, reply_key)
end end
def note_merge_request_email(recipient_id, note_id) def note_merge_request_email(recipient_id, note_id)
...@@ -43,7 +43,7 @@ module Emails ...@@ -43,7 +43,7 @@ module Emails
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})")) subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
SentNotification.record(@merge_request, recipient_id, reply_key) SentNotification.record_note(@note, recipient_id, reply_key)
end end
end end
end end
...@@ -110,7 +110,7 @@ class Notify < BaseMailer ...@@ -110,7 +110,7 @@ class Notify < BaseMailer
if reply_key if reply_key
headers['X-GitLab-Reply-Key'] = reply_key headers['X-GitLab-Reply-Key'] = reply_key
address = Mail::Address.new(Gitlab::ReplyByEmail.reply_address(reply_key)) address = Mail::Address.new(Gitlab::IncomingEmail.reply_address(reply_key))
address.display_name = @project.name_with_namespace address.display_name = @project.name_with_namespace
headers['Reply-To'] = address headers['Reply-To'] = address
...@@ -150,6 +150,6 @@ class Notify < BaseMailer ...@@ -150,6 +150,6 @@ class Notify < BaseMailer
end end
def reply_key def reply_key
@reply_key ||= Gitlab::ReplyByEmail.reply_key @reply_key ||= SentNotification.reply_key
end end
end end
...@@ -30,7 +30,6 @@ module Ci ...@@ -30,7 +30,6 @@ module Ci
LAZY_ATTRIBUTES = ['trace'] LAZY_ATTRIBUTES = ['trace']
belongs_to :commit, class_name: 'Ci::Commit' belongs_to :commit, class_name: 'Ci::Commit'
belongs_to :project, class_name: 'Ci::Project'
belongs_to :runner, class_name: 'Ci::Runner' belongs_to :runner, class_name: 'Ci::Runner'
belongs_to :trigger_request, class_name: 'Ci::TriggerRequest' belongs_to :trigger_request, class_name: 'Ci::TriggerRequest'
...@@ -80,7 +79,6 @@ module Ci ...@@ -80,7 +79,6 @@ module Ci
new_build.commands = build.commands new_build.commands = build.commands
new_build.tag_list = build.tag_list new_build.tag_list = build.tag_list
new_build.commit_id = build.commit_id new_build.commit_id = build.commit_id
new_build.project_id = build.project_id
new_build.name = build.name new_build.name = build.name
new_build.allow_failure = build.allow_failure new_build.allow_failure = build.allow_failure
new_build.stage = build.stage new_build.stage = build.stage
...@@ -137,7 +135,7 @@ module Ci ...@@ -137,7 +135,7 @@ module Ci
state :canceled, value: 'canceled' state :canceled, value: 'canceled'
end end
delegate :sha, :short_sha, :before_sha, :ref, delegate :sha, :short_sha, :before_sha, :ref, :project,
to: :commit, prefix: false to: :commit, prefix: false
def trace_html def trace_html
...@@ -188,7 +186,7 @@ module Ci ...@@ -188,7 +186,7 @@ module Ci
end end
def project_id def project_id
commit.project_id commit.project.id
end end
def project_name def project_name
......
...@@ -18,8 +18,8 @@ ...@@ -18,8 +18,8 @@
module Ci module Ci
class Commit < ActiveRecord::Base class Commit < ActiveRecord::Base
extend Ci::Model extend Ci::Model
belongs_to :project, class_name: 'Ci::Project' belongs_to :gl_project, class_name: '::Project', foreign_key: :gl_project_id
has_many :builds, dependent: :destroy, class_name: 'Ci::Build' has_many :builds, dependent: :destroy, class_name: 'Ci::Build'
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest' has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest'
...@@ -36,6 +36,14 @@ module Ci ...@@ -36,6 +36,14 @@ module Ci
sha sha
end end
def project
@project ||= gl_project.ensure_gitlab_ci_project
end
def project_id
project.id
end
def last_build def last_build
builds.order(:id).last builds.order(:id).last
end end
...@@ -111,15 +119,14 @@ module Ci ...@@ -111,15 +119,14 @@ module Ci
builds_attrs = config_processor.builds_for_stage_and_ref(stage, ref, tag) builds_attrs = config_processor.builds_for_stage_and_ref(stage, ref, tag)
builds_attrs.map do |build_attrs| builds_attrs.map do |build_attrs|
builds.create!({ builds.create!({
project: project, name: build_attrs[:name],
name: build_attrs[:name], commands: build_attrs[:script],
commands: build_attrs[:script], tag_list: build_attrs[:tags],
tag_list: build_attrs[:tags], options: build_attrs[:options],
options: build_attrs[:options], allow_failure: build_attrs[:allow_failure],
allow_failure: build_attrs[:allow_failure], stage: build_attrs[:stage],
stage: build_attrs[:stage], trigger_request: trigger_request,
trigger_request: trigger_request, })
})
end end
end end
...@@ -236,7 +243,7 @@ module Ci ...@@ -236,7 +243,7 @@ module Ci
end end
def config_processor def config_processor
@config_processor ||= Ci::GitlabCiYamlProcessor.new(push_data[:ci_yaml_file] || project.generated_yaml_config) @config_processor ||= Ci::GitlabCiYamlProcessor.new(push_data[:ci_yaml_file])
rescue Ci::GitlabCiYamlProcessor::ValidationError => e rescue Ci::GitlabCiYamlProcessor::ValidationError => e
save_yaml_error(e.message) save_yaml_error(e.message)
nil nil
......
...@@ -33,8 +33,6 @@ module Ci ...@@ -33,8 +33,6 @@ module Ci
belongs_to :gl_project, class_name: '::Project', foreign_key: :gitlab_id belongs_to :gl_project, class_name: '::Project', foreign_key: :gitlab_id
has_many :commits, ->() { order('CASE WHEN ci_commits.committed_at IS NULL THEN 0 ELSE 1 END', :committed_at, :id) }, dependent: :destroy, class_name: 'Ci::Commit'
has_many :builds, through: :commits, dependent: :destroy, class_name: 'Ci::Build'
has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject'
has_many :runners, through: :runner_projects, class_name: 'Ci::Runner' has_many :runners, through: :runner_projects, class_name: 'Ci::Runner'
has_many :web_hooks, dependent: :destroy, class_name: 'Ci::WebHook' has_many :web_hooks, dependent: :destroy, class_name: 'Ci::WebHook'
...@@ -54,13 +52,13 @@ module Ci ...@@ -54,13 +52,13 @@ module Ci
# Validations # Validations
# #
validates_presence_of :name, :timeout, :token, :default_ref, validates_presence_of :name, :timeout, :token, :default_ref,
:path, :ssh_url_to_repo, :gitlab_id :path, :ssh_url_to_repo, :gitlab_id
validates_uniqueness_of :gitlab_id validates_uniqueness_of :gitlab_id
validates :polling_interval, validates :polling_interval,
presence: true, presence: true,
if: ->(project) { project.always_build.present? } if: ->(project) { project.always_build.present? }
scope :public_only, ->() { where(public: true) } scope :public_only, ->() { where(public: true) }
...@@ -78,12 +76,12 @@ module Ci ...@@ -78,12 +76,12 @@ module Ci
def parse(project) def parse(project)
params = { params = {
name: project.name_with_namespace, name: project.name_with_namespace,
gitlab_id: project.id, gitlab_id: project.id,
path: project.path_with_namespace, path: project.path_with_namespace,
default_ref: project.default_branch || 'master', default_ref: project.default_branch || 'master',
ssh_url_to_repo: project.ssh_url_to_repo, ssh_url_to_repo: project.ssh_url_to_repo,
email_add_pusher: current_application_settings.add_pusher, email_add_pusher: current_application_settings.add_pusher,
email_only_broken_builds: current_application_settings.all_broken_builds, email_only_broken_builds: current_application_settings.all_broken_builds,
} }
...@@ -92,21 +90,6 @@ module Ci ...@@ -92,21 +90,6 @@ module Ci
project project
end end
# TODO: remove
def from_gitlab(user, scope = :owned, options)
opts = user.authenticate_options
opts.merge! options
raise 'Implement me of fix'
#projects = Ci::Network.new.projects(opts.compact, scope)
if projects
projects.map { |pr| OpenStruct.new(pr) }
else
[]
end
end
def already_added?(project) def already_added?(project)
where(gitlab_id: project.id).any? where(gitlab_id: project.id).any?
end end
...@@ -114,12 +97,12 @@ module Ci ...@@ -114,12 +97,12 @@ module Ci
def unassigned(runner) def unassigned(runner)
joins("LEFT JOIN #{Ci::RunnerProject.table_name} ON #{Ci::RunnerProject.table_name}.project_id = #{Ci::Project.table_name}.id " \ joins("LEFT JOIN #{Ci::RunnerProject.table_name} ON #{Ci::RunnerProject.table_name}.project_id = #{Ci::Project.table_name}.id " \
"AND #{Ci::RunnerProject.table_name}.runner_id = #{runner.id}"). "AND #{Ci::RunnerProject.table_name}.runner_id = #{runner.id}").
where('#{Ci::RunnerProject.table_name}.project_id' => nil) where("#{Ci::RunnerProject.table_name}.project_id" => nil)
end end
def ordered_by_last_commit_date def ordered_by_last_commit_date
last_commit_subquery = "(SELECT project_id, MAX(committed_at) committed_at FROM #{Ci::Commit.table_name} GROUP BY 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}.id = last_commit.project_id"). joins("LEFT JOIN #{last_commit_subquery} AS last_commit ON #{Ci::Project.table_name}.gitlab_id = last_commit.gl_project_id").
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
...@@ -139,10 +122,14 @@ module Ci ...@@ -139,10 +122,14 @@ module Ci
def set_default_values def set_default_values
self.token = SecureRandom.hex(15) if self.token.blank? self.token = SecureRandom.hex(15) if self.token.blank?
self.default_ref ||= 'master'
self.name ||= gl_project.name_with_namespace
self.path ||= gl_project.path_with_namespace
self.ssh_url_to_repo ||= gl_project.ssh_url_to_repo
end end
def tracked_refs def tracked_refs
@tracked_refs ||= default_ref.split(",").map{|ref| ref.strip} @tracked_refs ||= default_ref.split(",").map { |ref| ref.strip }
end end
def valid_token? token def valid_token? token
...@@ -221,5 +208,13 @@ module Ci ...@@ -221,5 +208,13 @@ module Ci
def setup_finished? def setup_finished?
commits.any? commits.any?
end end
def commits
gl_project.ci_commits
end
def builds
gl_project.ci_builds
end
end end
end end
...@@ -41,6 +41,10 @@ module Ci ...@@ -41,6 +41,10 @@ module Ci
query: "%#{query.try(:downcase)}%") query: "%#{query.try(:downcase)}%")
end end
def gl_projects_ids
projects.select(:gitlab_id)
end
def set_default_values def set_default_values
self.token = SecureRandom.hex(15) if self.token.blank? self.token = SecureRandom.hex(15) if self.token.blank?
end end
......
...@@ -433,10 +433,22 @@ class MergeRequest < ActiveRecord::Base ...@@ -433,10 +433,22 @@ class MergeRequest < ActiveRecord::Base
target_project.repository.fetch_ref( target_project.repository.fetch_ref(
source_project.repository.path_to_repo, source_project.repository.path_to_repo,
"refs/heads/#{source_branch}", "refs/heads/#{source_branch}",
"refs/merge-requests/#{iid}/head" ref_path
) )
end end
def ref_path
"refs/merge-requests/#{iid}/head"
end
def ref_is_fetched?
File.exists?(File.join(project.repository.path_to_repo, ref_path))
end
def ensure_ref_fetched
fetch_ref unless ref_is_fetched?
end
def in_locked_state def in_locked_state
begin begin
lock_mr lock_mr
......
...@@ -123,12 +123,12 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -123,12 +123,12 @@ class MergeRequestDiff < ActiveRecord::Base
if new_diffs.any? if new_diffs.any?
if new_diffs.size > Commit::DIFF_HARD_LIMIT_FILES if new_diffs.size > Commit::DIFF_HARD_LIMIT_FILES
self.state = :overflow_diff_files_limit self.state = :overflow_diff_files_limit
new_diffs = new_diffs.first[Commit::DIFF_HARD_LIMIT_LINES] new_diffs = new_diffs.first(Commit::DIFF_HARD_LIMIT_LINES)
end end
if new_diffs.sum { |diff| diff.diff.lines.count } > Commit::DIFF_HARD_LIMIT_LINES if new_diffs.sum { |diff| diff.diff.lines.count } > Commit::DIFF_HARD_LIMIT_LINES
self.state = :overflow_diff_lines_limit self.state = :overflow_diff_lines_limit
new_diffs = new_diffs.first[Commit::DIFF_HARD_LIMIT_LINES] new_diffs = new_diffs.first(Commit::DIFF_HARD_LIMIT_LINES)
end end
end end
......
...@@ -12,7 +12,7 @@ class Notification ...@@ -12,7 +12,7 @@ class Notification
class << self class << self
def notification_levels def notification_levels
[N_DISABLED, N_PARTICIPATING, N_WATCH, N_MENTION] [N_DISABLED, N_MENTION, N_PARTICIPATING, N_WATCH]
end end
def options_with_labels def options_with_labels
...@@ -26,7 +26,7 @@ class Notification ...@@ -26,7 +26,7 @@ class Notification
end end
def project_notification_levels def project_notification_levels
[N_DISABLED, N_PARTICIPATING, N_WATCH, N_GLOBAL, N_MENTION] [N_DISABLED, N_MENTION, N_PARTICIPATING, N_WATCH, N_GLOBAL]
end end
end end
...@@ -57,4 +57,21 @@ class Notification ...@@ -57,4 +57,21 @@ class Notification
def level def level
target.notification_level target.notification_level
end end
def to_s
case level
when N_DISABLED
'Disabled'
when N_PARTICIPATING
'Participating'
when N_WATCH
'Watching'
when N_MENTION
'On mention'
when N_GLOBAL
'Global'
else
# do nothing
end
end
end end
...@@ -39,6 +39,7 @@ class Project < ActiveRecord::Base ...@@ -39,6 +39,7 @@ class Project < ActiveRecord::Base
include Gitlab::VisibilityLevel include Gitlab::VisibilityLevel
include Referable include Referable
include Sortable include Sortable
include AfterCommitQueue
extend Gitlab::ConfigHelper extend Gitlab::ConfigHelper
extend Enumerize extend Enumerize
...@@ -117,6 +118,8 @@ class Project < ActiveRecord::Base ...@@ -117,6 +118,8 @@ class Project < ActiveRecord::Base
has_many :deploy_keys, through: :deploy_keys_projects has_many :deploy_keys, through: :deploy_keys_projects
has_many :users_star_projects, dependent: :destroy has_many :users_star_projects, dependent: :destroy
has_many :starrers, through: :users_star_projects, source: :user has_many :starrers, through: :users_star_projects, source: :user
has_many :ci_commits, ->() { order('CASE WHEN ci_commits.committed_at IS NULL THEN 0 ELSE 1 END', :committed_at, :id) }, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id
has_many :ci_builds, through: :ci_commits, source: :builds, dependent: :destroy, class_name: 'Ci::Build'
has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
has_one :gitlab_ci_project, dependent: :destroy, class_name: "Ci::Project", foreign_key: :gitlab_id has_one :gitlab_ci_project, dependent: :destroy, class_name: "Ci::Project", foreign_key: :gitlab_id
...@@ -191,7 +194,7 @@ class Project < ActiveRecord::Base ...@@ -191,7 +194,7 @@ class Project < ActiveRecord::Base
state :finished state :finished
state :failed state :failed
after_transition any => :started, do: :add_import_job after_transition any => :started, do: :schedule_add_import_job
after_transition any => :finished, do: :clear_import_data after_transition any => :finished, do: :clear_import_data
end end
...@@ -275,13 +278,17 @@ class Project < ActiveRecord::Base ...@@ -275,13 +278,17 @@ class Project < ActiveRecord::Base
id && persisted? id && persisted?
end end
def schedule_add_import_job
run_after_commit(:add_import_job)
end
def add_import_job def add_import_job
if forked? if forked?
unless RepositoryForkWorker.perform_async(id, forked_from_project.path_with_namespace, self.namespace.path) unless RepositoryForkWorker.perform_async(id, forked_from_project.path_with_namespace, self.namespace.path)
import_fail import_fail
end end
else else
RepositoryImportWorker.perform_in(2.seconds, id) RepositoryImportWorker.perform_async(id)
end end
end end
...@@ -428,7 +435,7 @@ class Project < ActiveRecord::Base ...@@ -428,7 +435,7 @@ class Project < ActiveRecord::Base
end end
def gitlab_ci? def gitlab_ci?
gitlab_ci_service && gitlab_ci_service.active gitlab_ci_service && gitlab_ci_service.active && gitlab_ci_project.present?
end end
def ci_services def ci_services
...@@ -735,4 +742,22 @@ class Project < ActiveRecord::Base ...@@ -735,4 +742,22 @@ class Project < ActiveRecord::Base
errors.add(:base, 'Failed create wiki') errors.add(:base, 'Failed create wiki')
false false
end end
def ci_commit(sha)
gitlab_ci_project.commits.find_by(sha: sha) if gitlab_ci?
end
def ensure_gitlab_ci_project
gitlab_ci_project || create_gitlab_ci_project
end
def enable_ci(user)
# Enable service
service = gitlab_ci_service || create_gitlab_ci_service
service.active = true
service.save
# Create Ci::Project
Ci::CreateProjectService.new.execute(user, self)
end
end end
...@@ -69,14 +69,6 @@ class BuildkiteService < CiService ...@@ -69,14 +69,6 @@ class BuildkiteService < CiService
"#{project_url}/builds?commit=#{sha}" "#{project_url}/builds?commit=#{sha}"
end end
def builds_path
"#{project_url}/builds?branch=#{project.default_branch}"
end
def status_img_path
"#{buildkite_endpoint('badge')}/#{status_token}.svg"
end
def title def title
'Buildkite' 'Buildkite'
end end
......
module Ci module Ci
class HipChatMessage class HipChatMessage
include Gitlab::Application.routes.url_helpers
attr_reader :build attr_reader :build
def initialize(build) def initialize(build)
...@@ -8,13 +10,13 @@ module Ci ...@@ -8,13 +10,13 @@ module Ci
def to_s def to_s
lines = Array.new lines = Array.new
lines.push("<a href=\"#{Ci::RoutesHelper.ci_project_url(project)}\">#{project.name}</a> - ") lines.push("<a href=\"#{ci_project_url(project)}\">#{project.name}</a> - ")
if commit.matrix? if commit.matrix?
lines.push("<a href=\"#{Ci::RoutesHelper.ci_project_ref_commits_url(project, commit.ref, commit.sha)}\">Commit ##{commit.id}</a></br>") lines.push("<a href=\"#{ci_project_ref_commits_url(project, commit.ref, commit.sha)}\">Commit ##{commit.id}</a></br>")
else else
first_build = commit.builds_without_retry.first first_build = commit.builds_without_retry.first
lines.push("<a href=\"#{Ci::RoutesHelper.ci_project_build_url(project, first_build)}\">Build '#{first_build.name}' ##{first_build.id}</a></br>") lines.push("<a href=\"#{ci_project_build_url(project, first_build)}\">Build '#{first_build.name}' ##{first_build.id}</a></br>")
end end
lines.push("#{commit.short_sha} #{commit.git_author_name} - #{commit.git_commit_message}</br>") lines.push("#{commit.short_sha} #{commit.git_author_name} - #{commit.git_commit_message}</br>")
......
...@@ -2,6 +2,8 @@ require 'slack-notifier' ...@@ -2,6 +2,8 @@ require 'slack-notifier'
module Ci module Ci
class SlackMessage class SlackMessage
include Gitlab::Application.routes.url_helpers
def initialize(commit) def initialize(commit)
@commit = commit @commit = commit
end end
...@@ -27,7 +29,7 @@ module Ci ...@@ -27,7 +29,7 @@ module Ci
next unless build.failed? next unless build.failed?
fields << { fields << {
title: build.name, title: build.name,
value: "Build <#{Ci::RoutesHelper.ci_project_build_url(project, build)}|\##{build.id}> failed in #{build.duration.to_i} second(s)." value: "Build <#{ci_project_build_url(project, build)}|\##{build.id}> failed in #{build.duration.to_i} second(s)."
} }
end end
end end
...@@ -44,12 +46,12 @@ module Ci ...@@ -44,12 +46,12 @@ module Ci
attr_reader :commit attr_reader :commit
def attachment_message def attachment_message
out = "<#{Ci::RoutesHelper.ci_project_url(project)}|#{project_name}>: " out = "<#{ci_project_url(project)}|#{project_name}>: "
if commit.matrix? if commit.matrix?
out << "Commit <#{Ci::RoutesHelper.ci_project_ref_commits_url(project, commit.ref, commit.sha)}|\##{commit.id}> " out << "Commit <#{ci_project_ref_commits_url(project, commit.ref, commit.sha)}|\##{commit.id}> "
else else
build = commit.builds_without_retry.first build = commit.builds_without_retry.first
out << "Build <#{Ci::RoutesHelper.ci_project_build_path(project, build)}|\##{build.id}> " out << "Build <#{ci_project_build_url(project, build)}|\##{build.id}> "
end end
out << "(<#{commit_sha_link}|#{commit.short_sha}>) " out << "(<#{commit_sha_link}|#{commit.short_sha}>) "
out << "of <#{commit_ref_link}|#{commit.ref}> " out << "of <#{commit_ref_link}|#{commit.ref}> "
......
...@@ -26,7 +26,7 @@ class DroneCiService < CiService ...@@ -26,7 +26,7 @@ class DroneCiService < CiService
format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }, if: :activated? format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }, if: :activated?
validates :token, validates :token,
presence: true, presence: true,
format: { with: /\A([A-Za-z0-9]+)\z/ }, if: :activated? if: :activated?
after_save :compose_service_hook, if: :activated? after_save :compose_service_hook, if: :activated?
...@@ -135,20 +135,6 @@ class DroneCiService < CiService ...@@ -135,20 +135,6 @@ class DroneCiService < CiService
commit_page(sha, ref) commit_page(sha, ref)
end end
def builds_path
url = [drone_url, "#{project.namespace.path}/#{project.path}"]
URI.join(*url).to_s
end
def status_img_path
url = [drone_url,
"api/badges/#{project.namespace.path}/#{project.path}/status.svg",
"?branch=#{URI::encode(project.default_branch)}"]
URI.join(*url).to_s
end
def title def title
'Drone CI' 'Drone CI'
end end
......
...@@ -19,22 +19,12 @@ ...@@ -19,22 +19,12 @@
# #
class GitlabCiService < CiService class GitlabCiService < CiService
API_PREFIX = "api/v1" include Gitlab::Application.routes.url_helpers
prop_accessor :project_url, :token, :enable_ssl_verification
validates :project_url,
presence: true,
format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }, if: :activated?
validates :token,
presence: true,
format: { with: /\A([A-Za-z0-9]+)\z/ }, if: :activated?
after_save :compose_service_hook, if: :activated? after_save :compose_service_hook, if: :activated?
def compose_service_hook def compose_service_hook
hook = service_hook || build_service_hook hook = service_hook || build_service_hook
hook.url = [project_url, "/build", "?token=#{token}"].join("")
hook.enable_ssl_verification = enable_ssl_verification
hook.save hook.save
end end
...@@ -55,71 +45,57 @@ class GitlabCiService < CiService ...@@ -55,71 +45,57 @@ class GitlabCiService < CiService
end end
end end
service_hook.execute(data) ci_project = Ci::Project.find_by(gitlab_id: project.id)
if ci_project
Ci::CreateCommitService.new.execute(ci_project, data)
end
end end
def commit_status_path(sha, ref) def token
URI::encode(project_url + "/refs/#{ref}/commits/#{sha}/status.json?token=#{token}") if project.gitlab_ci_project.present?
project.gitlab_ci_project.token
end
end end
def get_ci_build(sha, ref) def get_ci_commit(sha, ref)
@ci_builds ||= {} Ci::Project.find(project.gitlab_ci_project).commits.find_by_sha_and_ref!(sha, ref)
@ci_builds[sha] ||= HTTParty.get(commit_status_path(sha, ref), verify: false)
end end
def commit_status(sha, ref) def commit_status(sha, ref)
response = get_ci_build(sha, ref) get_ci_commit(sha, ref).status
rescue ActiveRecord::RecordNotFound
if response.code == 200 and response["status"]
response["status"]
else
:error
end
rescue Errno::ECONNREFUSED
:error :error
end end
def fork_registration(new_project, private_token) def fork_registration(new_project, current_user)
params = { params = OpenStruct.new({
id: new_project.id, id: new_project.id,
name_with_namespace: new_project.name_with_namespace, name_with_namespace: new_project.name_with_namespace,
path_with_namespace: new_project.path_with_namespace, path_with_namespace: new_project.path_with_namespace,
web_url: new_project.web_url, web_url: new_project.web_url,
default_branch: new_project.default_branch, default_branch: new_project.default_branch,
ssh_url_to_repo: new_project.ssh_url_to_repo ssh_url_to_repo: new_project.ssh_url_to_repo
} })
HTTParty.post( ci_project = Ci::Project.find_by!(gitlab_id: project.id)
fork_registration_path,
body: { Ci::CreateProjectService.new.execute(
project_id: project.id, current_user,
project_token: token, params,
private_token: private_token, ci_project
data: params },
verify: false
) )
end end
def commit_coverage(sha, ref) def commit_coverage(sha, ref)
response = get_ci_build(sha, ref) get_ci_commit(sha, ref).coverage
rescue ActiveRecord::RecordNotFound
if response.code == 200 and response["coverage"] :error
response["coverage"]
end
rescue Errno::ECONNREFUSED
nil
end end
def build_page(sha, ref) def build_page(sha, ref)
URI::encode(project_url + "/refs/#{ref}/commits/#{sha}") if project.gitlab_ci_project.present?
end ci_project_ref_commits_url(project.gitlab_ci_project, ref, sha)
end
def builds_path
project_url + "?ref=" + project.default_branch
end
def status_img_path
project_url + "/status.png?ref=" + project.default_branch
end end
def title def title
...@@ -135,11 +111,7 @@ class GitlabCiService < CiService ...@@ -135,11 +111,7 @@ class GitlabCiService < CiService
end end
def fields def fields
[ []
{ type: 'text', name: 'token', placeholder: 'GitLab CI project specific token' },
{ type: 'text', name: 'project_url', placeholder: 'http://ci.gitlabhq.com/projects/3' },
{ type: 'checkbox', name: 'enable_ssl_verification', title: "Enable SSL verification" }
]
end end
private private
...@@ -148,10 +120,6 @@ class GitlabCiService < CiService ...@@ -148,10 +120,6 @@ class GitlabCiService < CiService
repository.blob_at(sha, '.gitlab-ci.yml') repository.blob_at(sha, '.gitlab-ci.yml')
end end
def fork_registration_path
project_url.sub(/projects\/\d*/, "#{API_PREFIX}/forks")
end
def repository def repository
project.repository project.repository
end end
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
# noteable_type :string(255) # noteable_type :string(255)
# recipient_id :integer # recipient_id :integer
# commit_id :string(255) # commit_id :string(255)
# line_code :string(255)
# reply_key :string(255) not null # reply_key :string(255) not null
# #
...@@ -21,13 +22,20 @@ class SentNotification < ActiveRecord::Base ...@@ -21,13 +22,20 @@ class SentNotification < ActiveRecord::Base
validates :noteable_id, presence: true, unless: :for_commit? validates :noteable_id, presence: true, unless: :for_commit?
validates :commit_id, presence: true, if: :for_commit? validates :commit_id, presence: true, if: :for_commit?
validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true
class << self class << self
def reply_key
return nil unless Gitlab::IncomingEmail.enabled?
SecureRandom.hex(16)
end
def for(reply_key) def for(reply_key)
find_by(reply_key: reply_key) find_by(reply_key: reply_key)
end end
def record(noteable, recipient_id, reply_key) def record(noteable, recipient_id, reply_key, params = {})
return unless reply_key return unless reply_key
noteable_id = nil noteable_id = nil
...@@ -38,7 +46,7 @@ class SentNotification < ActiveRecord::Base ...@@ -38,7 +46,7 @@ class SentNotification < ActiveRecord::Base
noteable_id = noteable.id noteable_id = noteable.id
end end
create( params.reverse_merge!(
project: noteable.project, project: noteable.project,
noteable_type: noteable.class.name, noteable_type: noteable.class.name,
noteable_id: noteable_id, noteable_id: noteable_id,
...@@ -46,6 +54,14 @@ class SentNotification < ActiveRecord::Base ...@@ -46,6 +54,14 @@ class SentNotification < ActiveRecord::Base
recipient_id: recipient_id, recipient_id: recipient_id,
reply_key: reply_key reply_key: reply_key
) )
create(params)
end
def record_note(note, recipient_id, reply_key, params = {})
params[:line_code] = note.line_code
record(note.noteable, recipient_id, reply_key, params)
end end
end end
......
...@@ -172,7 +172,7 @@ class User < ActiveRecord::Base ...@@ -172,7 +172,7 @@ class User < ActiveRecord::Base
# User's Dashboard preference # User's Dashboard preference
# Note: When adding an option, it MUST go on the end of the array. # Note: When adding an option, it MUST go on the end of the array.
enum dashboard: [:projects, :stars] enum dashboard: [:projects, :stars, :project_activity, :starred_project_activity]
# User's Project preference # User's Project preference
# Note: When adding an option, it MUST go on the end of the array. # Note: When adding an option, it MUST go on the end of the array.
......
...@@ -2,20 +2,15 @@ module Ci ...@@ -2,20 +2,15 @@ module Ci
class CreateProjectService class CreateProjectService
include Gitlab::Application.routes.url_helpers include Gitlab::Application.routes.url_helpers
def execute(current_user, params, project_route, forked_project = nil) def execute(current_user, params, forked_project = nil)
@project = Ci::Project.parse(params) @project = Ci::Project.parse(params)
Ci::Project.transaction do Ci::Project.transaction do
@project.save! @project.save!
data = {
token: @project.token,
project_url: project_route.gsub(":project_id", @project.id.to_s),
}
gl_project = ::Project.find(@project.gitlab_id) gl_project = ::Project.find(@project.gitlab_id)
gl_project.build_missing_services gl_project.build_missing_services
gl_project.gitlab_ci_service.update_attributes(data.merge(active: true)) gl_project.gitlab_ci_service.update_attributes(active: true)
end end
if forked_project if forked_project
......
...@@ -8,10 +8,10 @@ module Ci ...@@ -8,10 +8,10 @@ module Ci
builds = builds =
if current_runner.shared? if current_runner.shared?
# don't run projects which have not enables shared runners # don't run projects which have not enables shared runners
builds.includes(:project).where(ci_projects: { shared_runners_enabled: true }) builds.joins(commit: { gl_project: :gitlab_ci_project }).where(ci_projects: { shared_runners_enabled: true })
else else
# do run projects which are only assigned to this runner # do run projects which are only assigned to this runner
builds.where(project_id: current_runner.projects) builds.joins(:commit).where(ci_commits: { gl_project_id: current_runner.gl_projects_ids })
end end
builds = builds.order('created_at ASC') builds = builds.order('created_at ASC')
...@@ -19,7 +19,7 @@ module Ci ...@@ -19,7 +19,7 @@ module Ci
build = builds.find do |build| build = builds.find do |build|
(build.tag_list - current_runner.tag_list).empty? (build.tag_list - current_runner.tag_list).empty?
end end
if build if build
# In case when 2 runners try to assign the same build, second runner will be declined # In case when 2 runners try to assign the same build, second runner will be declined
......
...@@ -4,7 +4,10 @@ require 'securerandom' ...@@ -4,7 +4,10 @@ require 'securerandom'
# and return Gitlab::CompareResult object that responds to commits and diffs # and return Gitlab::CompareResult object that responds to commits and diffs
class CompareService class CompareService
def execute(source_project, source_branch, target_project, target_branch) def execute(source_project, source_branch, target_project, target_branch)
source_sha = source_project.commit(source_branch).sha source_commit = source_project.commit(source_branch)
return unless source_commit
source_sha = source_commit.sha
# If compare with other project we need to fetch ref first # If compare with other project we need to fetch ref first
unless target_project == source_project unless target_project == source_project
......
...@@ -55,6 +55,12 @@ class GitPushService ...@@ -55,6 +55,12 @@ class GitPushService
@push_data = build_push_data(oldrev, newrev, ref) @push_data = build_push_data(oldrev, newrev, ref)
# If CI was disabled but .gitlab-ci.yml file was pushed
# we enable CI automatically
if !project.gitlab_ci? && gitlab_ci_yaml?(newrev)
project.enable_ci(user)
end
EventCreateService.new.push(project, user, @push_data) EventCreateService.new.push(project, user, @push_data)
project.execute_hooks(@push_data.dup, :push_hooks) project.execute_hooks(@push_data.dup, :push_hooks)
project.execute_services(@push_data.dup, :push_hooks) project.execute_services(@push_data.dup, :push_hooks)
...@@ -143,4 +149,10 @@ class GitPushService ...@@ -143,4 +149,10 @@ class GitPushService
def commit_user(commit) def commit_user(commit)
commit.author || user commit.author || user
end end
def gitlab_ci_yaml?(sha)
@project.repository.blob_at(sha, '.gitlab-ci.yml')
rescue Rugged::ReferenceError
nil
end
end end
...@@ -4,10 +4,15 @@ module Milestones ...@@ -4,10 +4,15 @@ module Milestones
Milestone.transaction do Milestone.transaction do
update_params = { milestone: nil } update_params = { milestone: nil }
milestone.issues.each do |issue| milestone.issues.each do |issue|
Issues::UpdateService.new(project, current_user, update_params).execute(issue) Issues::UpdateService.new(project, current_user, update_params).execute(issue)
end end
milestone.merge_requests.each do |merge_request|
MergeRequests::UpdateService.new(project, current_user, update_params).execute(merge_request)
end
event_service.destroy_milestone(milestone, current_user) event_service.destroy_milestone(milestone, current_user)
Event.for_milestone_id(milestone.id).each do |event| Event.for_milestone_id(milestone.id).each do |event|
......
...@@ -18,7 +18,7 @@ module Projects ...@@ -18,7 +18,7 @@ module Projects
if new_project.persisted? if new_project.persisted?
if @project.gitlab_ci? if @project.gitlab_ci?
ForkRegistrationWorker.perform_async(@project.id, new_project.id, @current_user.private_token) @project.gitlab_ci_service.fork_registration(new_project, @current_user)
end end
end end
......
...@@ -131,7 +131,7 @@ ...@@ -131,7 +131,7 @@
.checkbox .checkbox
= f.label :ci_enabled do = f.label :ci_enabled do
= f.check_box :ci_enabled = f.check_box :ci_enabled
Enable Continuous Integration Disable to prevent CI usage until rake ci:migrate is run (8.0 only)
.form-actions .form-actions
= f.submit 'Save', class: 'btn btn-primary' = f.submit 'Save', class: 'btn btn-primary'
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment