summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/application.js.coffee4
-rw-r--r--app/assets/javascripts/ci/build.coffee91
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss4
-rw-r--r--app/assets/stylesheets/framework/variables.scss3
-rw-r--r--app/assets/stylesheets/pages/builds.scss91
-rw-r--r--app/assets/stylesheets/pages/issuable.scss6
-rw-r--r--app/assets/stylesheets/pages/xterm.scss19
-rw-r--r--app/controllers/application_controller.rb1
-rw-r--r--app/controllers/projects/avatars_controller.rb5
-rw-r--r--app/controllers/projects/builds_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests_controller.rb7
-rw-r--r--app/controllers/projects/raw_controller.rb5
-rw-r--r--app/controllers/projects/repositories_controller.rb3
-rw-r--r--app/helpers/nav_helper.rb2
-rw-r--r--app/helpers/workhorse_helper.rb24
-rw-r--r--app/models/merge_request.rb8
-rw-r--r--app/models/project.rb71
-rw-r--r--app/views/layouts/nav/_project.html.haml18
-rw-r--r--app/views/projects/artifacts/browse.html.haml1
-rw-r--r--app/views/projects/builds/_header.html.haml16
-rw-r--r--app/views/projects/builds/_sidebar.html.haml93
-rw-r--r--app/views/projects/builds/_user.html.haml4
-rw-r--r--app/views/projects/builds/show.html.haml189
-rw-r--r--app/views/projects/issues/_head.html.haml25
-rw-r--r--app/views/projects/issues/index.html.haml33
-rw-r--r--app/views/projects/labels/index.html.haml63
-rw-r--r--app/views/projects/merge_requests/_head.html.haml5
-rw-r--r--app/views/projects/merge_requests/index.html.haml26
-rw-r--r--app/views/projects/milestones/index.html.haml29
29 files changed, 494 insertions, 354 deletions
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index b28327ce12d..e0ca546350b 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -267,8 +267,8 @@ $ ->
$(document).trigger('breakpoint:change', [bootstrapBreakpoint])
$(window)
- .off "resize"
- .on "resize", (e) ->
+ .off "resize.app"
+ .on "resize.app", (e) ->
fitSidebarForSize()
gl.awardsHandler = new AwardsHandler()
diff --git a/app/assets/javascripts/ci/build.coffee b/app/assets/javascripts/ci/build.coffee
index 98d05e41273..f763ba96e33 100644
--- a/app/assets/javascripts/ci/build.coffee
+++ b/app/assets/javascripts/ci/build.coffee
@@ -1,19 +1,31 @@
-class CiBuild
+class @CiBuild
@interval: null
@state: null
- constructor: (build_url, build_status, build_state) ->
+ constructor: (@build_url, @build_status, @state) ->
clearInterval(CiBuild.interval)
- @state = build_state
+ # Init breakpoint checker
+ @bp = Breakpoints.get()
+ @hideSidebar()
+ $('.js-build-sidebar').niceScroll()
+ $(document)
+ .off 'click', '.js-sidebar-build-toggle'
+ .on 'click', '.js-sidebar-build-toggle', @toggleSidebar
- @initScrollButtonAffix()
+ $(window)
+ .off 'resize.build'
+ .on 'resize.build', @hideSidebar
- if build_status == "running" || build_status == "pending"
+ if $('#build-trace').length
+ @getInitialBuildTrace()
+ @initScrollButtonAffix()
+
+ if @build_status is "running" or @build_status is "pending"
#
# Bind autoscroll button to follow build output
#
- $("#autoscroll-button").bind "click", ->
+ $('#autoscroll-button').on 'click', ->
state = $(this).data("state")
if "enabled" is state
$(this).data "state", "disabled"
@@ -27,26 +39,37 @@ class CiBuild
# Only valid for runnig build when output changes during time
#
CiBuild.interval = setInterval =>
- if window.location.href.split("#").first() is build_url
- last_state = @state
- $.ajax
- url: build_url + "/trace.json?state=" + encodeURIComponent(@state)
- dataType: "json"
- success: (log) =>
- return unless last_state is @state
-
- if log.state and log.status is "running"
- @state = log.state
- if log.append
- $('.fa-refresh').before log.html
- else
- $('#build-trace code').html log.html
- $('#build-trace code').append '<i class="fa fa-refresh fa-spin"/>'
- @checkAutoscroll()
- else if log.status isnt build_status
- Turbolinks.visit build_url
+ if window.location.href.split("#").first() is @build_url
+ @getBuildTrace()
, 4000
+ getInitialBuildTrace: ->
+ $.ajax
+ url: @build_url
+ dataType: 'json'
+ success: (build_data) ->
+ $('.js-build-output').html build_data.trace_html
+
+ if build_data.status is 'success' or build_data.status is 'failed'
+ $('.js-build-refresh').remove()
+
+ getBuildTrace: ->
+ $.ajax
+ url: "#{@build_url}/trace.json?state=#{encodeURIComponent(@state)}"
+ dataType: "json"
+ success: (log) =>
+ if log.state
+ @state = log.state
+
+ if log.status is "running"
+ if log.append
+ $('.js-build-output').append log.html
+ else
+ $('.js-build-output').html log.html
+ @checkAutoscroll()
+ else if log.status isnt @build_status
+ Turbolinks.visit @build_url
+
checkAutoscroll: ->
$("html,body").scrollTop $("#build-trace").height() if "enabled" is $("#autoscroll-button").data("state")
@@ -61,4 +84,22 @@ class CiBuild
$body.outerHeight() - ($buildTrace.outerHeight() + $buildTrace.offset().top)
)
-@CiBuild = CiBuild
+ shouldHideSidebar: ->
+ bootstrapBreakpoint = @bp.getBreakpointSize()
+
+ bootstrapBreakpoint is 'xs' or bootstrapBreakpoint is 'sm'
+
+ toggleSidebar: =>
+ if @shouldHideSidebar()
+ $('.js-build-sidebar')
+ .toggleClass 'right-sidebar-expanded right-sidebar-collapsed'
+
+ hideSidebar: =>
+ if @shouldHideSidebar()
+ $('.js-build-sidebar')
+ .removeClass 'right-sidebar-expanded'
+ .addClass 'right-sidebar-collapsed'
+ else
+ $('.js-build-sidebar')
+ .removeClass 'right-sidebar-collapsed'
+ .addClass 'right-sidebar-expanded'
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 94985413746..06a688690f8 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -273,7 +273,9 @@
padding-right: 0;
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
- padding-right: $sidebar_collapsed_width;
+ &:not(.build-sidebar) {
+ padding-right: $sidebar_collapsed_width;
+ }
}
@media (min-width: $screen-md-min) {
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 99e3df119ed..847b2f80bdf 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -260,3 +260,6 @@ $calendar-header-color: #b8b8b8;
$calendar-hover-bg: #ecf3fe;
$calendar-border-color: rgba(#000, .1);
$calendar-unselectable-bg: #faf9f9;
+
+$ci-output-bg: #1d1f21;
+$ci-text-color: #c5c8c6;
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index 44222e8e8a4..e8f1935d239 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -53,37 +53,92 @@
left: 70px;
}
}
+}
- .build-widget {
- padding: 10px;
- background: $background-color;
- margin-bottom: 20px;
- border-radius: 4px;
+.build-header {
+ position: relative;
+ padding-right: 40px;
- .title {
- margin-top: 0;
- color: #666;
- line-height: 1.5;
- }
- .attr-name {
- color: #777;
- }
+ @media (min-width: $screen-sm-min) {
+ padding-right: 0;
}
- .alert-disabled {
- background: $background-color;
+ a {
+ color: $gl-gray;
- a {
- color: #3084bb !important;
+ &:hover {
+ color: $gl-link-color;
+ text-decoration: none;
}
}
+
+ code {
+ color: $code-color;
+ }
+
+ .avatar {
+ float: none;
+ margin-right: 2px;
+ margin-left: 2px;
+ }
}
table.builds {
-
.build-link {
a {
color: $gl-dark-link-color;
}
}
}
+
+.build-trace {
+ background: $ci-output-bg;
+ color: $ci-text-color;
+ white-space: pre;
+ overflow-x: auto;
+ font-size: 12px;
+
+ .fa-refresh {
+ font-size: 24px;
+ }
+
+ .bash {
+ display: block;
+ }
+}
+
+.right-sidebar.build-sidebar {
+ padding-top: $gl-padding;
+ padding-bottom: $gl-padding;
+
+ &.right-sidebar-collapsed {
+ display: none;
+ }
+
+ .block {
+ width: 100%;
+ }
+
+ .build-sidebar-header {
+ padding-top: 0;
+
+ .gutter-toggle {
+ margin-top: 0;
+ }
+ }
+}
+
+.build-detail-row {
+ margin-bottom: 5px;
+}
+
+.build-light-text {
+ color: $gl-placeholder-color;
+}
+
+.build-gutter-toggle {
+ position: absolute;
+ top: 50%;
+ right: 0;
+ margin-top: -17px;
+}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 787c387379e..ea453ce356a 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -29,7 +29,7 @@
}
}
-.issuable-sidebar {
+.right-sidebar {
a {
color: inherit;
}
@@ -74,6 +74,10 @@
}
}
+ .block-first {
+ padding-top: 0;
+ }
+
.title {
color: $gl-text-color;
margin-bottom: 10px;
diff --git a/app/assets/stylesheets/pages/xterm.scss b/app/assets/stylesheets/pages/xterm.scss
index 3f28e402929..8d855ce99b0 100644
--- a/app/assets/stylesheets/pages/xterm.scss
+++ b/app/assets/stylesheets/pages/xterm.scss
@@ -11,18 +11,15 @@
$magenta: #cd00cd;
$cyan: #00cdcd;
$white: #e5e5e5;
- $l-black: #7f7f7f;
- $l-red: #f00;
- $l-green: #0f0;
- $l-yellow: #ff0;
- $l-blue: #5c5cff;
- $l-magenta: #f0f;
- $l-cyan: #0ff;
- $l-white: #fff;
+ $l-black: #373b41;
+ $l-red: #c66;
+ $l-green: #b5bd68;
+ $l-yellow: #f0c674;
+ $l-blue: #81a2be;
+ $l-magenta: #b294bb;
+ $l-cyan: #8abeb7;
+ $l-white: $ci-text-color;
- .term-bold {
- font-weight: bold;
- }
.term-italic {
font-style: italic;
}
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 62f63701799..cd6ae507cf1 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -6,6 +6,7 @@ class ApplicationController < ActionController::Base
include Gitlab::GonHelper
include GitlabRoutingHelper
include PageLayoutHelper
+ include WorkhorseHelper
before_action :authenticate_user_from_token!
before_action :authenticate_user!
diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb
index 72921b3aa14..5962f74c39b 100644
--- a/app/controllers/projects/avatars_controller.rb
+++ b/app/controllers/projects/avatars_controller.rb
@@ -10,10 +10,7 @@ class Projects::AvatarsController < Projects::ApplicationController
return if cached_blob?
- headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob))
- headers['Content-Disposition'] = 'inline'
- headers['Content-Type'] = safe_content_type(@blob)
- head :ok # 'render nothing: true' messes up the Content-Type
+ send_git_blob @repository, @blob
else
render_404
end
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index 9b80efa5f11..14c82826342 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -41,7 +41,7 @@ class Projects::BuildsController < Projects::ApplicationController
def trace
respond_to do |format|
format.json do
- render json: @build.trace_with_state(params[:state]).merge!(id: @build.id, status: @build.status)
+ render json: @build.trace_with_state(params[:state].presence).merge!(id: @build.id, status: @build.status)
end
end
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 06a114dcbe8..67e7187c10d 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -61,12 +61,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
format.json { render json: @merge_request }
format.patch { render text: @merge_request.to_patch }
format.diff do
- headers.store(*Gitlab::Workhorse.send_git_diff(@project.repository,
- @merge_request.diff_base_commit.id,
- @merge_request.last_commit.id))
- headers['Content-Disposition'] = 'inline'
+ return render_404 unless @merge_request.diff_refs
- head :ok
+ send_git_diff @project.repository, @merge_request.diff_refs
end
end
end
diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb
index 10de0e60530..10d24da16d7 100644
--- a/app/controllers/projects/raw_controller.rb
+++ b/app/controllers/projects/raw_controller.rb
@@ -18,10 +18,7 @@ class Projects::RawController < Projects::ApplicationController
if @blob.lfs_pointer?
send_lfs_object
else
- headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob))
- headers['Content-Disposition'] = 'inline'
- headers['Content-Type'] = safe_content_type(@blob)
- head :ok # 'render nothing: true' messes up the Content-Type
+ send_git_blob @repository, @blob
end
else
render_404
diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb
index bb7a6b6a5ab..d5af0341d18 100644
--- a/app/controllers/projects/repositories_controller.rb
+++ b/app/controllers/projects/repositories_controller.rb
@@ -11,8 +11,7 @@ class Projects::RepositoriesController < Projects::ApplicationController
end
def archive
- headers.store(*Gitlab::Workhorse.send_git_archive(@project, params[:ref], params[:format]))
- head :ok
+ send_git_archive @repository, ref: params[:ref], format: params[:format]
rescue => ex
logger.error("#{self.class.name}: #{ex}")
return git_not_found!
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index f685e547537..39831ce2e48 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -30,6 +30,8 @@ module NavHelper
else
"page-gutter right-sidebar-expanded"
end
+ elsif current_path?('builds#show')
+ "page-gutter build-sidebar right-sidebar-expanded"
end
end
diff --git a/app/helpers/workhorse_helper.rb b/app/helpers/workhorse_helper.rb
new file mode 100644
index 00000000000..2bd0dbfd095
--- /dev/null
+++ b/app/helpers/workhorse_helper.rb
@@ -0,0 +1,24 @@
+# Helpers to send Git blobs, diffs or archives through Workhorse.
+# Workhorse will also serve files when using `send_file`.
+module WorkhorseHelper
+ # Send a Git blob through Workhorse
+ def send_git_blob(repository, blob)
+ headers.store(*Gitlab::Workhorse.send_git_blob(repository, blob))
+ headers['Content-Disposition'] = 'inline'
+ headers['Content-Type'] = safe_content_type(blob)
+ head :ok # 'render nothing: true' messes up the Content-Type
+ end
+
+ # Send a Git diff through Workhorse
+ def send_git_diff(repository, diff_refs)
+ headers.store(*Gitlab::Workhorse.send_git_diff(repository, diff_refs))
+ headers['Content-Disposition'] = 'inline'
+ head :ok
+ end
+
+ # Archive a Git repository and send it through Workhorse
+ def send_git_archive(repository, ref:, format:)
+ headers.store(*Gitlab::Workhorse.send_git_archive(repository, ref: ref, format: format))
+ head :ok
+ end
+end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 29f36570912..7b8858b24d6 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -276,14 +276,6 @@ class MergeRequest < ActiveRecord::Base
true
end
- def gitlab_merge_status
- if work_in_progress?
- "work_in_progress"
- else
- merge_status_name
- end
- end
-
def can_cancel_merge_when_build_succeeds?(current_user)
can_be_merged_by?(current_user) || self.author == current_user
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 472e79cfcf8..e2f7ffe493c 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -253,20 +253,69 @@ class Project < ActiveRecord::Base
non_archived.where(table[:name].matches(pattern))
end
- def find_with_namespace(id)
- namespace_path, project_path = id.split('/', 2)
+ # Finds a single project for the given path.
+ #
+ # path - The full project path (including namespace path).
+ #
+ # Returns a Project, or nil if no project could be found.
+ def find_with_namespace(path)
+ where_paths_in([path]).reorder(nil).take
+ end
- return nil if !namespace_path || !project_path
+ # Builds a relation to find multiple projects by their full paths.
+ #
+ # Each path must be in the following format:
+ #
+ # namespace_path/project_path
+ #
+ # For example:
+ #
+ # gitlab-org/gitlab-ce
+ #
+ # Usage:
+ #
+ # Project.where_paths_in(%w{gitlab-org/gitlab-ce gitlab-org/gitlab-ee})
+ #
+ # This would return the projects with the full paths matching the values
+ # given.
+ #
+ # paths - An Array of full paths (namespace path + project path) for which
+ # to find the projects.
+ #
+ # Returns an ActiveRecord::Relation.
+ def where_paths_in(paths)
+ wheres = []
+ cast_lower = Gitlab::Database.postgresql?
+
+ paths.each do |path|
+ namespace_path, project_path = path.split('/', 2)
+
+ next unless namespace_path && project_path
+
+ namespace_path = connection.quote(namespace_path)
+ project_path = connection.quote(project_path)
+
+ where = "(namespaces.path = #{namespace_path}
+ AND projects.path = #{project_path})"
+
+ if cast_lower
+ where = "(
+ #{where}
+ OR (
+ LOWER(namespaces.path) = LOWER(#{namespace_path})
+ AND LOWER(projects.path) = LOWER(#{project_path})
+ )
+ )"
+ end
- # Use of unscoped ensures we're not secretly adding any ORDER BYs, which
- # have a negative impact on performance (and aren't needed for this
- # query).
- projects = unscoped.
- joins(:namespace).
- iwhere('namespaces.path' => namespace_path)
+ wheres << where
+ end
- projects.find_by('projects.path' => project_path) ||
- projects.iwhere('projects.path' => project_path).take
+ if wheres.empty?
+ none
+ else
+ joins(:namespace).where(wheres.join(' OR '))
+ end
end
def visibility_levels
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index ca99ba8def3..79cc5c02888 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -33,7 +33,7 @@
= navbar_icon('activity')
%span
Activity
-
+
- if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare repositories tags branches releases network)) do
= link_to project_files_path(@project), title: 'Code', class: 'shortcuts-tree' do
@@ -62,15 +62,8 @@
%span
Graphs
- - if project_nav_tab? :milestones
- = nav_link(controller: :milestones) do
- = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do
- = navbar_icon('milestones')
- %span
- Milestones
-
- if project_nav_tab? :issues
- = nav_link(controller: :issues) do
+ = nav_link(controller: [:issues, :labels, :milestones]) do
= link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues' do
= navbar_icon('issues')
%span
@@ -86,13 +79,6 @@
Merge Requests
%span.badge.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count)
- - if project_nav_tab? :labels
- = nav_link(controller: :labels) do
- = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do
- = icon('tags fw')
- %span
- Labels
-
- if project_nav_tab? :wiki
= nav_link(controller: :wikis) do
= link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do
diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml
index ede01dcc1aa..539d07d634a 100644
--- a/app/views/projects/artifacts/browse.html.haml
+++ b/app/views/projects/artifacts/browse.html.haml
@@ -1,4 +1,5 @@
- page_title 'Artifacts', "#{@build.name} (##{@build.id})", 'Builds'
+- header_title project_title(@project, "Builds", project_builds_path(@project))
.top-block.row-content-block.clearfix
.pull-right
diff --git a/app/views/projects/builds/_header.html.haml b/app/views/projects/builds/_header.html.haml
new file mode 100644
index 00000000000..51b5bd9db42
--- /dev/null
+++ b/app/views/projects/builds/_header.html.haml
@@ -0,0 +1,16 @@
+.content-block.build-header
+ = ci_status_with_icon(@build.status)
+ Build
+ %strong ##{@build.id}
+ for commit
+ = link_to ci_status_path(@build.pipeline) do
+ %strong= @build.pipeline.short_sha
+ from
+ = link_to namespace_project_commits_path(@project.namespace, @project, @build.ref) do
+ %code
+ = @build.ref
+ - if @build.user
+ = render "user"
+ = time_ago_with_tooltip(@build.created_at)
+ %button.btn.btn-default.pull-right.visible-xs-block.visible-sm-block.build-gutter-toggle.js-sidebar-build-toggle{ role: "button", type: "button" }
+ = icon('angle-double-left')
diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml
new file mode 100644
index 00000000000..5d931389dfb
--- /dev/null
+++ b/app/views/projects/builds/_sidebar.html.haml
@@ -0,0 +1,93 @@
+%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar
+ .block.build-sidebar-header.visible-xs-block.visible-sm-block.append-bottom-default
+ Build
+ %strong ##{@build.id}
+ %a.gutter-toggle.pull-right.js-sidebar-build-toggle{ href: "#" }
+ = icon('angle-double-right')
+ - if @build.coverage
+ .block.block-first
+ .title
+ Test coverage
+ %p.build-detail-row
+ #{@build.coverage}%
+
+ - if can?(current_user, :read_build, @project) && @build.artifacts?
+ .block{ class: ("block-first" if !@build.coverage) }
+ .title
+ Build artifacts
+ .btn-group.btn-group-justified{ role: :group }
+ = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default' do
+ Download
+
+ - if @build.artifacts_metadata?
+ = link_to browse_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default' do
+ Browse
+
+ .block{ class: ("block-first" if !@build.coverage && !(can?(current_user, :read_build, @project) && @build.artifacts?)) }
+ .title
+ Build details
+ - if @build.retryable?
+ = link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right', method: :post
+ - if @build.merge_request
+ %p.build-detail-row
+ %span.build-light-text Merge Request:
+ = link_to "#{@build.merge_request.to_reference}", merge_request_path(@build.merge_request)
+ - if @build.duration
+ %p.build-detail-row
+ %span.build-light-text Duration:
+ #{duration_in_words(@build.finished_at, @build.started_at)}
+ - if @build.finished_at
+ %p.build-detail-row
+ %span.build-light-text Finished:
+ #{time_ago_with_tooltip(@build.finished_at)}
+ - if @build.erased_at
+ %p.build-detail-row
+ %span.build-light-text Erased:
+ #{time_ago_with_tooltip(@build.erased_at)}
+ %p.build-detail-row
+ %span.build-light-text Runner:
+ - if @build.runner && current_user && current_user.admin
+ = link_to "##{@build.runner.id}", admin_runner_path(@build.runner.id)
+ - elsif @build.runner
+ \##{@build.runner.id}
+ .btn-group.btn-group-justified{ role: :group }
+ - if @build.has_trace?
+ = link_to 'Raw', raw_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default'
+ - if @build.active?
+ = link_to "Cancel", cancel_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default', method: :post
+ - if can?(current_user, :update_build, @project) && @build.erasable?
+ = link_to erase_namespace_project_build_path(@project.namespace, @project, @build),
+ class: "btn btn-sm btn-default", method: :post,
+ data: { confirm: "Are you sure you want to erase this build?" } do
+ Erase
+
+ - if @build.trigger_request
+ .build-widget
+ %h4.title
+ Trigger
+
+ %p
+ %span.build-light-text Token:
+ #{@build.trigger_request.trigger.short_token}
+
+ - if @build.trigger_request.variables
+ %p
+ %span.build-light-text Variables:
+
+ %code
+ - @build.trigger_request.variables.each do |key, value|
+ #{key}=#{value}
+
+ .block
+ .title
+ Commit message
+ %p.build-light-text.append-bottom-0
+ #{@build.pipeline.git_commit_message}
+
+ - if @build.tags.any?
+ .block
+ .title
+ Tags
+ - @build.tag_list.each do |tag|
+ %span.label.label-primary
+ = tag
diff --git a/app/views/projects/builds/_user.html.haml b/app/views/projects/builds/_user.html.haml
new file mode 100644
index 00000000000..2642de8021d
--- /dev/null
+++ b/app/views/projects/builds/_user.html.haml
@@ -0,0 +1,4 @@
+by
+%a{ href: user_path(@build.user) }
+ = image_tag avatar_icon(@build.user, 24), class: "avatar s24"
+ %strong= @build.user.to_reference
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index 5477fc65c2b..a26f8aeb315 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -1,18 +1,10 @@
- page_title "#{@build.name} (##{@build.id})", "Builds"
- trace_with_state = @build.trace_with_state
+- header_title project_title(@project, "Builds", project_builds_path(@project))
.build-page
- .row-content-block.top-block
- Build ##{@build.id} for commit
- %strong.monospace= link_to @build.pipeline.short_sha, ci_status_path(@build.pipeline)
- from
- = link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref)
- - merge_request = @build.merge_request
- - if merge_request
- via
- = link_to "merge request #{merge_request.to_reference}", merge_request_path(merge_request)
+ = render "header"
- #up-build-trace
- builds = @build.pipeline.builds.latest.to_a
- if builds.size > 1
%ul.nav-links.no-top.no-bottom
@@ -33,18 +25,6 @@
&middot;
%i.fa.fa-warning
This build was retried.
-
- .row-content-block.middle-block
- .build-head
- .clearfix
- = ci_status_with_icon(@build.status)
- - if @build.duration
- %span
- %i.fa.fa-time
- #{duration_in_words(@build.finished_at, @build.started_at)}
- .pull-right
- #{time_ago_with_tooltip(@build.finished_at) if @build.finished_at}
-
- if @build.stuck?
- unless @build.any_runners_online?
.bs-callout.bs-callout-warning
@@ -64,158 +44,27 @@
= link_to namespace_project_runners_path(@build.project.namespace, @build.project) do
Runners page
- .row.prepend-top-default
- .col-md-9
- .clearfix
- - if @build.active?
- .autoscroll-container
- %button.btn.btn-success.btn-sm#autoscroll-button{:type => "button", :data => {:state => 'disabled'}} enable autoscroll
- .clearfix
+ .prepend-top-default
+ - if @build.active?
+ .autoscroll-container
+ %button.btn.btn-success.btn-sm#autoscroll-button{:type => "button", :data => {:state => 'disabled'}} enable autoscroll
#js-build-scroll.scroll-controls
- = link_to '#up-build-trace', class: 'btn' do
+ = link_to '#build-trace', class: 'btn' do
%i.fa.fa-angle-up
= link_to '#down-build-trace', class: 'btn' do
%i.fa.fa-angle-down
+ - if @build.erased?
+ .erased.alert.alert-warning
+ - erased_by = "by #{link_to @build.erased_by.name, user_path(@build.erased_by)}" if @build.erased_by
+ Build has been erased #{erased_by.html_safe} #{time_ago_with_tooltip(@build.erased_at)}
+ - else
+ %pre.build-trace#build-trace
+ %code.bash.js-build-output
+ = icon("refresh spin", class: "js-build-refresh")
- - if @build.erased?
- .erased.alert.alert-warning
- - erased_by = "by #{link_to @build.erased_by.name, user_path(@build.erased_by)}" if @build.erased_by
- Build has been erased #{erased_by.html_safe} #{time_ago_with_tooltip(@build.erased_at)}
- - else
- %pre.trace#build-trace
- %code.bash
- = preserve do
- = raw trace_with_state[:html]
- - if @build.active?
- %i{:class => "fa fa-refresh fa-spin"}
-
- %div#down-build-trace
-
- .col-md-3
- - if @build.coverage
- .build-widget
- %h4.title
- Test coverage
- %h1 #{@build.coverage}%
-
- - if can?(current_user, :read_build, @project) && @build.artifacts?
- .build-widget.artifacts
- %h4.title Build artifacts
- .center
- .btn-group{ role: :group }
- = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary' do
- = icon('download')
- Download
-
- - if @build.artifacts_metadata?
- = link_to browse_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary' do
- = icon('folder-open')
- Browse
-
- .build-widget.build-controls
- %h4.title
- Build ##{@build.id}
- - if can?(current_user, :update_build, @project)
- .center
- .btn-group{ role: :group }
- - if @build.active?
- = link_to "Cancel", cancel_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-danger', method: :post
- - elsif @build.retryable?
- = link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary', method: :post
-
- - if @build.erasable?
- = link_to erase_namespace_project_build_path(@project.namespace, @project, @build),
- class: 'btn btn-sm btn-warning', method: :post,
- data: { confirm: 'Are you sure you want to erase this build?' } do
- = icon('eraser')
- Erase
- - if @build.has_trace?
- = link_to 'Raw', raw_namespace_project_build_path(@project.namespace, @project, @build),
- class: 'btn btn-sm btn-success', target: '_blank'
-
- .clearfix
- - if @build.duration
- %p
- %span.attr-name Duration:
- #{duration_in_words(@build.finished_at, @build.started_at)}
- %p
- %span.attr-name Created:
- #{time_ago_with_tooltip(@build.created_at)}
- - if @build.finished_at
- %p
- %span.attr-name Finished:
- #{time_ago_with_tooltip(@build.finished_at)}
- - if @build.erased_at
- %p
- %span.attr-name Erased:
- #{time_ago_with_tooltip(@build.erased_at)}
- %p
- %span.attr-name Runner:
- - if @build.runner && current_user && current_user.admin
- = link_to "##{@build.runner.id}", admin_runner_path(@build.runner.id)
- - elsif @build.runner
- \##{@build.runner.id}
-
- - if @build.trigger_request
- .build-widget
- %h4.title
- Trigger
-
- %p
- %span.attr-name Token:
- #{@build.trigger_request.trigger.short_token}
-
- - if @build.trigger_request.variables
- %p
- %span.attr-name Variables:
-
- %code
- - @build.trigger_request.variables.each do |key, value|
- #{key}=#{value}
-
- .build-widget
- %h4.title
- Commit
- .pull-right
- %small
- = link_to @build.pipeline.short_sha, ci_status_path(@build.pipeline), class: "monospace"
- %p
- %span.attr-name Branch:
- = link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref)
- %p
- %span.attr-name Author:
- #{@build.pipeline.git_author_name}
- %p
- %span.attr-name Message:
- #{@build.pipeline.git_commit_message}
-
- - if @build.tags.any?
- .build-widget
- %h4.title
- Tags
- - @build.tag_list.each do |tag|
- %span.label.label-primary
- = tag
-
- - if @builds.present?
- .build-widget
- %h4.title #{pluralize(@builds.count(:id), "other build")} for
- = succeed ":" do
- = link_to @build.pipeline.short_sha, ci_status_path(@build.pipeline), class: "monospace"
- %table.table.builds
- - @builds.each_with_index do |build, i|
- %tr.build
- %td
- = ci_icon_for_status(build.status)
- %td
- = link_to namespace_project_build_path(@project.namespace, @project, build) do
- - if build.name
- = build.name
- - else
- %span ##{build.id}
-
- %td.status= build.status
+ #down-build-trace
+= render "sidebar"
- :javascript
- new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{@build.status}", "#{trace_with_state[:state]}")
+:javascript
+ new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{@build.status}", "#{trace_with_state[:state]}")
diff --git a/app/views/projects/issues/_head.html.haml b/app/views/projects/issues/_head.html.haml
new file mode 100644
index 00000000000..166dae248b6
--- /dev/null
+++ b/app/views/projects/issues/_head.html.haml
@@ -0,0 +1,25 @@
+%ul.nav-links.sub-nav
+ %div{ class: (container_class) }
+ - if project_nav_tab?(:issues) && !current_controller?(:merge_requests)
+ = nav_link(controller: :issues) do
+ = link_to url_for_project_issues(@project, only_path: true), title: 'Issues' do
+ %span
+ Issues
+
+ - if project_nav_tab?(:merge_requests) && current_controller?(:merge_requests)
+ = nav_link(controller: :merge_requests) do
+ = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests' do
+ %span
+ Merge Requests
+
+ - if project_nav_tab? :labels
+ = nav_link(controller: :labels) do
+ = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do
+ %span
+ Labels
+
+ - if project_nav_tab? :milestones
+ = nav_link(controller: :milestones) do
+ = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do
+ %span
+ Milestones
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index 7c1457553d9..cd876b5ea62 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -1,23 +1,26 @@
+- @no_container = true
- page_title "Issues"
+= render "projects/issues/head"
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
-.top-area
- = render 'shared/issuable/nav', type: :issues
- .nav-controls
- - if current_user
- = link_to namespace_project_issues_path(@project.namespace, @project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do
- = icon('rss')
- %span.icon-label
- Subscribe
- = render 'shared/issuable/search_form', path: namespace_project_issues_path(@project.namespace, @project)
- - if can? current_user, :create_issue, @project
- = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { assignee_id: @issuable_finder.assignee.try(:id), milestone_id: @issuable_finder.milestones.try(:first).try(:id) }), class: "btn btn-new", title: "New Issue", id: "new_issue_link" do
- New Issue
+%div{ class: (container_class) }
+ .top-area
+ = render 'shared/issuable/nav', type: :issues
+ .nav-controls
+ - if current_user
+ = link_to namespace_project_issues_path(@project.namespace, @project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do
+ = icon('rss')
+ %span.icon-label
+ Subscribe
+ = render 'shared/issuable/search_form', path: namespace_project_issues_path(@project.namespace, @project)
+ - if can? current_user, :create_issue, @project
+ = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { assignee_id: @issuable_finder.assignee.try(:id), milestone_id: @issuable_finder.milestones.try(:first).try(:id) }), class: "btn btn-new", title: "New Issue", id: "new_issue_link" do
+ New Issue
-= render 'shared/issuable/filter', type: :issues
+ = render 'shared/issuable/filter', type: :issues
-.issues-holder
- = render "issues"
+ .issues-holder
+ = render "issues"
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index 93583c92609..6e1baa46b05 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -1,35 +1,38 @@
+- @no_container = true
- page_title "Labels"
- hide_class = ''
+= render "projects/issues/head"
-.top-area
- .nav-text
- Labels can be applied to issues and merge requests.
- .nav-controls
- - if can?(current_user, :admin_label, @project)
- = link_to new_namespace_project_label_path(@project.namespace, @project), class: "btn btn-new" do
- New label
+%div{ class: (container_class) }
+ .top-area
+ .nav-text
+ Labels can be applied to issues and merge requests.
+ .nav-controls
+ - if can?(current_user, :admin_label, @project)
+ = link_to new_namespace_project_label_path(@project.namespace, @project), class: "btn btn-new" do
+ New label
-.labels
- - if can?(current_user, :admin_label, @project)
- -# Only show it in the first page
- - hide = @project.labels.empty? || (params[:page].present? && params[:page] != '1')
- .prioritized-labels{ class: ('hide' if hide) }
- %h5 Prioritized Labels
- %ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_namespace_project_labels_path(@project.namespace, @project) }
- - if @prioritized_labels.present?
- = render @prioritized_labels
- - else
- %p.empty-message No prioritized labels yet
- .other-labels
+ .labels
- if can?(current_user, :admin_label, @project)
- %h5{ class: ('hide' if hide) } Other Labels
- - if @labels.present?
- %ul.content-list.manage-labels-list.js-other-labels
- = render @labels
- = paginate @labels, theme: 'gitlab'
- - else
- .nothing-here-block
- - if can?(current_user, :admin_label, @project)
- Create a label or #{link_to 'generate a default set of labels', generate_namespace_project_labels_path(@project.namespace, @project), method: :post}.
- - else
- No labels created
+ -# Only show it in the first page
+ - hide = @project.labels.empty? || (params[:page].present? && params[:page] != '1')
+ .prioritized-labels{ class: ('hide' if hide) }
+ %h5 Prioritized Labels
+ %ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_namespace_project_labels_path(@project.namespace, @project) }
+ - if @prioritized_labels.present?
+ = render @prioritized_labels
+ - else
+ %p.empty-message No prioritized labels yet
+ .other-labels
+ - if can?(current_user, :admin_label, @project)
+ %h5{ class: ('hide' if hide) } Other Labels
+ - if @labels.present?
+ %ul.content-list.manage-labels-list.js-other-labels
+ = render @labels
+ = paginate @labels, theme: 'gitlab'
+ - else
+ .nothing-here-block
+ - if can?(current_user, :admin_label, @project)
+ Create a label or #{link_to 'generate a default set of labels', generate_namespace_project_labels_path(@project.namespace, @project), method: :post}.
+ - else
+ No labels created
diff --git a/app/views/projects/merge_requests/_head.html.haml b/app/views/projects/merge_requests/_head.html.haml
deleted file mode 100644
index 19e4dab874b..00000000000
--- a/app/views/projects/merge_requests/_head.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-.top-tabs
- = link_to namespace_project_merge_requests_path(@project.namespace, @project), class: "tab #{'active' if current_page?(namespace_project_merge_requests_path(@project.namespace, @project)) }" do
- %span
- Merge Requests
-
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index c8653cb0c30..9f948d41dda 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -1,18 +1,20 @@
+- @no_container = true
- page_title "Merge Requests"
-
+= render "projects/issues/head"
= render 'projects/last_push'
-.top-area
- = render 'shared/issuable/nav', type: :merge_requests
- .nav-controls
- = render 'shared/issuable/search_form', path: namespace_project_merge_requests_path(@project.namespace, @project)
+%div{ class: (container_class) }
+ .top-area
+ = render 'shared/issuable/nav', type: :merge_requests
+ .nav-controls
+ = render 'shared/issuable/search_form', path: namespace_project_merge_requests_path(@project.namespace, @project)
- - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
- - if merge_project
- = link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project), class: "btn btn-new", title: "New Merge Request" do
- New Merge Request
+ - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
+ - if merge_project
+ = link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project), class: "btn btn-new", title: "New Merge Request" do
+ New Merge Request
-= render 'shared/issuable/filter', type: :merge_requests
+ = render 'shared/issuable/filter', type: :merge_requests
-.merge-requests-holder
- = render 'merge_requests'
+ .merge-requests-holder
+ = render 'merge_requests'
diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml
index 60a5b83434e..b0e0bdfff5a 100644
--- a/app/views/projects/milestones/index.html.haml
+++ b/app/views/projects/milestones/index.html.haml
@@ -1,19 +1,22 @@
+- @no_container = true
- page_title "Milestones"
+= render "projects/issues/head"
-.top-area
- = render 'shared/milestones_filter'
+%div{ class: (container_class) }
+ .top-area
+ = render 'shared/milestones_filter'
- .nav-controls
- - if can?(current_user, :admin_milestone, @project)
- = link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "btn btn-new", title: "New Milestone" do
- New Milestone
+ .nav-controls
+ - if can?(current_user, :admin_milestone, @project)
+ = link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "btn btn-new", title: "New Milestone" do
+ New Milestone
-.milestones
- %ul.content-list
- = render @milestones
+ .milestones
+ %ul.content-list
+ = render @milestones
- - if @milestones.blank?
- %li
- .nothing-here-block No milestones to show
+ - if @milestones.blank?
+ %li
+ .nothing-here-block No milestones to show
- = paginate @milestones, theme: "gitlab"
+ = paginate @milestones, theme: "gitlab"