summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG14
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--VERSION2
-rw-r--r--app/assets/javascripts/ci/pager.js.coffee42
-rw-r--r--app/assets/stylesheets/base/mixins.scss72
-rw-r--r--app/assets/stylesheets/generic/common.scss2
-rw-r--r--app/assets/stylesheets/generic/pagination.scss2
-rw-r--r--app/assets/stylesheets/generic/typography.scss8
-rw-r--r--app/controllers/ci/projects_controller.rb45
-rw-r--r--app/controllers/help_controller.rb5
-rw-r--r--app/controllers/projects/merge_requests_controller.rb3
-rw-r--r--app/helpers/ci_status_helper.rb34
-rw-r--r--app/helpers/commits_helper.rb2
-rw-r--r--app/helpers/gitlab_markdown_helper.rb2
-rw-r--r--app/models/ci/project.rb1
-rw-r--r--app/models/project.rb14
-rw-r--r--app/models/project_services/gitlab_ci_service.rb2
-rw-r--r--app/services/git_push_service.rb12
-rw-r--r--app/views/ci/projects/_project.html.haml61
-rw-r--r--app/views/ci/projects/_search.html.haml7
-rw-r--r--app/views/ci/projects/index.html.haml39
-rw-r--r--app/views/help/index.html.haml6
-rw-r--r--app/views/kaminari/gitlab/_first_page.html.haml2
-rw-r--r--app/views/kaminari/gitlab/_last_page.html.haml2
-rw-r--r--app/views/kaminari/gitlab/_paginator.html.haml9
-rw-r--r--app/views/projects/commits/_commit.html.haml10
-rw-r--r--app/views/projects/graphs/commits.html.haml76
-rw-r--r--app/views/projects/merge_requests/widget/_merged.html.haml2
-rw-r--r--doc/README.md2
-rw-r--r--doc/markdown/markdown.md1
-rw-r--r--lib/api/users.rb11
-rw-r--r--lib/ci/project_list_builder.rb21
-rw-r--r--lib/gitlab/ldap/auth_hash.rb3
-rw-r--r--spec/controllers/ci/projects_controller_spec.rb50
-rw-r--r--spec/helpers/ci_status_helper_spec.rb18
-rw-r--r--spec/helpers/gitlab_markdown_helper_spec.rb20
-rw-r--r--spec/lib/gitlab/ldap/auth_hash_spec.rb12
-rw-r--r--spec/models/project_spec.rb20
-rw-r--r--spec/requests/api/users_spec.rb14
40 files changed, 347 insertions, 307 deletions
diff --git a/CHANGELOG b/CHANGELOG
index b0540151fce..3710e8f55bd 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,19 @@
Please view this file on the master branch, on stable branches it's out of date.
+v 8.1.0 (unreleased)
+ - Show CI status on all pages where commits list is rendered
+ - Automatically enable CI when push .gitlab-ci.yml file to repository
+ - Fix cases where Markdown did not render links in activity feed (Stan Hu)
+ - Add first and last to pagination (Zeger-Jan van de Weg)
+
+v 8.0.2 (unreleased)
+ - 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
+
v 8.0.1
- Remove git refs used internally by GitLab from network graph (Stan Hu)
- Improve CI migration procedure and documentation
diff --git a/Gemfile b/Gemfile
index 924ee382f4d..b29846f738a 100644
--- a/Gemfile
+++ b/Gemfile
@@ -77,7 +77,7 @@ gem "stamp", '~> 0.5.0'
gem 'enumerize', '~> 0.7.0'
# Pagination
-gem "kaminari", "~> 0.15.1"
+gem "kaminari", "~> 0.16.3"
# HAML
gem "haml-rails", '~> 0.5.3'
diff --git a/Gemfile.lock b/Gemfile.lock
index 320f7629fb6..6070beb9d07 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -367,7 +367,7 @@ GEM
railties (>= 3.2.16)
json (1.8.3)
jwt (1.5.1)
- kaminari (0.15.1)
+ kaminari (0.16.3)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
kgio (2.9.3)
@@ -846,7 +846,7 @@ DEPENDENCIES
jquery-scrollto-rails (~> 1.4.3)
jquery-turbolinks (~> 2.0.1)
jquery-ui-rails (~> 4.2.1)
- kaminari (~> 0.15.1)
+ kaminari (~> 0.16.3)
letter_opener (~> 1.1.2)
mail_room (~> 0.5.1)
minitest (~> 5.7.0)
diff --git a/VERSION b/VERSION
index 939cbc3ea74..a2264f05f50 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-8.0.0.pre
+8.1.0.pre
diff --git a/app/assets/javascripts/ci/pager.js.coffee b/app/assets/javascripts/ci/pager.js.coffee
deleted file mode 100644
index 226fbd654ab..00000000000
--- a/app/assets/javascripts/ci/pager.js.coffee
+++ /dev/null
@@ -1,42 +0,0 @@
-@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()
diff --git a/app/assets/stylesheets/base/mixins.scss b/app/assets/stylesheets/base/mixins.scss
index a2f6c3e21f4..421588f64d8 100644
--- a/app/assets/stylesheets/base/mixins.scss
+++ b/app/assets/stylesheets/base/mixins.scss
@@ -93,46 +93,87 @@
}
h1 {
- margin-top: 45px;
- font-size: 2.5em;
+ font-size: 1.3em;
+ font-weight: 600;
+ margin: 24px 0 12px 0;
+ padding: 0 0 10px 0;
+ border-bottom: 1px solid #e7e9ed;
+ color: #313236;
}
h2 {
- margin-top: 40px;
- font-size: 2em;
+ font-size: 1.2em;
+ font-weight: 600;
+ margin: 24px 0 12px 0;
+ color: #313236;
}
h3 {
- margin-top: 35px;
- font-size: 1.5em;
+ margin: 24px 0 12px 0;
+ font-size: 1.25em;
}
h4 {
- margin-top: 30px;
- font-size: 1.2em;
+ margin: 24px 0 12px 0;
+ font-size: 1.1em;
+ }
+
+ h5 {
+ margin: 24px 0 12px 0;
+ font-size: 1em;
+ }
+
+ h6 {
+ margin: 24px 0 12px 0;
+ font-size: 0.90em;
}
blockquote {
- color: #888;
+ padding: 8px 21px;
+ margin: 12px 0 12px;
+ border-left: 3px solid #e7e9ed;
+ }
+
+ blockquote p {
+ color: #7f8fa4 !important;
font-size: 15px;
line-height: 1.5;
}
-
+
+ p {
+ color:#5c5d5e;
+ margin:6px 0 0 0;
+ }
+
table {
@extend .table;
@extend .table-bordered;
+ margin: 12px 0 12px 0;
+ color: #5c5d5e;
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;
}
p > code {
- font-size: inherit;
font-weight: inherit;
}
+
+ ul {
+ color: #5c5d5e;
+ }
+
li {
- line-height: 1.5;
+ line-height: 1.6em;
}
a[href*="/uploads/"], a[href*="storage.googleapis.com/google-code-attachments/"] {
@@ -152,6 +193,7 @@
}
}
+
@mixin str-truncated($max_width: 82%) {
display: inline-block;
overflow: hidden;
@@ -183,7 +225,7 @@
&.active {
background: #f9f9f9;
a {
- font-weight: bold;
+ font-weight: 600;
}
}
diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss
index 48fad7701ef..b659717b4e1 100644
--- a/app/assets/stylesheets/generic/common.scss
+++ b/app/assets/stylesheets/generic/common.scss
@@ -302,7 +302,7 @@ table {
}
.btn-sign-in {
- margin-top: 15px;
+ margin-top: 8px;
text-shadow: none;
}
diff --git a/app/assets/stylesheets/generic/pagination.scss b/app/assets/stylesheets/generic/pagination.scss
index a937677ebdc..6677f94dafd 100644
--- a/app/assets/stylesheets/generic/pagination.scss
+++ b/app/assets/stylesheets/generic/pagination.scss
@@ -9,6 +9,8 @@
margin: 0;
display: block;
+ li.first,
+ li.last,
li.next,
li.prev {
> a {
diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss
index d5f0d86a307..7a8a17ced99 100644
--- a/app/assets/stylesheets/generic/typography.scss
+++ b/app/assets/stylesheets/generic/typography.scss
@@ -55,6 +55,7 @@ a > code {
@include md-typography;
word-wrap: break-word;
+ padding: 7px;
/* Link to current header. */
h1, h2, h3, h4, h5, h6 {
@@ -83,9 +84,12 @@ a > code {
}
}
- ul {
+ ul,ol {
padding: 0;
- margin: 0 0 9px 25px !important;
+ margin: 6px 0 6px 18px !important;
+ }
+ ol {
+ color: #5c5d5e;
}
}
diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb
index 40b61edb0a9..250a2e79313 100644
--- a/app/controllers/ci/projects_controller.rb
+++ b/app/controllers/ci/projects_controller.rb
@@ -1,12 +1,10 @@
module Ci
class ProjectsController < Ci::ApplicationController
- PROJECTS_BATCH = 100
-
before_action :authenticate_user!, except: [:build, :badge, :index, :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 :authorize_access_project!, except: [:build, :badge, :index, :show, :new, :create, :disabled]
- before_action :authorize_manage_project!, only: [:edit, :integration, :update, :destroy, :toggle_shared_runners, :dumped_yaml]
+ before_action :project, only: [:build, :show, :badge, :edit, :update, :destroy, :toggle_shared_runners, :dumped_yaml]
+ before_action :authorize_access_project!, except: [:build, :badge, :index, :show, :new, :disabled]
+ before_action :authorize_manage_project!, only: [:edit, :update, :destroy, :toggle_shared_runners, :dumped_yaml]
before_action :authenticate_token!, only: [:build]
before_action :no_cache, only: [:badge]
skip_before_action :check_enable_flag!, only: [:disabled]
@@ -18,23 +16,15 @@ module Ci
end
def index
- @limit, @offset = (params[:limit] || PROJECTS_BATCH).to_i, (params[:offset] || 0).to_i
- @page = @offset == 0 ? 1 : (@offset / @limit + 1)
+ @projects = Ci::Project.all
if current_user
- @projects = ProjectListBuilder.new.execute(current_user, params[:search])
-
- @projects = @projects.page(@page).per(@limit)
-
- @total_count = @projects.size
+ @projects = @projects.where(gitlab_id: current_user.authorized_projects.pluck(:id))
end
- respond_to do |format|
- format.json do
- pager_json("ci/projects/index", @total_count)
- end
- format.html
- end
+ @projects = @projects.search(params[:search]) if params[:search].present?
+ @projects = @projects.includes(:last_commit).order('ci_commits.created_at DESC')
+ @projects = @projects.page(params[:page]).per(40)
end
def show
@@ -45,25 +35,6 @@ module Ci
@commits = @commits.page(params[:page]).per(20)
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)
-
- 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
diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb
index ad00948da51..55050615473 100644
--- a/app/controllers/help_controller.rb
+++ b/app/controllers/help_controller.rb
@@ -4,6 +4,11 @@ class HelpController < ApplicationController
layout 'help'
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
def show
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index c1231994f25..7574842cd43 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -258,8 +258,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@commits = @merge_request.commits
@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?
@merge_request.unlock_mr
@merge_request.close
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
new file mode 100644
index 00000000000..18c30ddb281
--- /dev/null
+++ b/app/helpers/ci_status_helper.rb
@@ -0,0 +1,34 @@
+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)
+ icon_name =
+ case ci_commit.status
+ when 'success'
+ 'check'
+ when 'failed'
+ 'close'
+ when 'running', 'pending'
+ 'clock-o'
+ else
+ 'circle'
+ end
+
+ icon(icon_name)
+ 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
+end
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index d13d80be293..9df20c9fce5 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -135,7 +135,7 @@ module CommitsHelper
# size: size of the avatar image in px
def commit_person_link(commit, options = {})
user = commit.send(options[:source])
-
+
source_name = clean(commit.send "#{options[:source]}_name".to_sym)
source_email = clean(commit.send "#{options[:source]}_email".to_sym)
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index 78bf25f55e7..153a44870f6 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -165,7 +165,7 @@ module GitlabMarkdownHelper
# and return true. Otherwise return false.
def truncate_if_block(node, 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
else
truncated
diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb
index 37fbcc287bb..a52e28615f7 100644
--- a/app/models/ci/project.rb
+++ b/app/models/ci/project.rb
@@ -41,6 +41,7 @@ module Ci
has_many :events, dependent: :destroy, class_name: 'Ci::Event'
has_many :variables, dependent: :destroy, class_name: 'Ci::Variable'
has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger'
+ has_one :last_commit, -> { order 'ci_commits.created_at DESC' }, class_name: 'Ci::Commit'
# Project services
has_many :services, dependent: :destroy, class_name: 'Ci::Service'
diff --git a/app/models/project.rb b/app/models/project.rb
index 1a5c1c978c9..a7ea1236b86 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -735,4 +735,18 @@ class Project < ActiveRecord::Base
errors.add(:base, 'Failed create wiki')
false
end
+
+ def ci_commit(sha)
+ gitlab_ci_project.commits.find_by(sha: sha) if gitlab_ci?
+ 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
diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb
index 9e2b3bcd873..ec3bd44024f 100644
--- a/app/models/project_services/gitlab_ci_service.rb
+++ b/app/models/project_services/gitlab_ci_service.rb
@@ -72,7 +72,7 @@ class GitlabCiService < CiService
})
ci_project = Ci::Project.find_by!(gitlab_id: project.id)
-
+
Ci::CreateProjectService.new.execute(
current_user,
params,
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index 0a73244774a..8193b6e192d 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -55,6 +55,12 @@ class GitPushService
@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)
project.execute_hooks(@push_data.dup, :push_hooks)
project.execute_services(@push_data.dup, :push_hooks)
@@ -143,4 +149,10 @@ class GitPushService
def commit_user(commit)
commit.author || user
end
+
+ def gitlab_ci_yaml?(sha)
+ @project.repository.blob_at(sha, '.gitlab-ci.yml')
+ rescue Rugged::ReferenceError
+ nil
+ end
end
diff --git a/app/views/ci/projects/_project.html.haml b/app/views/ci/projects/_project.html.haml
index 844b6677b3d..58022de9bc1 100644
--- a/app/views/ci/projects/_project.html.haml
+++ b/app/views/ci/projects/_project.html.haml
@@ -1,37 +1,24 @@
-- if project.gitlab_ci_project
- - ci_project = project.gitlab_ci_project
- - last_commit = ci_project.last_commit
- %tr
- %td
- = link_to [:ci, ci_project] do
- = ci_project.name
- %td
- - if last_commit
- = ci_status_with_icon(last_commit.status)
- = commit_link(last_commit)
- &middot;
- - if ci_project.last_commit_date
- = time_ago_in_words ci_project.last_commit_date
- ago
- - else
- No builds yet
- %td
- - if ci_project.public
- %i.fa.fa-globe
- Public
- - else
- %i.fa.fa-lock
- Private
- %td
- = ci_project.commits.count
-- else
- %tr.light
- %td
- = project.name_with_namespace
- %td
- %small Not added to CI
- %td
- %td
- = form_tag ci_projects_path do
- = hidden_field_tag :project, project.to_json(methods: [:name_with_namespace, :path_with_namespace, :ssh_url_to_repo])
- = submit_tag 'Add project to CI', class: 'btn btn-default btn-sm'
+- last_commit = project.last_commit
+%tr
+ %td
+ = link_to [:ci, project] do
+ = project.name
+ %td
+ - if last_commit
+ = ci_status_with_icon(last_commit.status)
+ = commit_link(last_commit)
+ &middot;
+ - if project.last_commit_date
+ = time_ago_in_words project.last_commit_date
+ ago
+ - else
+ No builds yet
+ %td
+ - if project.public
+ %i.fa.fa-globe
+ Public
+ - else
+ %i.fa.fa-lock
+ Private
+ %td
+ = project.commits.count
diff --git a/app/views/ci/projects/_search.html.haml b/app/views/ci/projects/_search.html.haml
index 4ab43a403f7..a956ed4c0bc 100644
--- a/app/views/ci/projects/_search.html.haml
+++ b/app/views/ci/projects/_search.html.haml
@@ -1,11 +1,6 @@
.search
- = form_tag "#", method: :get, class: 'ci-search-form' do |f|
+ = form_tag ci_root_path, method: :get, class: 'ci-search-form' do |f|
.input-group
= search_field_tag "search", params[:search], placeholder: "Search", class: "search-input form-control"
.input-group-addon
%i.fa.fa-search
-
-:coffeescript
- $('.ci-search-form').submit ->
- CiPager.init "#{ci_projects_path}" + "?search=" + query, #{Ci::ProjectsController::PROJECTS_BATCH}, false
- false
diff --git a/app/views/ci/projects/index.html.haml b/app/views/ci/projects/index.html.haml
index 2b618d61f79..046095a3c12 100644
--- a/app/views/ci/projects/index.html.haml
+++ b/app/views/ci/projects/index.html.haml
@@ -1,30 +1,17 @@
- if current_user
- - if @offset > 0
- = render @projects
- - else
- .gray-content-block.top-block
- = render "search"
- .projects
- .gray-content-block.clearfix.light.second-block
- .pull-left.fetch-status
- - if params[:search].present?
- by keyword: "#{params[:search]}",
- #{@total_count} projects
-
- .wide-table-holder
- %table.table.projects-table.content-list
- %thead
- %tr
- %th Project Name
- %th Last commit
- %th Access
- %th Commits
-
- = render @projects
- %p.text-center.hide.loading
- %i.fa.fa-refresh.fa-spin
- :coffeescript
- CiPager.init "#{ci_projects_path}", #{Ci::ProjectsController::PROJECTS_BATCH}, false
+ .gray-content-block.top-block
+ = render "search"
+ .projects
+ .wide-table-holder
+ %table.table.projects-table.content-list
+ %thead
+ %tr
+ %th Project Name
+ %th Last commit
+ %th Access
+ %th Commits
+ = render @projects
+ = paginate @projects, theme: 'gitlab'
- else
= render 'public'
diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml
index f492aaf4c0a..ab7ed1b5d95 100644
--- a/app/views/help/index.html.haml
+++ b/app/views/help/index.html.haml
@@ -27,11 +27,7 @@
.col-md-8
.documentation-index
= preserve do
- - readme_text = File.read(Rails.root.join("doc", "README.md"))
- - text = readme_text.dup
- - readme_text.scan(/\]\(([^(]+)\)/) { |match| text.gsub!(match.first, "help/#{match.first}") }
- = markdown text
-
+ = markdown(@help_index)
.col-md-4
.panel.panel-default
.panel-heading
diff --git a/app/views/kaminari/gitlab/_first_page.html.haml b/app/views/kaminari/gitlab/_first_page.html.haml
index 41c9c0b3af6..ada7306d98d 100644
--- a/app/views/kaminari/gitlab/_first_page.html.haml
+++ b/app/views/kaminari/gitlab/_first_page.html.haml
@@ -5,5 +5,5 @@
-# num_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
-%span.first
+%li.first
= link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, remote: remote
diff --git a/app/views/kaminari/gitlab/_last_page.html.haml b/app/views/kaminari/gitlab/_last_page.html.haml
index b03a206224c..3431d029bcc 100644
--- a/app/views/kaminari/gitlab/_last_page.html.haml
+++ b/app/views/kaminari/gitlab/_last_page.html.haml
@@ -5,5 +5,5 @@
-# num_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
-%span.last
+%li.last
= link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {remote: remote}
diff --git a/app/views/kaminari/gitlab/_paginator.html.haml b/app/views/kaminari/gitlab/_paginator.html.haml
index b8d419b5894..2f645186921 100644
--- a/app/views/kaminari/gitlab/_paginator.html.haml
+++ b/app/views/kaminari/gitlab/_paginator.html.haml
@@ -8,10 +8,15 @@
= paginator.render do
%div.gl-pagination
%ul.pagination.clearfix
- = prev_page_tag unless current_page.first?
+ - unless current_page.first?
+ = first_page_tag unless num_pages < 5 # As kaminari will always show the first 5 pages
+ = prev_page_tag
- each_page do |page|
- if page.left_outer? || page.right_outer? || page.inside_window?
= page_tag page
- elsif !page.was_truncated?
= gap_tag
- = next_page_tag unless current_page.last?
+ - unless current_page.last?
+ = next_page_tag
+ = last_page_tag unless num_pages < 5
+
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 74f8d8b15cf..efad4cb1473 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -4,7 +4,11 @@
- notes = commit.notes
- note_count = notes.user.count
-= cache [project.id, commit.id, note_count] do
+- ci_commit = project.ci_commit(commit.sha)
+- cache_key = [project.id, commit.id, note_count]
+- cache_key.push(ci_commit.status) if ci_commit
+
+= cache(cache_key) do
%li.commit.js-toggle-container
.commit-row-title
%strong.str-truncated
@@ -13,6 +17,10 @@
%a.text-expander.js-toggle-button ...
.pull-right
+ - if ci_commit
+ = link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}" do
+ = ci_status_icon(ci_commit)
+ &nbsp;
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
.notes_count
diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml
index bf0cac539b8..112be875b6b 100644
--- a/app/views/projects/graphs/commits.html.haml
+++ b/app/views/projects/graphs/commits.html.haml
@@ -32,61 +32,55 @@
%div
%p.slead
Commits per day of month
- %canvas#month-chart{width: 800, height: 400}
+ %canvas#month-chart
.row
.col-md-6
%div
%p.slead
Commits per day hour (UTC)
- %canvas#hour-chart{width: 800, height: 400}
+ %canvas#hour-chart
.col-md-6
%div
%p.slead
Commits per weekday
- %canvas#weekday-chart{width: 800, height: 400}
+ %canvas#weekday-chart
:coffeescript
- data = {
- labels : #{@commits_per_time.keys.to_json},
- datasets : [{
- fillColor : "rgba(220,220,220,0.5)",
- strokeColor : "rgba(220,220,220,1)",
- barStrokeWidth: 1,
- barValueSpacing: 1,
- barDatasetSpacing: 1,
- data : #{@commits_per_time.values.to_json}
- }]
- }
+ responsiveChart = (selector, data) ->
+ options = { "scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2, maintainAspectRatio: false }
- ctx = $("#hour-chart").get(0).getContext("2d");
- new Chart(ctx).Bar(data,{"scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2})
+ # get selector by context
+ ctx = selector.get(0).getContext("2d")
+ # pointing parent container to make chart.js inherit its width
+ container = $(selector).parent()
- data = {
- labels : #{@commits_per_week_days.keys.to_json},
- datasets : [{
- fillColor : "rgba(220,220,220,0.5)",
- strokeColor : "rgba(220,220,220,1)",
- barStrokeWidth: 1,
- barValueSpacing: 1,
- barDatasetSpacing: 1,
- data : #{@commits_per_week_days.values.to_json}
- }]
- }
+ generateChart = ->
+ selector.attr('width', $(container).width())
+ new Chart(ctx).Bar(data, options)
+
+ # enabling auto-resizing
+ $(window).resize( generateChart )
- ctx = $("#weekday-chart").get(0).getContext("2d");
- new Chart(ctx).Bar(data,{"scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2})
+ generateChart()
- data = {
- labels : #{@commits_per_month.keys.to_json},
- datasets : [{
- fillColor : "rgba(220,220,220,0.5)",
- strokeColor : "rgba(220,220,220,1)",
- barStrokeWidth: 1,
- barValueSpacing: 1,
- barDatasetSpacing: 1,
- data : #{@commits_per_month.values.to_json}
- }]
+ chartData = (keys, values) ->
+ data = {
+ labels : keys,
+ datasets : [{
+ fillColor : "rgba(220,220,220,0.5)",
+ strokeColor : "rgba(220,220,220,1)",
+ barStrokeWidth: 1,
+ barValueSpacing: 1,
+ barDatasetSpacing: 1,
+ data : values
+ }]
}
- ctx = $("#month-chart").get(0).getContext("2d");
- new Chart(ctx).Bar(data, {"scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2})
+ hourData = chartData(#{@commits_per_time.keys.to_json}, #{@commits_per_time.values.to_json})
+ responsiveChart($('#hour-chart'), hourData)
+
+ dayData = chartData(#{@commits_per_week_days.keys.to_json}, #{@commits_per_week_days.values.to_json})
+ responsiveChart($('#weekday-chart'), dayData)
+
+ monthData = chartData(#{@commits_per_month.keys.to_json}, #{@commits_per_month.values.to_json})
+ responsiveChart($('#month-chart'), monthData)
diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml
index d22dfa085b8..f223f687def 100644
--- a/app/views/projects/merge_requests/widget/_merged.html.haml
+++ b/app/views/projects/merge_requests/widget/_merged.html.haml
@@ -20,7 +20,7 @@
The changes were merged into
%span.label-branch= @merge_request.target_branch
You can remove the source branch now.
- = link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @source_branch), remote: true, method: :delete, class: "btn btn-primary btn-sm remove_source_branch" do
+ = link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request.source_branch), remote: true, method: :delete, class: "btn btn-primary btn-sm remove_source_branch" do
%i.fa.fa-times
Remove Source Branch
diff --git a/doc/README.md b/doc/README.md
index 5f38086b8e3..a0ff856ebb6 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -51,7 +51,7 @@
### Administrator documentation
-+ [User permissions](permissions/README.md)
++ [User permissions](permissions/permissions.md)
+ [API](api/README.md)
## Contributor documentation
diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md
index 6fdb2fe1491..d83838127c9 100644
--- a/doc/markdown/markdown.md
+++ b/doc/markdown/markdown.md
@@ -33,7 +33,6 @@ For GitLab we developed something we call "GitLab Flavored Markdown" (GFM). It e
You can use GFM in
-- commit messages
- comments
- issues
- merge requests
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 813cc379e43..a98d668e02d 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -121,6 +121,17 @@ module API
User.where(username: attrs[:username]).
where.not(id: user.id).count > 0
+ identity_attrs = attributes_for_keys [:provider, :extern_uid]
+ if identity_attrs.any?
+ identity = user.identities.find_by(provider: identity_attrs[:provider])
+ if identity
+ identity.update_attributes(identity_attrs)
+ else
+ identity = user.identities.build(identity_attrs)
+ identity.save
+ end
+ end
+
if user.update_attributes(attrs)
present user, with: Entities::UserFull
else
diff --git a/lib/ci/project_list_builder.rb b/lib/ci/project_list_builder.rb
deleted file mode 100644
index da26f9a9f47..00000000000
--- a/lib/ci/project_list_builder.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-module Ci
- class ProjectListBuilder
- def execute(current_user, search = nil)
- projects = current_user.authorized_projects
- projects = projects.search(search) if search
-
- projects.
- joins("LEFT JOIN ci_projects ON projects.id = ci_projects.gitlab_id
- LEFT JOIN #{last_commit_subquery} AS last_commit ON #{Ci::Project.table_name}.id = last_commit.project_id").
- reorder("ci_projects.id is NULL ASC,
- CASE WHEN last_commit.committed_at IS NULL THEN 1 ELSE 0 END,
- last_commit.committed_at DESC")
- end
-
- private
-
- def last_commit_subquery
- "(SELECT project_id, MAX(committed_at) committed_at FROM #{Ci::Commit.table_name} GROUP BY project_id)"
- end
- end
-end
diff --git a/lib/gitlab/ldap/auth_hash.rb b/lib/gitlab/ldap/auth_hash.rb
index 55deeeacd90..bf4dd9542d5 100644
--- a/lib/gitlab/ldap/auth_hash.rb
+++ b/lib/gitlab/ldap/auth_hash.rb
@@ -6,7 +6,7 @@ module Gitlab
private
def get_info(key)
- attributes = ldap_config.attributes[key]
+ attributes = ldap_config.attributes[key.to_s]
return super unless attributes
attributes = Array(attributes)
@@ -14,6 +14,7 @@ module Gitlab
value = nil
attributes.each do |attribute|
value = get_raw(attribute)
+ value = value.first if value
break if value.present?
end
diff --git a/spec/controllers/ci/projects_controller_spec.rb b/spec/controllers/ci/projects_controller_spec.rb
deleted file mode 100644
index 3e579f9a7d6..00000000000
--- a/spec/controllers/ci/projects_controller_spec.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-require "spec_helper"
-
-describe Ci::ProjectsController do
- before do
- @project = FactoryGirl.create :ci_project
- end
-
- describe "POST /projects" do
- let(:project_dump) { OpenStruct.new({ id: @project.gitlab_id }) }
-
- let(:user) do
- create(:user)
- end
-
- before do
- sign_in(user)
- end
-
- it "creates project" do
- post :create, { project: JSON.dump(project_dump.to_h) }.with_indifferent_access
-
- expect(response.code).to eq('302')
- expect(assigns(:project)).not_to be_a_new(Ci::Project)
- end
-
- it "shows error" do
- post :create, { project: JSON.dump(project_dump.to_h) }.with_indifferent_access
-
- expect(response.code).to eq('302')
- expect(flash[:alert]).to include("You have to have at least master role to enable CI for this project")
- end
- end
-
- describe "GET /gitlab" do
- let(:user) do
- create(:user)
- end
-
- before do
- sign_in(user)
- end
-
- it "searches projects" do
- xhr :get, :index, { search: "str", format: "json" }.with_indifferent_access
-
- expect(response).to be_success
- expect(response.code).to eq('200')
- end
- end
-end
diff --git a/spec/helpers/ci_status_helper_spec.rb b/spec/helpers/ci_status_helper_spec.rb
new file mode 100644
index 00000000000..7fc53eb1472
--- /dev/null
+++ b/spec/helpers/ci_status_helper_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+describe CiStatusHelper do
+ include IconsHelper
+
+ let(:success_commit) { double("Ci::Commit", status: 'success') }
+ let(:failed_commit) { double("Ci::Commit", status: 'failed') }
+
+ describe 'ci_status_color' do
+ it { expect(ci_status_icon(success_commit)).to include('fa-check') }
+ it { expect(ci_status_icon(failed_commit)).to include('fa-close') }
+ end
+
+ describe 'ci_status_color' do
+ it { expect(ci_status_color(success_commit)).to eq('green') }
+ it { expect(ci_status_color(failed_commit)).to eq('red') }
+ end
+end
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index b8101ae77ec..be0e0c747b7 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -146,4 +146,24 @@ describe GitlabMarkdownHelper do
expect(random_markdown_tip).to eq 'Random tip'
end
end
+
+ describe '#first_line_in_markdown' do
+ let(:text) { "@#{user.username}, can you look at this?\nHello world\n"}
+
+ it 'truncates Markdown properly' do
+ actual = first_line_in_markdown(text, 100, project: project)
+
+ doc = Nokogiri::HTML.parse(actual)
+
+ # Make sure we didn't create invalid markup
+ expect(doc.errors).to be_empty
+
+ # Leading user link
+ expect(doc.css('a').length).to eq(1)
+ expect(doc.css('a')[0].attr('href')).to eq user_path(user)
+ expect(doc.css('a')[0].text).to eq "@#{user.username}"
+
+ expect(doc.content).to eq "@#{user.username}, can you look at this?..."
+ end
+ end
end
diff --git a/spec/lib/gitlab/ldap/auth_hash_spec.rb b/spec/lib/gitlab/ldap/auth_hash_spec.rb
index 18c7924fea1..7d8268536a4 100644
--- a/spec/lib/gitlab/ldap/auth_hash_spec.rb
+++ b/spec/lib/gitlab/ldap/auth_hash_spec.rb
@@ -24,10 +24,10 @@ describe Gitlab::LDAP::AuthHash do
let(:raw_info) do
{
- uid: '123456',
- email: 'johnsmith@example.com',
- cn: 'Smith, J.',
- fullName: 'John Smith'
+ uid: ['123456'],
+ email: ['johnsmith@example.com'],
+ cn: ['Smith, J.'],
+ fullName: ['John Smith']
}
end
@@ -45,8 +45,8 @@ describe Gitlab::LDAP::AuthHash do
context "with overridden attributes" do
let(:attributes) do
{
- username: ['mail', 'email'],
- name: 'fullName'
+ 'username' => ['mail', 'email'],
+ 'name' => 'fullName'
}
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 2fcbd5ae108..9e7b6f5cb30 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -401,4 +401,24 @@ describe Project do
it { should eq "http://localhost#{avatar_path}" }
end
end
+
+ describe :ci_commit do
+ let(:project) { create :project }
+ let(:ci_project) { create :ci_project, gl_project: project }
+ let(:commit) { create :ci_commit, project: ci_project }
+
+ before { project.create_gitlab_ci_service(active: true) }
+
+ it { expect(project.ci_commit(commit.sha)).to eq(commit) }
+ end
+
+ describe :enable_ci do
+ let(:project) { create :project }
+ let(:user) { create :user }
+
+ before { project.enable_ci(user) }
+
+ it { expect(project.gitlab_ci?).to be_truthy }
+ it { expect(project.gitlab_ci_project).to be_a(Ci::Project) }
+ end
end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index f9bc63680ba..d26a300ed82 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -7,6 +7,7 @@ describe API::API, api: true do
let(:admin) { create(:admin) }
let(:key) { create(:key, user: user) }
let(:email) { create(:email, user: user) }
+ let(:omniauth_user) { create(:omniauth_user) }
describe "GET /users" do
context "when unauthenticated" do
@@ -230,6 +231,19 @@ describe API::API, api: true do
expect(user.reload.username).to eq(user.username)
end
+ it "should update user's existing identity" do
+ put api("/users/#{omniauth_user.id}", admin), provider: 'ldapmain', extern_uid: '654321'
+ expect(response.status).to eq(200)
+ expect(omniauth_user.reload.identities.first.extern_uid).to eq('654321')
+ end
+
+ it 'should update user with new identity' do
+ put api("/users/#{user.id}", admin), provider: 'github', extern_uid: '67890'
+ expect(response.status).to eq(200)
+ expect(user.reload.identities.first.extern_uid).to eq('67890')
+ expect(user.reload.identities.first.provider).to eq('github')
+ end
+
it "should update admin status" do
put api("/users/#{user.id}", admin), { admin: true }
expect(response.status).to eq(200)