summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/projects/pipelines_controller.rb102
-rw-r--r--app/models/ability.rb7
-rw-r--r--app/views/layouts/nav/_project.html.haml7
-rw-r--r--app/views/projects/ci/commits/_commit.html.haml73
-rw-r--r--app/views/projects/ci_commits/_header_title.html.haml1
-rw-r--r--app/views/projects/ci_commits/index.html.haml65
-rw-r--r--app/views/projects/ci_commits/new.html.haml25
-rw-r--r--config/routes.rb7
8 files changed, 286 insertions, 1 deletions
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index e69de29bb2d..764c8cc9cca 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -0,0 +1,102 @@
+class Projects::PipelineController < Projects::ApplicationController
+ before_action :ci_commit, except: [:index, :new, :create]
+ before_action :authorize_read_pipeline!
+ before_action :authorize_create_pipeline!, only: [:new, :create]
+ before_action :authorize_update_pipeline!, only: [:retry, :cancel]
+ layout 'project'
+
+ def index
+ @scope = params[:scope]
+ @all_commits = project.ci_commits
+ @commits = @all_commits.order(id: :desc)
+ @commits =
+ case @scope
+ when 'latest'
+ @commits
+ when 'running'
+ @commits.running_or_pending
+ when 'branches'
+ refs = project.repository.branches.map(&:name)
+ ids = @all_commits.where(ref: refs).group(:ref).select('max(id)')
+ @commits.where(id: ids)
+ when 'tags'
+ refs = project.repository.tags.map(&:name)
+ ids = @all_commits.where(ref: refs).group(:ref).select('max(id)')
+ @commits.where(id: ids)
+ else
+ @commits
+ end
+ @commits = @commits.page(params[:page]).per(30)
+ end
+
+ def new
+ end
+
+ def create
+ ref_names = project.repository.ref_names
+ unless ref_names.include?(params[:ref])
+ @error = 'Reference not found'
+ render action: 'new'
+ return
+ end
+
+ commit = project.commit(params[:ref])
+ unless commit
+ @error = 'Commit not found'
+ render action: 'new'
+ return
+ end
+
+ ci_commit = project.ci_commit(commit.id, params[:ref])
+ if ci_commit
+ @error = 'Pipeline already created'
+ render action: 'new'
+ return
+ end
+
+ # Skip creating ci_commit when no gitlab-ci.yml is found
+ commit = project.ci_commits.new(sha: commit.id, ref: params[:ref], before_sha: Gitlab::Git::BLANK_SHA)
+ unless commit.config_processor
+ @error = commit.yaml_errors || 'Missing .gitlab-ci.yml file'
+ render action: 'new'
+ return
+ end
+
+ Ci::Commit.transaction do
+ commit.save!
+ commit.create_builds(params[:ref], false, current_user)
+ end
+
+ redirect_to builds_namespace_project_commit_path(project.namespace, project, commit.id)
+ end
+
+ def show
+ @commit = @ci_commit.commit
+ @builds = @ci_commit.builds
+ @statuses = @ci_commit.statuses
+
+ respond_to do |format|
+ format.html
+ end
+ end
+
+ def retry
+ ci_commit.builds.latest.failed.select(&:retryable?).each(&:retry)
+
+ redirect_back_or_default default: namespace_project_pipelines_path(project.namespace, project)
+ end
+
+ def cancel
+ ci_commit.builds.running_or_pending.each(&:cancel)
+
+ redirect_back_or_default default: namespace_project_pipelines_path(project.namespace, project)
+ end
+
+ def retry_builds
+ end
+ private
+
+ def ci_commit
+ @ci_commit ||= project.ci_commits.find_by!(id: params[:id])
+ end
+end
diff --git a/app/models/ability.rb b/app/models/ability.rb
index c0bf6def7c5..ec5ac54c277 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -195,6 +195,7 @@ class Ability
:admin_label,
:read_commit_status,
:read_build,
+ :read_pipeline,
]
end
@@ -206,6 +207,8 @@ class Ability
:update_commit_status,
:create_build,
:update_build,
+ :create_pipeline,
+ :update_pipeline,
:create_merge_request,
:create_wiki,
:push_code
@@ -234,7 +237,8 @@ class Ability
:admin_wiki,
:admin_project,
:admin_commit_status,
- :admin_build
+ :admin_build,
+ :admin_pipeline
]
end
@@ -277,6 +281,7 @@ class Ability
unless project.builds_enabled
rules += named_abilities('build')
+ rules += named_abilities('pipeline')
end
rules
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 86b46e8c75e..fcce1b1dc98 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -39,6 +39,13 @@
Commits
- if project_nav_tab? :builds
+ = nav_link(controller: %w(ci_commits)) do
+ = link_to project_ci_commits_path(@project), title: 'Pipelines', class: 'shortcuts-builds' do
+ = icon('ship fw')
+ %span
+ Pipelines
+ %span.count.ci_counter= number_with_delimiter(@project.ci_commits.running_or_pending.count(:all))
+
= nav_link(controller: %w(builds)) do
= link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do
= icon('cubes fw')
diff --git a/app/views/projects/ci/commits/_commit.html.haml b/app/views/projects/ci/commits/_commit.html.haml
index e69de29bb2d..29efcc9cfdd 100644
--- a/app/views/projects/ci/commits/_commit.html.haml
+++ b/app/views/projects/ci/commits/_commit.html.haml
@@ -0,0 +1,73 @@
+- status = commit.status
+%tr.commit
+ %td.commit-link
+ = link_to namespace_project_commit_url(@project.namespace, @project, commit), class: "ci-status ci-#{status}" do
+ = ci_icon_for_status(status)
+ %strong ##{commit.id}
+
+ %td
+ %div
+ - if commit.ref
+ = link_to commit.ref, namespace_project_commits_path(@project.namespace, @project, commit.ref)
+ &nbsp;
+ - if commit.tag?
+ %span.label.label-primary tag
+ - if commit.branch?
+ %span.label.label-primary branch
+ - if commit.trigger_requests.any?
+ %span.label.label-primary triggered
+ - if commit.yaml_errors.present?
+ %span.label.label-danger.has-tooltip(title="#{commit.yaml_errors}") yaml invalid
+ - if commit.builds.any?(&:stuck?)
+ %span.label.label-warning stuck
+
+ - if commit_data = commit.commit_data
+ = render 'projects/branches/commit', commit: commit_data, project: @project
+ - else
+ %p
+ Cant find HEAD commit for this branch
+
+ - stages.each do |stage|
+ %td
+ - status = commit.statuses.latest.where(stage: stage).status
+ %span.has-tooltip(title="#{status || "missing"}"){class: "ci-status-icon-#{status || "skipped"}"}
+ = ci_icon_for_status(status || "missing")
+ -#- if status
+ -# = ci_status_with_icon(status)
+ -#- else
+ -# = ci_status_with_icon('missing')
+
+ %td
+ - if commit.started_at && commit.finished_at
+ %p
+ #{duration_in_words(commit.finished_at, commit.started_at)}
+ - if commit.finished_at
+ %p
+ #{time_ago_with_tooltip(commit.finished_at)}
+
+ %td.content
+ .controls.hidden-xs.pull-right
+ - artifacts = commit.builds.latest.select { |status| status.artifacts? }
+ - if artifacts.present?
+ .dropdown.inline
+ %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
+ = icon('download')
+ %b.caret
+ %ul.dropdown-menu.dropdown-menu-align-right
+ - artifacts.each do |build|
+ %li
+ = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, build), rel: 'nofollow' do
+ %i.fa.fa-download
+ %span #{build.name}
+ &nbsp;
+
+ - if can?(current_user, :update_pipeline, @project)
+ - if commit.retryable?
+ = link_to retry_namespace_project_ci_commit_path(@project.namespace, @project, commit.id), class: 'btn has-tooltip', title: "Retry", method: :post do
+ = icon("repeat")
+
+ &nbsp;
+
+ - if commit.active?
+ = link_to cancel_namespace_project_ci_commit_path(@project.namespace, @project, commit.id), class: 'btn btn-remove has-tooltip', title: "Cancel", method: :post do
+ = icon("remove cred")
diff --git a/app/views/projects/ci_commits/_header_title.html.haml b/app/views/projects/ci_commits/_header_title.html.haml
index e69de29bb2d..27c125ca40f 100644
--- a/app/views/projects/ci_commits/_header_title.html.haml
+++ b/app/views/projects/ci_commits/_header_title.html.haml
@@ -0,0 +1 @@
+- header_title project_title(@project, "Pipelines", project_ci_commits_path(@project))
diff --git a/app/views/projects/ci_commits/index.html.haml b/app/views/projects/ci_commits/index.html.haml
index e69de29bb2d..0347c220382 100644
--- a/app/views/projects/ci_commits/index.html.haml
+++ b/app/views/projects/ci_commits/index.html.haml
@@ -0,0 +1,65 @@
+- page_title "Pipelines"
+= render "header_title"
+
+.top-area
+ %ul.nav-links
+ %li{class: ('active' if @scope.nil?)}
+ = link_to project_ci_commits_path(@project) do
+ All
+ %span.badge.js-totalbuilds-count
+ = number_with_delimiter(@all_commits.count(:id))
+
+ %li{class: ('active' if @scope == 'branches')}
+ = link_to project_ci_commits_path(@project, scope: :branches) do
+ Branches
+ %span.badge.js-running-count
+ = number_with_delimiter(@all_commits.running_or_pending.count(:id))
+
+ %li{class: ('active' if @scope == 'tags')}
+ = link_to project_ci_commits_path(@project, scope: :tags) do
+ Tags
+ %span.badge.js-running-count
+ = number_with_delimiter(@all_commits.running_or_pending.count(:id))
+
+ %li{class: ('active' if @scope == 'running')}
+ = link_to project_ci_commits_path(@project, scope: :running) do
+ Failed
+ %span.badge.js-running-count
+ = number_with_delimiter(@all_commits.running_or_pending.count(:id))
+
+ .nav-controls
+ - if can? current_user, :create_pipeline, @project
+ = link_to new_namespace_project_ci_commit_path(@project.namespace, @project), class: 'btn btn-create' do
+ = icon('plus')
+ New
+
+ - if can?(current_user, :update_build, @project)
+ - unless @repository.gitlab_ci_yml
+ = link_to 'Get started with Pipelines', help_page_path('ci/quick_start', 'README'), class: 'btn btn-info'
+
+ = link_to ci_lint_path, class: 'btn btn-default' do
+ = icon('wrench')
+ %span CI Lint
+
+.gray-content-block
+ Pipelines for #{(@scope || 'changes')} on this project
+
+%ul.content-list
+ - stages = @commits.stages
+ - if @commits.blank?
+ %li
+ .nothing-here-block No pipelines to show
+ - else
+ .table-holder
+ %table.table.builds
+ %tbody
+ %th Pipeline ID
+ %th Commit
+ - @commits.stages.each do |stage|
+ %th
+ = stage.titleize
+ %th
+ %th
+ = render @commits.includes(:statuses).includes(:builds), commit_sha: true, stage: true, allow_retry: true, stages: stages
+
+ = paginate @commits, theme: 'gitlab'
diff --git a/app/views/projects/ci_commits/new.html.haml b/app/views/projects/ci_commits/new.html.haml
index e69de29bb2d..e9a22bbb157 100644
--- a/app/views/projects/ci_commits/new.html.haml
+++ b/app/views/projects/ci_commits/new.html.haml
@@ -0,0 +1,25 @@
+- page_title "New Pipeline"
+= render "header_title"
+
+- if @error
+ .alert.alert-danger
+ %button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
+ = @error
+%h3.page-title
+ New Pipeline
+%hr
+
+= form_tag namespace_project_ci_commits_path, method: :post, id: "new-pipeline-form", class: "form-horizontal js-create-branch-form js-requires-input" do
+ .form-group
+ = label_tag :ref, 'Create for', class: 'control-label'
+ .col-sm-10
+ = text_field_tag :ref, params[:ref] || @project.default_branch, required: true, tabindex: 2, class: 'form-control'
+ .help-block Existing branch name, tag
+ .form-actions
+ = button_tag 'Create pipeline', class: 'btn btn-create', tabindex: 3
+ = link_to 'Cancel', namespace_project_ci_commits_path(@project.namespace, @project), class: 'btn btn-cancel'
+
+:javascript
+ var availableRefs = #{@project.repository.ref_names.to_json};
+
+ new NewBranchForm($('.js-create-branch-form'), availableRefs)
diff --git a/config/routes.rb b/config/routes.rb
index 842fbb99843..841b3f26272 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -654,6 +654,13 @@ Rails.application.routes.draw do
resource :variables, only: [:show, :update]
resources :triggers, only: [:index, :create, :destroy]
+ resources :pipelines, only: [:index, :new, :create] do
+ member do
+ post :cancel
+ post :retry
+ end
+ end
+
resources :builds, only: [:index, :show], constraints: { id: /\d+/ } do
collection do
post :cancel_all