Commit b866cba7 authored by Eric Eastwood's avatar Eric Eastwood

Add Auto DevOps and Kubernetes cluster button to project page

parent dd8f56e8
...@@ -444,6 +444,19 @@ ...@@ -444,6 +444,19 @@
} }
} }
.btn-missing {
color: $notes-light-color;
border: 1px dashed $border-gray-normal-dashed;
border-radius: $border-radius-default;
&:hover,
&:active,
&:focus {
color: $notes-light-color;
background-color: $white-normal;
}
}
.btn-svg svg { .btn-svg svg {
@include btn-svg; @include btn-svg;
} }
......
...@@ -63,10 +63,6 @@ ...@@ -63,10 +63,6 @@
} }
} }
.project-stats {
display: none;
}
.group-buttons { .group-buttons {
display: none; display: none;
} }
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
transition: padding $sidebar-transition-duration; transition: padding $sidebar-transition-duration;
.container-fluid { .container-fluid {
background: $white-light;
padding: 0 $gl-padding; padding: 0 $gl-padding;
&.container-blank { &.container-blank {
......
...@@ -296,7 +296,7 @@ body { ...@@ -296,7 +296,7 @@ body {
line-height: 1.3; line-height: 1.3;
font-size: 1.25em; font-size: 1.25em;
font-weight: $gl-font-weight-bold; font-weight: $gl-font-weight-bold;
margin: 12px 7px; margin: 12px 0;
} }
h1, h1,
......
...@@ -215,8 +215,8 @@ $tooltip-font-size: 12px; ...@@ -215,8 +215,8 @@ $tooltip-font-size: 12px;
*/ */
$gl-padding: 16px; $gl-padding: 16px;
$gl-padding-8: 8px; $gl-padding-8: 8px;
$gl-padding-4: 4px;
$gl-col-padding: 15px; $gl-col-padding: 15px;
$gl-btn-padding: 10px;
$gl-input-padding: 10px; $gl-input-padding: 10px;
$gl-vert-padding: 6px; $gl-vert-padding: 6px;
$gl-padding-top: 10px; $gl-padding-top: 10px;
...@@ -377,6 +377,10 @@ $inactive-badge-background: rgba(0, 0, 0, .08); ...@@ -377,6 +377,10 @@ $inactive-badge-background: rgba(0, 0, 0, .08);
$btn-active-gray: #ececec; $btn-active-gray: #ececec;
$btn-active-gray-light: e4e7ed; $btn-active-gray-light: e4e7ed;
$btn-white-active: #848484; $btn-white-active: #848484;
$gl-btn-padding: 10px;
$gl-btn-line-height: 16px;
$gl-btn-vert-padding: 8px;
$gl-btn-horz-padding: 12px;
/* /*
* Badges * Badges
......
...@@ -678,6 +678,9 @@ a.deploy-project-label { ...@@ -678,6 +678,9 @@ a.deploy-project-label {
} }
} }
.project-empty-note-panel {
border-bottom: 1px solid $border-color;
}
.project-stats { .project-stats {
font-size: 0; font-size: 0;
...@@ -686,11 +689,13 @@ a.deploy-project-label { ...@@ -686,11 +689,13 @@ a.deploy-project-label {
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
.nav { .nav {
padding-top: 12px; margin-top: $gl-padding-8;
padding-bottom: 12px; margin-bottom: $gl-padding-8;
> li { > li {
display: inline-block; display: inline-block;
margin-top: $gl-padding-4;
margin-bottom: $gl-padding-4;
&:not(:last-child) { &:not(:last-child) {
margin-right: $gl-padding; margin-right: $gl-padding;
...@@ -704,36 +709,32 @@ a.deploy-project-label { ...@@ -704,36 +709,32 @@ a.deploy-project-label {
float: right; float: right;
} }
} }
}
> a { .stat-text,
padding: 0; .stat-link {
background-color: transparent; padding: $gl-btn-vert-padding 0;
font-size: 14px; background-color: transparent;
line-height: 29px; font-size: $gl-font-size;
color: $notes-light-color; line-height: $gl-btn-line-height;
color: $notes-light-color;
}
&:hover, .stat-link {
&:focus { &:hover,
color: $gl-text-color; &:focus {
text-decoration: underline; color: $gl-text-color;
} text-decoration: underline;
} }
} }
}
li.missing {
border: 1px dashed $border-gray-normal-dashed;
border-radius: $border-radius-default;
a { .btn {
padding-left: 10px; padding: $gl-btn-vert-padding $gl-btn-horz-padding;
padding-right: 10px; line-height: $gl-btn-line-height;
color: $notes-light-color;
display: block;
} }
&:hover { .btn-missing {
background-color: $gray-normal; @extend .btn-missing;
} }
} }
} }
...@@ -743,7 +744,7 @@ pre.light-well { ...@@ -743,7 +744,7 @@ pre.light-well {
} }
.git-empty { .git-empty {
margin: 0 7px 7px; margin-bottom: 7px;
h5 { h5 {
color: $gl-text-color; color: $gl-text-color;
......
...@@ -604,4 +604,218 @@ module ProjectsHelper ...@@ -604,4 +604,218 @@ module ProjectsHelper
project_find_file_path(@project, ref) project_find_file_path(@project, ref)
end end
def can_current_user_push_code?(project)
project.empty_repo? ? can?(current_user, :push_code, project) : can_push_branch?(project, project.default_branch)
end
def files_anchor_data(project)
{
enabled: true,
label: _('Files (%{human_size})') % { human_size: storage_counter(@project.statistics.total_repository_size) },
link: project_tree_path(@project)
}
end
def commits_anchor_data(project)
{
enabled: true,
label: n_('Commit (%{commit_count})', 'Commits (%{commit_count})', @project.statistics.commit_count) % { commit_count: number_with_delimiter(@project.statistics.commit_count) },
link: project_commits_path(@project, current_ref)
}
end
def branches_anchor_data(project)
{
enabled: true,
label: n_('Branch (%{branch_count})', 'Branches (%{branch_count})', @repository.branch_count) % { branch_count: number_with_delimiter(@repository.branch_count) },
link: project_branches_path(@project)
}
end
def tags_anchor_data(project)
{
enabled: true,
label: n_('Tag (%{tag_count})', 'Tags (%{tag_count})', @repository.tag_count) % { tag_count: number_with_delimiter(@repository.tag_count) },
link: project_tags_path(@project)
}
end
def new_file_anchor_data(project)
if current_user && can_current_user_push_code?(project)
{
enabled: false,
label: _('New file'),
link: project_new_blob_path(project, project.default_branch || 'master'),
class_modifier: 'new'
}
end
end
def readme_anchor_data(project)
if current_user && can_current_user_push_code?(project) && project.repository.readme.blank?
{
enabled: false,
label: _('Add Readme'),
link: add_special_file_path(project, file_name: 'README.md')
}
elsif project.repository.readme.present?
{
enabled: true,
label: _('Readme'),
link: default_project_view != 'readme' ? readme_path(@project) : '#readme'
}
end
end
def changelog_anchor_data(project)
if current_user && can_current_user_push_code?(project) && project.repository.changelog.blank?
{
enabled: false,
label: _('Add Changelog'),
link: add_special_file_path(project, file_name: 'CHANGELOG')
}
elsif project.repository.changelog.present?
{
enabled: true,
label: _('Changelog'),
link: changelog_path(project)
}
end
end
def license_anchor_data(project)
if current_user && can_current_user_push_code?(project) && project.repository.license_blob.blank?
{
enabled: false,
label: _('Add License'),
link: add_special_file_path(project, file_name: 'LICENSE')
}
elsif project.repository.license_blob.present?
{
enabled: true,
label: license_short_name(project),
link: license_path(project)
}
end
end
def contribution_guide_anchor_data(project)
if current_user && can_current_user_push_code?(project) && project.repository.contribution_guide.blank?
{
enabled: false,
label: _('Add Contribution guide'),
link: add_special_file_path(project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide')
}
elsif project.repository.contribution_guide.present?
{
enabled: true,
label: _('Contribution guide'),
link: contribution_guide_path(@project)
}
end
end
def autodevops_anchor_data(project, ignore_callout: false)
if current_user && can?(current_user, :admin_pipeline, project) && project.repository.gitlab_ci_yml.blank? && (ignore_callout || !show_auto_devops_callout?(project))
{
enabled: project.auto_devops_enabled?,
label: project.auto_devops_enabled? ? _('Auto DevOps enabled') : _('Enable Auto DevOps'),
link: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings')
}
elsif project.auto_devops_enabled?
{
enabled: true,
label: _('Auto DevOps enabled'),
link: nil
}
end
end
def kubernetes_cluster_anchor_data(project)
if current_user && can?(current_user, :create_cluster, project)
cluster_link = project.clusters.size == 1 ? project_cluster_path(project, project.clusters.first) : project_clusters_path(project)
if project.clusters.empty?
cluster_link = new_project_cluster_path(project)
end
{
enabled: !project.clusters.empty?,
label: project.clusters.empty? ? _('Add Kubernetes cluster') : n_('Kubernetes cluster', 'Kubernetes clusters', project.clusters.size),
link: cluster_link
}
end
end
def gitlab_ci_anchor_data(project)
if current_user && can_current_user_push_code?(project) && project.repository.gitlab_ci_yml.blank? && !project.auto_devops_enabled?
{
enabled: false,
label: _('Set up CI/CD'),
link: add_special_file_path(project, file_name: '.gitlab-ci.yml')
}
elsif project.repository.gitlab_ci_yml.present?
{
enabled: true,
label: _('CI/CD configuration'),
link: ci_configuration_path(@project)
}
end
end
def koding_anchor_data(project)
if current_user && can_current_user_push_code?(project) && koding_enabled? && project.repository.koding_yml.blank?
{
enabled: false,
label: _('Set up Koding'),
link: add_koding_stack_path(project)
}
end
end
def empty_project_stat_anchor_items(project)
[
autodevops_anchor_data(project, ignore_callout: true),
kubernetes_cluster_anchor_data(project)
].compact.reject { |i| !i[:enabled] }
end
def empty_project_stat_button_items(project)
[
new_file_anchor_data(project),
readme_anchor_data(project),
license_anchor_data(project),
autodevops_anchor_data(project, ignore_callout: true),
kubernetes_cluster_anchor_data(project)
].compact.reject { |i| i[:enabled] }
end
def project_stat_anchor_items(project)
[
files_anchor_data(project),
commits_anchor_data(project),
branches_anchor_data(project),
tags_anchor_data(project),
readme_anchor_data(project),
changelog_anchor_data(project),
license_anchor_data(project),
contribution_guide_anchor_data(project),
gitlab_ci_anchor_data(project),
autodevops_anchor_data(project),
kubernetes_cluster_anchor_data(project)
].compact.reject { |i| !i[:enabled] }
end
def project_stat_button_items(project)
[
changelog_anchor_data(project),
license_anchor_data(project),
contribution_guide_anchor_data(project),
autodevops_anchor_data(project),
kubernetes_cluster_anchor_data(project),
gitlab_ci_anchor_data(project),
koding_anchor_data(project)
].compact.reject { |i| i[:enabled] }
end
end end
- anchors = local_assigns.fetch(:anchors, [])
- if anchors.size > 0
%ul.nav
= render partial: 'stat_anchor_list_item', collection: anchors, as: :anchor
%li
- if anchor[:link]
= link_to anchor[:link], class: anchor[:enabled] ? 'stat-link' : "btn btn-#{anchor[:class_modifier] || 'missing'}" do
= anchor[:label]
- else
%span.stat-text
= anchor[:label]
...@@ -5,38 +5,41 @@ ...@@ -5,38 +5,41 @@
= render "home_panel" = render "home_panel"
.row-content-block.second-block.center .project-empty-note-panel
%h4 %div{ class: [container_class, ("limit-container-width-sm" unless fluid_layout)] }
The repository for this project is empty .prepend-top-20
%h4
= _('The repository for this project is empty')
- if can?(current_user, :push_code, @project)
%p
- link_to_cli = link_to _('command line instructions'), '#repo-command-line-instructions'
= _('If you already have files you can push them using the %{link_to_cli} below.').html_safe % { link_to_cli: link_to_cli }
%p
%em
- link_to_protected_branches = link_to _('Learn more about protected branches'), help_page_path('user/project/protected_branches')
= _('Note that the master branch is automatically protected. %{link_to_protected_branches}').html_safe % { link_to_protected_branches: link_to_protected_branches }
- if can?(current_user, :push_code, @project) %hr
%p %p
If you already have files you can push them using command line instructions below. - link_to_auto_devops_settings = link_to(s_('AutoDevOps|enable Auto DevOps (Beta)'), project_settings_ci_cd_path(@project, anchor: 'js-general-pipeline-settings'))
%p - link_to_add_kubernetes_cluster = link_to(s_('AutoDevOps|add a Kubernetes cluster'), project_clusters_path(@project))
Otherwise you can start with adding a = s_('AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}.').html_safe % { link_to_auto_devops_settings: link_to_auto_devops_settings, link_to_add_kubernetes_cluster: link_to_add_kubernetes_cluster }
= succeed ',' do
= link_to "README", add_special_file_path(@project, file_name: 'README.md')
a
= succeed ',' do
= link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE')
or a
= link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore')
to this project.
%p
You will need to be owner or have the master permission level for the initial push, as the master branch is automatically protected.
- if show_auto_devops_callout?(@project) %hr
%p %p
- link = link_to(s_('AutoDevOps|Auto DevOps (Beta)'), project_settings_ci_cd_path(@project, anchor: 'js-general-pipeline-settings')) = _('Otherwise it is recommended you start with one of the options below.')
= s_('AutoDevOps|You can activate %{link_to_settings} for this project.').html_safe % { link_to_settings: link } .prepend-top-20
%p= s_('AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration.')
%p= link_to _('New file'), project_new_blob_path(@project, @project.default_branch || 'master'), class: 'btn btn-new' %nav.project-stats{ class: container_class }
= render 'stat_anchor_list', anchors: empty_project_stat_anchor_items(@project)
= render 'stat_anchor_list', anchors: empty_project_stat_button_items(@project)
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
%div{ class: container_class } %div{ class: [container_class, ("limit-container-width-sm" unless fluid_layout)] }
.prepend-top-20 .prepend-top-20
.empty_wrapper .empty_wrapper
%h3.page-title-empty %h3#repo-command-line-instructions.page-title-empty
Command line instructions Command line instructions
.git-empty .git-empty
%fieldset %fieldset
......
...@@ -14,65 +14,9 @@ ...@@ -14,65 +14,9 @@
- if can?(current_user, :download_code, @project) - if can?(current_user, :download_code, @project)
%nav.project-stats{ class: container_class } %nav.project-stats{ class: container_class }
%ul.nav = render 'stat_anchor_list', anchors: project_stat_anchor_items(@project)
%li = render 'stat_anchor_list', anchors: project_stat_button_items(@project)
= link_to project_tree_path(@project) do
#{_('Files')} (#{storage_counter(@project.statistics.total_repository_size)})
%li
= link_to project_commits_path(@project, current_ref) do
#{n_('Commit', 'Commits', @project.statistics.commit_count)} (#{number_with_delimiter(@project.statistics.commit_count)})
%li
= link_to project_branches_path(@project) do
#{n_('Branch', 'Branches', @repository.branch_count)} (#{number_with_delimiter(@repository.branch_count)})
%li
= link_to project_tags_path(@project) do
#{n_('Tag', 'Tags', @repository.tag_count)} (#{number_with_delimiter(@repository.tag_count)})
- if @repository.readme
%li
= link_to _('Readme'),
default_project_view != 'readme' ? readme_path(@project) : '#readme'
- if @repository.changelog
%li
= link_to _('Changelog'), changelog_path(@project)
- if @repository.license_blob
%li
= link_to license_short_name(@project), license_path(@project)
- if @repository.contribution_guide
%li
= link_to _('Contribution guide'), contribution_guide_path(@project)
- if @repository.gitlab_ci_yml
%li
= link_to _('CI/CD configuration'), ci_configuration_path(@project)
- if current_user && can_push_branch?(@project, @project.default_branch)
- unless @repository.changelog
%li.missing
= link_to add_special_file_path(@project, file_name: 'CHANGELOG') do
#{ _('Add Changelog') }
- unless @repository.license_blob
%li.missing
= link_to add_special_file_path(@project, file_name: 'LICENSE') do
#{ _('Add License') }
- unless @repository.contribution_guide
%li.missing
= link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do
#{ _('Add Contribution guide') }
- unless @repository.gitlab_ci_yml
%li.missing
= link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do
#{ _('Set up CI/CD') }
- if koding_enabled? && @repository.koding_yml.blank?
%li.missing
= link_to _('Set up Koding'), add_koding_stack_path(@project)
- if @repository.gitlab_ci_yml.blank? && @project.deployment_platform.present?
%li.missing
= link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml', commit_message: 'Set up auto deploy', branch_name: 'auto-deploy', context: 'autodeploy') do
#{ _('Set up auto deploy') }
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] } %div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
- if @project.archived? - if @project.archived?
......
---
title: Add a button on the project page to set up a Kubernetes cluster and enable
Auto DevOps
merge_request: 16900
author:
type: added
require 'spec_helper'
describe 'Auto deploy' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
context 'when no deployment service is active' do
before do
trun_off
end
it 'does not show a button to set up auto deploy' do
visit project_path(project)
expect(page).to have_no_content('Set up auto deploy')
end
end
context 'when a deployment service is active' do
before do
trun_on
visit project_path(project)
end
it 'shows a button to set up auto deploy' do
expect(page).to have_link('Set up auto deploy')
end
it 'includes OpenShift as an available template', :js do
click_link 'Set up auto deploy'
click_button 'Apply a GitLab CI Yaml template'
within '.gitlab-ci-yml-selector' do
expect(page).to have_content('OpenShift')
end
end
it 'creates a merge request using "auto-deploy" branch', :js do
click_link 'Set up auto deploy'
click_button 'Apply a GitLab CI Yaml template'
within '.gitlab-ci-yml-selector' do
click_on 'OpenShift'
end
wait_for_requests
click_button 'Commit changes'
expect(page).to have_content('New Merge Request From auto-deploy into master')
end
end
end
context 'when user configured kubernetes from Integration > Kubernetes' do
before do
create :kubernetes_service, project: project
project.add_master(user)
sign_in user
end
let(:trun_on) { project.deployment_platform.update!(active: true) }
let(:trun_off) { project.deployment_platform.update!(active: false) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
context 'when user configured kubernetes from CI/CD > Clusters' do
before do
create(:cluster, :provided_by_gcp, projects: [project])
project.add_master(user)
sign_in user
end
let(:trun_on) { project.deployment_platform.cluster.update!(enabled: true) }
let(:trun_off) { project.deployment_platform.cluster.update!(enabled: false) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
end
...@@ -11,7 +11,7 @@ feature 'project owner sees a link to create a license file in empty project', : ...@@ -11,7 +11,7 @@ feature 'project owner sees a link to create a license file in empty project', :
scenario 'project master creates a license file from a template' do scenario 'project master creates a license file from a template' do
visit project_path(project) visit project_path(project)
click_on 'LICENSE' click_on 'Add License'
expect(page).to have_content('New file') expect(page).to have_content('New file')
expect(current_path).to eq( expect(current_path).to eq(
......
This diff is collapsed.
...@@ -13,7 +13,7 @@ feature 'Master views tags' do ...@@ -13,7 +13,7 @@ feature 'Master views tags' do
before do before do
visit project_path(project) visit project_path(project)
click_on 'README' click_on 'Add Readme'
fill_in :commit_message, with: 'Add a README file', visible: true fill_in :commit_message, with: 'Add a README file', visible: true
click_button 'Commit changes' click_button 'Commit changes'
visit project_tags_path(project) visit project_tags_path(project)
......
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