Commit 891acf9d authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch...

Merge branch 'ee-38962-automatically-run-a-pipeline-when-auto-devops-is-turned-on-in-project-settings' into 'master'

Add checkbox to automatically run a pipeline when Auto DevOps is turned on in project settings -- EE merge edition

Closes gitlab-ce#38962

See merge request gitlab-org/gitlab-ee!3578
parents 01fc0fb1 f90b2a36
...@@ -583,6 +583,13 @@ import initGroupAnalytics from './init_group_analytics'; ...@@ -583,6 +583,13 @@ import initGroupAnalytics from './init_group_analytics';
case 'projects:settings:ci_cd:show': case 'projects:settings:ci_cd:show':
// Initialize expandable settings panels // Initialize expandable settings panels
initSettingsPanels(); initSettingsPanels();
import(/* webpackChunkName: "ci-cd-settings" */ './projects/ci_cd_settings_bundle')
.then(ciCdSettings => ciCdSettings.default())
.catch((err) => {
Flash(s__('ProjectSettings|Problem setting up the CI/CD settings JavaScript'));
throw err;
});
case 'groups:settings:ci_cd:show': case 'groups:settings:ci_cd:show':
new ProjectVariables(); new ProjectVariables();
break; break;
......
...@@ -41,7 +41,7 @@ const createFlashEl = (message, type, isInContentWrapper = false) => ` ...@@ -41,7 +41,7 @@ const createFlashEl = (message, type, isInContentWrapper = false) => `
`; `;
const removeFlashClickListener = (flashEl, fadeTransition) => { const removeFlashClickListener = (flashEl, fadeTransition) => {
flashEl.parentNode.addEventListener('click', () => hideFlash(flashEl, fadeTransition)); flashEl.addEventListener('click', () => hideFlash(flashEl, fadeTransition));
}; };
/* /*
......
...@@ -319,6 +319,8 @@ $(function () { ...@@ -319,6 +319,8 @@ $(function () {
const flashContainer = document.querySelector('.flash-container'); const flashContainer = document.querySelector('.flash-container');
if (flashContainer && flashContainer.children.length) { if (flashContainer && flashContainer.children.length) {
removeFlashClickListener(flashContainer.children[0]); flashContainer.querySelectorAll('.flash-alert, .flash-notice, .flash-success').forEach((flashEl) => {
removeFlashClickListener(flashEl);
});
} }
}); });
function updateAutoDevopsRadios(radioWrappers) {
radioWrappers.forEach((radioWrapper) => {
const radio = radioWrapper.querySelector('.js-auto-devops-enable-radio');
const runPipelineCheckboxWrapper = radioWrapper.querySelector('.js-run-auto-devops-pipeline-checkbox-wrapper');
const runPipelineCheckbox = radioWrapper.querySelector('.js-run-auto-devops-pipeline-checkbox');
if (runPipelineCheckbox) {
runPipelineCheckbox.checked = radio.checked;
runPipelineCheckboxWrapper.classList.toggle('hide', !radio.checked);
}
});
}
export default function initCiCdSettings() {
const radioWrappers = document.querySelectorAll('.js-auto-devops-enable-radio-wrapper');
radioWrappers.forEach(radioWrapper =>
radioWrapper.addEventListener('change', () => updateAutoDevopsRadios(radioWrappers)),
);
}
...@@ -34,8 +34,15 @@ ...@@ -34,8 +34,15 @@
} }
} }
.flash-success {
@extend .alert;
@extend .alert-success;
margin: 0;
}
.flash-notice, .flash-notice,
.flash-alert { .flash-alert,
.flash-success {
border-radius: $border-radius-default; border-radius: $border-radius-default;
.container-fluid, .container-fluid,
...@@ -48,7 +55,8 @@ ...@@ -48,7 +55,8 @@
margin-bottom: 0; margin-bottom: 0;
.flash-notice, .flash-notice,
.flash-alert { .flash-alert,
.flash-success {
border-radius: 0; border-radius: 0;
} }
} }
......
...@@ -6,11 +6,19 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController ...@@ -6,11 +6,19 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
end end
def update def update
if @project.update(update_params) Projects::UpdateService.new(project, current_user, update_params).tap do |service|
flash[:notice] = "Pipelines settings for '#{@project.name}' were successfully updated." if service.execute
redirect_to project_settings_ci_cd_path(@project) flash[:notice] = "Pipelines settings for '#{@project.name}' were successfully updated."
else
render 'show' if service.run_auto_devops_pipeline?
CreatePipelineWorker.perform_async(project.id, current_user.id, project.default_branch, :web, ignore_skip_ci: true, save_on_errors: false)
flash[:success] = "A new Auto DevOps pipeline has been created, go to <a href=\"#{project_pipelines_path(@project)}\">Pipelines page</a> for details".html_safe
end
redirect_to project_settings_ci_cd_path(@project)
else
render 'show'
end
end end
end end
...@@ -21,6 +29,7 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController ...@@ -21,6 +29,7 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
:runners_token, :builds_enabled, :build_allow_git_fetch, :runners_token, :builds_enabled, :build_allow_git_fetch,
:build_timeout_in_minutes, :build_coverage_regex, :public_builds, :build_timeout_in_minutes, :build_coverage_regex, :public_builds,
:auto_cancel_pending_pipelines, :ci_config_path, :auto_cancel_pending_pipelines, :ci_config_path,
:run_auto_devops_pipeline_implicit, :run_auto_devops_pipeline_explicit,
auto_devops_attributes: [:id, :domain, :enabled] auto_devops_attributes: [:id, :domain, :enabled]
) )
end end
......
...@@ -8,6 +8,22 @@ module AutoDevopsHelper ...@@ -8,6 +8,22 @@ module AutoDevopsHelper
!project.ci_service !project.ci_service
end end
def show_run_auto_devops_pipeline_checkbox_for_instance_setting?(project)
return false if project.repository.gitlab_ci_yml
if project&.auto_devops&.enabled.present?
!project.auto_devops.enabled && current_application_settings.auto_devops_enabled?
else
current_application_settings.auto_devops_enabled?
end
end
def show_run_auto_devops_pipeline_checkbox_for_explicit_setting?(project)
return false if project.repository.gitlab_ci_yml
!project.auto_devops_enabled?
end
def auto_devops_warning_message(project) def auto_devops_warning_message(project)
missing_domain = !project.auto_devops&.has_domain? missing_domain = !project.auto_devops&.has_domain?
missing_service = !project.kubernetes_service&.active? missing_service = !project.kubernetes_service&.active?
......
...@@ -25,7 +25,7 @@ module Projects ...@@ -25,7 +25,7 @@ module Projects
return error("Could not set the default branch") unless project.change_head(params[:default_branch]) return error("Could not set the default branch") unless project.change_head(params[:default_branch])
end end
if project.update_attributes(params.except(:default_branch)) if project.update_attributes(update_params)
if project.previous_changes.include?('path') if project.previous_changes.include?('path')
project.rename_repo project.rename_repo
else else
...@@ -41,8 +41,16 @@ module Projects ...@@ -41,8 +41,16 @@ module Projects
end end
end end
def run_auto_devops_pipeline?
params.dig(:run_auto_devops_pipeline_explicit) == 'true' || params.dig(:run_auto_devops_pipeline_implicit) == 'true'
end
private private
def update_params
params.except(:default_branch, :run_auto_devops_pipeline_explicit, :run_auto_devops_pipeline_implicit)
end
def changing_storage_size? def changing_storage_size?
new_repository_storage = params[:repository_storage] new_repository_storage = params[:repository_storage]
......
.flash-container.flash-container-page .flash-container.flash-container-page
- if alert -# We currently only support `alert`, `notice`, `success`
.flash-alert - flash.each do |key, value|
%div{ class: "flash-#{key}" }
%div{ class: (container_class) } %div{ class: (container_class) }
%span= alert %span= value
- elsif notice
.flash-notice
%div{ class: (container_class) }
%span= notice
...@@ -13,29 +13,39 @@ ...@@ -13,29 +13,39 @@
%p.settings-message.text-center %p.settings-message.text-center
= message.html_safe = message.html_safe
= f.fields_for :auto_devops_attributes, @auto_devops do |form| = f.fields_for :auto_devops_attributes, @auto_devops do |form|
.radio .radio.js-auto-devops-enable-radio-wrapper
= form.label :enabled_true do = form.label :enabled_true do
= form.radio_button :enabled, 'true' = form.radio_button :enabled, 'true', class: 'js-auto-devops-enable-radio'
%strong Enable Auto DevOps %strong Enable Auto DevOps
%br %br
%span.descr %span.descr
The Auto DevOps pipeline configuration will be used when there is no <code>.gitlab-ci.yml</code> in the project. The Auto DevOps pipeline configuration will be used when there is no <code>.gitlab-ci.yml</code> in the project.
.radio - if show_run_auto_devops_pipeline_checkbox_for_explicit_setting?(@project)
.checkbox.hide.js-run-auto-devops-pipeline-checkbox-wrapper
= label_tag 'project[run_auto_devops_pipeline_explicit]' do
= check_box_tag 'project[run_auto_devops_pipeline_explicit]', true, false, class: 'js-run-auto-devops-pipeline-checkbox'
= s_('ProjectSettings|Immediately run a pipeline on the default branch')
.radio.js-auto-devops-enable-radio-wrapper
= form.label :enabled_false do = form.label :enabled_false do
= form.radio_button :enabled, 'false' = form.radio_button :enabled, 'false', class: 'js-auto-devops-enable-radio'
%strong Disable Auto DevOps %strong Disable Auto DevOps
%br %br
%span.descr %span.descr
An explicit <code>.gitlab-ci.yml</code> needs to be specified before you can begin using Continuous Integration and Delivery. An explicit <code>.gitlab-ci.yml</code> needs to be specified before you can begin using Continuous Integration and Delivery.
.radio .radio.js-auto-devops-enable-radio-wrapper
= form.label :enabled_nil do = form.label :enabled_ do
= form.radio_button :enabled, '' = form.radio_button :enabled, '', class: 'js-auto-devops-enable-radio'
%strong Instance default (#{current_application_settings.auto_devops_enabled? ? 'enabled' : 'disabled'}) %strong Instance default (#{current_application_settings.auto_devops_enabled? ? 'enabled' : 'disabled'})
%br %br
%span.descr %span.descr
Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific <code>.gitlab-ci.yml</code>. Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific <code>.gitlab-ci.yml</code>.
%br - if show_run_auto_devops_pipeline_checkbox_for_instance_setting?(@project)
.checkbox.hide.js-run-auto-devops-pipeline-checkbox-wrapper
= label_tag 'project[run_auto_devops_pipeline_implicit]' do
= check_box_tag 'project[run_auto_devops_pipeline_implicit]', true, false, class: 'js-run-auto-devops-pipeline-checkbox'
= s_('ProjectSettings|Immediately run a pipeline on the default branch')
%p %p
You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages. You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages.
= form.text_field :domain, class: 'form-control', placeholder: 'domain.com' = form.text_field :domain, class: 'form-control', placeholder: 'domain.com'
......
class CreatePipelineWorker
include Sidekiq::Worker
include PipelineQueue
enqueue_in group: :creation
def perform(project_id, user_id, ref, source, params = {})
project = Project.find(project_id)
user = User.find(user_id)
params = params.deep_symbolize_keys
Ci::CreatePipelineService
.new(project, user, ref: ref)
.execute(source, **params)
end
end
---
title: Add the option to automatically run a pipeline after updating AutoDevOps settings
merge_request: 15380
author:
type: changed
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
- [build, 2] - [build, 2]
- [pipeline, 2] - [pipeline, 2]
- [pipeline_processing, 5] - [pipeline_processing, 5]
- [pipeline_creation, 4]
- [pipeline_default, 3] - [pipeline_default, 3]
- [pipeline_cache, 3] - [pipeline_cache, 3]
- [pipeline_hooks, 2] - [pipeline_hooks, 2]
......
...@@ -121,7 +121,7 @@ Google Cloud. ...@@ -121,7 +121,7 @@ Google Cloud.
## Enabling Auto DevOps ## Enabling Auto DevOps
NOTE: **Note:** **Note:**
If you haven't done already, read the [prerequisites](#prerequisites) to make If you haven't done already, read the [prerequisites](#prerequisites) to make
full use of Auto DevOps. If this is your fist time, we recommend you follow the full use of Auto DevOps. If this is your fist time, we recommend you follow the
[quick start guide](#quick-start). [quick start guide](#quick-start).
...@@ -129,10 +129,14 @@ full use of Auto DevOps. If this is your fist time, we recommend you follow the ...@@ -129,10 +129,14 @@ full use of Auto DevOps. If this is your fist time, we recommend you follow the
1. Go to your project's **Settings > CI/CD > General pipelines settings** and 1. Go to your project's **Settings > CI/CD > General pipelines settings** and
find the Auto DevOps section find the Auto DevOps section
1. Select "Enable Auto DevOps" 1. Select "Enable Auto DevOps"
1. After selecting an option to enable Auto DevOps, a checkbox will appear below
so you can immediately run a pipeline on the default branch
1. Optionally, but recommended, add in the [base domain](#auto-devops-base-domain) 1. Optionally, but recommended, add in the [base domain](#auto-devops-base-domain)
that will be used by Kubernetes to deploy your application that will be used by Kubernetes to deploy your application
1. Hit **Save changes** for the changes to take effect 1. Hit **Save changes** for the changes to take effect
![Project AutoDevops settings section](img/auto_devops_settings.png)
Now that it's enabled, there are a few more steps depending on whether your project Now that it's enabled, there are a few more steps depending on whether your project
has a `.gitlab-ci.yml` or not: has a `.gitlab-ci.yml` or not:
......
...@@ -12,19 +12,22 @@ describe Projects::PipelinesSettingsController do ...@@ -12,19 +12,22 @@ describe Projects::PipelinesSettingsController do
end end
describe 'PATCH update' do describe 'PATCH update' do
before do subject do
patch :update, patch :update,
namespace_id: project.namespace.to_param, namespace_id: project.namespace.to_param,
project_id: project, project_id: project,
project: { project: { auto_devops_attributes: params,
auto_devops_attributes: params run_auto_devops_pipeline_implicit: 'false',
} run_auto_devops_pipeline_explicit: auto_devops_pipeline }
end end
context 'when updating the auto_devops settings' do context 'when updating the auto_devops settings' do
let(:params) { { enabled: '', domain: 'mepmep.md' } } let(:params) { { enabled: '', domain: 'mepmep.md' } }
let(:auto_devops_pipeline) { 'false' }
it 'redirects to the settings page' do it 'redirects to the settings page' do
subject
expect(response).to have_gitlab_http_status(302) expect(response).to have_gitlab_http_status(302)
expect(flash[:notice]).to eq("Pipelines settings for '#{project.name}' were successfully updated.") expect(flash[:notice]).to eq("Pipelines settings for '#{project.name}' were successfully updated.")
end end
...@@ -33,11 +36,32 @@ describe Projects::PipelinesSettingsController do ...@@ -33,11 +36,32 @@ describe Projects::PipelinesSettingsController do
let(:params) { { enabled: '' } } let(:params) { { enabled: '' } }
it 'allows enabled to be set to nil' do it 'allows enabled to be set to nil' do
subject
project_auto_devops.reload project_auto_devops.reload
expect(project_auto_devops.enabled).to be_nil expect(project_auto_devops.enabled).to be_nil
end end
end end
context 'when run_auto_devops_pipeline is true' do
let(:auto_devops_pipeline) { 'true' }
it 'queues a CreatePipelineWorker' do
expect(CreatePipelineWorker).to receive(:perform_async).with(project.id, user.id, project.default_branch, :web, any_args)
subject
end
end
context 'when run_auto_devops_pipeline is not true' do
let(:auto_devops_pipeline) { 'false' }
it 'does not queue a CreatePipelineWorker' do
expect(CreatePipelineWorker).not_to receive(:perform_async).with(project.id, user.id, :web, any_args)
subject
end
end
end end
end end
end end
...@@ -8,13 +8,14 @@ feature "Pipelines settings" do ...@@ -8,13 +8,14 @@ feature "Pipelines settings" do
background do background do
sign_in(user) sign_in(user)
project.team << [user, role] project.team << [user, role]
visit project_pipelines_settings_path(project)
end end
context 'for developer' do context 'for developer' do
given(:role) { :developer } given(:role) { :developer }
scenario 'to be disallowed to view' do scenario 'to be disallowed to view' do
visit project_settings_ci_cd_path(project)
expect(page.status_code).to eq(404) expect(page.status_code).to eq(404)
end end
end end
...@@ -23,6 +24,8 @@ feature "Pipelines settings" do ...@@ -23,6 +24,8 @@ feature "Pipelines settings" do
given(:role) { :master } given(:role) { :master }
scenario 'be allowed to change' do scenario 'be allowed to change' do
visit project_settings_ci_cd_path(project)
fill_in('Test coverage parsing', with: 'coverage_regex') fill_in('Test coverage parsing', with: 'coverage_regex')
click_on 'Save changes' click_on 'Save changes'
...@@ -32,6 +35,8 @@ feature "Pipelines settings" do ...@@ -32,6 +35,8 @@ feature "Pipelines settings" do
end end
scenario 'updates auto_cancel_pending_pipelines' do scenario 'updates auto_cancel_pending_pipelines' do
visit project_settings_ci_cd_path(project)
page.check('Auto-cancel redundant, pending pipelines') page.check('Auto-cancel redundant, pending pipelines')
click_on 'Save changes' click_on 'Save changes'
...@@ -42,14 +47,119 @@ feature "Pipelines settings" do ...@@ -42,14 +47,119 @@ feature "Pipelines settings" do
expect(checkbox).to be_checked expect(checkbox).to be_checked
end end
scenario 'update auto devops settings' do describe 'Auto DevOps' do
fill_in('project_auto_devops_attributes_domain', with: 'test.com') it 'update auto devops settings' do
page.choose('project_auto_devops_attributes_enabled_false') visit project_settings_ci_cd_path(project)
click_on 'Save changes'
expect(page.status_code).to eq(200) fill_in('project_auto_devops_attributes_domain', with: 'test.com')
expect(project.auto_devops).to be_present page.choose('project_auto_devops_attributes_enabled_false')
expect(project.auto_devops).not_to be_enabled click_on 'Save changes'
expect(page.status_code).to eq(200)
expect(project.auto_devops).to be_present
expect(project.auto_devops).not_to be_enabled
end
describe 'Immediately run pipeline checkbox option', :js do
context 'when auto devops is set to instance default (enabled)' do
before do
stub_application_setting(auto_devops_enabled: true)
project.create_auto_devops!(enabled: nil)
visit project_settings_ci_cd_path(project)
end
it 'does not show checkboxes on page-load' do
expect(page).to have_selector('.js-run-auto-devops-pipeline-checkbox-wrapper.hide', count: 1, visible: false)
end
it 'selecting explicit disabled hides all checkboxes' do
page.choose('project_auto_devops_attributes_enabled_false')
expect(page).to have_selector('.js-run-auto-devops-pipeline-checkbox-wrapper.hide', count: 1, visible: false)
end
it 'selecting explicit enabled hides all checkboxes because we are already enabled' do
page.choose('project_auto_devops_attributes_enabled_true')
expect(page).to have_selector('.js-run-auto-devops-pipeline-checkbox-wrapper.hide', count: 1, visible: false)
end
end
context 'when auto devops is set to instance default (disabled)' do
before do
stub_application_setting(auto_devops_enabled: false)
project.create_auto_devops!(enabled: nil)
visit project_settings_ci_cd_path(project)
end
it 'does not show checkboxes on page-load' do
expect(page).to have_selector('.js-run-auto-devops-pipeline-checkbox-wrapper.hide', count: 1, visible: false)
end
it 'selecting explicit disabled hides all checkboxes' do
page.choose('project_auto_devops_attributes_enabled_false')
expect(page).to have_selector('.js-run-auto-devops-pipeline-checkbox-wrapper.hide', count: 1, visible: false)
end
it 'selecting explicit enabled shows a checkbox' do
page.choose('project_auto_devops_attributes_enabled_true')
expect(page).to have_selector('.js-run-auto-devops-pipeline-checkbox-wrapper:not(.hide)', count: 1)
end
end
context 'when auto devops is set to explicit disabled' do
before do
stub_application_setting(auto_devops_enabled: true)
project.create_auto_devops!(enabled: false)
visit project_settings_ci_cd_path(project)
end
it 'does not show checkboxes on page-load' do
expect(page).to have_selector('.js-run-auto-devops-pipeline-checkbox-wrapper.hide', count: 2, visible: false)
end
it 'selecting explicit enabled shows a checkbox' do
page.choose('project_auto_devops_attributes_enabled_true')
expect(page).to have_selector('.js-run-auto-devops-pipeline-checkbox-wrapper:not(.hide)', count: 1)
end
it 'selecting instance default (enabled) shows a checkbox' do
page.choose('project_auto_devops_attributes_enabled_')
expect(page).to have_selector('.js-run-auto-devops-pipeline-checkbox-wrapper:not(.hide)', count: 1)
end
end
context 'when auto devops is set to explicit enabled' do
before do
stub_application_setting(auto_devops_enabled: false)
project.create_auto_devops!(enabled: true)
visit project_settings_ci_cd_path(project)
end
it 'does not have any checkboxes' do
expect(page).not_to have_selector('.js-run-auto-devops-pipeline-checkbox-wrapper', visible: false)
end
end
context 'when master contains a .gitlab-ci.yml file' do
let(:project) { create(:project, :repository) }
before do
project.repository.create_file(user, '.gitlab-ci.yml', "script: ['test']", message: 'test', branch_name: project.default_branch)
stub_application_setting(auto_devops_enabled: true)
project.create_auto_devops!(enabled: false)
visit project_settings_ci_cd_path(project)
end
it 'does not have any checkboxes' do
expect(page).not_to have_selector('.js-run-auto-devops-pipeline-checkbox-wrapper', visible: false)
end
end
end
end end
end end
end end
...@@ -82,4 +82,104 @@ describe AutoDevopsHelper do ...@@ -82,4 +82,104 @@ describe AutoDevopsHelper do
it { is_expected.to eq(false) } it { is_expected.to eq(false) }
end end
end end
describe '.show_run_auto_devops_pipeline_checkbox_for_instance_setting?' do
subject { helper.show_run_auto_devops_pipeline_checkbox_for_instance_setting?(project) }
context 'when master contains a .gitlab-ci.yml file' do
before do
allow(project.repository).to receive(:gitlab_ci_yml).and_return("script: ['test']")
end
it { is_expected.to eq(false) }
end
context 'when auto devops is explicitly enabled' do
before do
project.create_auto_devops!(enabled: true)
end
it { is_expected.to eq(false) }
end
context 'when auto devops is explicitly disabled' do
before do
project.create_auto_devops!(enabled: false)
end
context 'when auto devops is enabled system-wide' do
before do
stub_application_setting(auto_devops_enabled: true)
end
it { is_expected.to eq(true) }
end
context 'when auto devops is disabled system-wide' do
before do
stub_application_setting(auto_devops_enabled: false)
end
it { is_expected.to eq(false) }
end
end
context 'when auto devops is set to instance setting' do
before do
project.create_auto_devops!(enabled: nil)
end
it { is_expected.to eq(false) }
end
end
describe '.show_run_auto_devops_pipeline_checkbox_for_explicit_setting?' do
subject { helper.show_run_auto_devops_pipeline_checkbox_for_explicit_setting?(project) }
context 'when master contains a .gitlab-ci.yml file' do
before do
allow(project.repository).to receive(:gitlab_ci_yml).and_return("script: ['test']")
end
it { is_expected.to eq(false) }
end
context 'when auto devops is explicitly enabled' do
before do
project.create_auto_devops!(enabled: true)
end
it { is_expected.to eq(false) }
end
context 'when auto devops is explicitly disabled' do
before do
project.create_auto_devops!(enabled: false)
end
it { is_expected.to eq(true) }
end
context 'when auto devops is set to instance setting' do
before do
project.create_auto_devops!(enabled: nil)
end
context 'when auto devops is enabled system-wide' do
before do
stub_application_setting(auto_devops_enabled: true)
end
it { is_expected.to eq(false) }
end
context 'when auto devops is disabled system-wide' do
before do
stub_application_setting(auto_devops_enabled: false)
end
it { is_expected.to eq(true) }
end
end
end
end end
...@@ -278,7 +278,7 @@ describe('Flash', () => { ...@@ -278,7 +278,7 @@ describe('Flash', () => {
removeFlashClickListener(flashEl, false); removeFlashClickListener(flashEl, false);
flashEl.parentNode.click(); flashEl.click();
setTimeout(() => { setTimeout(() => {
expect(document.querySelector('.flash')).toBeNull(); expect(document.querySelector('.flash')).toBeNull();
......
...@@ -4,196 +4,220 @@ describe Projects::UpdateService, '#execute' do ...@@ -4,196 +4,220 @@ describe Projects::UpdateService, '#execute' do
include StubConfiguration include StubConfiguration
include ProjectForksHelper include ProjectForksHelper
let(:gitlab_shell) { Gitlab::Shell.new }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:admin) { create(:admin) }
let(:project) do let(:project) do
create(:project, creator: user, namespace: user.namespace) create(:project, creator: user, namespace: user.namespace)
end end
context 'when changing visibility level' do describe '#execute' do
context 'when visibility_level is INTERNAL' do let(:gitlab_shell) { Gitlab::Shell.new }
it 'updates the project to internal' do let(:admin) { create(:admin) }
result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL)
expect(result).to eq({ status: :success })
expect(project).to be_internal
end
end
context 'when visibility_level is PUBLIC' do
it 'updates the project to public' do
result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
expect(result).to eq({ status: :success })
expect(project).to be_public
end
end
context 'when visibility levels are restricted to PUBLIC only' do
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
end
context 'when changing visibility level' do
context 'when visibility_level is INTERNAL' do context 'when visibility_level is INTERNAL' do
it 'updates the project to internal' do it 'updates the project to internal' do
result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL) result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL)
expect(result).to eq({ status: :success }) expect(result).to eq({ status: :success })
expect(project).to be_internal expect(project).to be_internal
end end
end end
context 'when visibility_level is PUBLIC' do context 'when visibility_level is PUBLIC' do
it 'does not update the project to public' do it 'updates the project to public' do
result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC) result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
expect(result).to eq({ status: :success })
expect(project).to be_public
end
end
expect(result).to eq({ status: :error, message: 'New visibility level not allowed!' }) context 'when visibility levels are restricted to PUBLIC only' do
expect(project).to be_private before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
end end
context 'when updated by an admin' do context 'when visibility_level is INTERNAL' do
it 'updates the project to public' do it 'updates the project to internal' do
result = update_project(project, admin, visibility_level: Gitlab::VisibilityLevel::PUBLIC) result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL)
expect(result).to eq({ status: :success }) expect(result).to eq({ status: :success })
expect(project).to be_public expect(project).to be_internal
end end
end end
end
end
context 'When project visibility is higher than parent group' do context 'when visibility_level is PUBLIC' do
let(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) } it 'does not update the project to public' do
result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
before do expect(result).to eq({ status: :error, message: 'New visibility level not allowed!' })
project.update(namespace: group, visibility_level: group.visibility_level) expect(project).to be_private
end
context 'when updated by an admin' do
it 'updates the project to public' do
result = update_project(project, admin, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
expect(result).to eq({ status: :success })
expect(project).to be_public
end
end
end
end end
it 'does not update project visibility level' do context 'When project visibility is higher than parent group' do
result = update_project(project, admin, visibility_level: Gitlab::VisibilityLevel::PUBLIC) let(:group) { create(:group, visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
expect(result).to eq({ status: :error, message: 'Visibility level public is not allowed in a internal group.' }) before do
expect(project.reload).to be_internal project.update(namespace: group, visibility_level: group.visibility_level)
end
it 'does not update project visibility level' do
result = update_project(project, admin, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
expect(result).to eq({ status: :error, message: 'Visibility level public is not allowed in a internal group.' })
expect(project.reload).to be_internal
end
end end
end end
end
describe 'when updating project that has forks' do describe 'when updating project that has forks' do
let(:project) { create(:project, :internal) } let(:project) { create(:project, :internal) }
let(:forked_project) { fork_project(project) } let(:forked_project) { fork_project(project) }
it 'updates forks visibility level when parent set to more restrictive' do it 'updates forks visibility level when parent set to more restrictive' do
opts = { visibility_level: Gitlab::VisibilityLevel::PRIVATE } opts = { visibility_level: Gitlab::VisibilityLevel::PRIVATE }
expect(project).to be_internal expect(project).to be_internal
expect(forked_project).to be_internal expect(forked_project).to be_internal
expect(update_project(project, admin, opts)).to eq({ status: :success }) expect(update_project(project, admin, opts)).to eq({ status: :success })
expect(project).to be_private expect(project).to be_private
expect(forked_project.reload).to be_private expect(forked_project.reload).to be_private
end end
it 'does not update forks visibility level when parent set to less restrictive' do it 'does not update forks visibility level when parent set to less restrictive' do
opts = { visibility_level: Gitlab::VisibilityLevel::PUBLIC } opts = { visibility_level: Gitlab::VisibilityLevel::PUBLIC }
expect(project).to be_internal expect(project).to be_internal
expect(forked_project).to be_internal expect(forked_project).to be_internal
expect(update_project(project, admin, opts)).to eq({ status: :success }) expect(update_project(project, admin, opts)).to eq({ status: :success })
expect(project).to be_public expect(project).to be_public
expect(forked_project.reload).to be_internal expect(forked_project.reload).to be_internal
end
end end
end
context 'when updating a default branch' do context 'when updating a default branch' do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
it 'changes a default branch' do it 'changes a default branch' do
update_project(project, admin, default_branch: 'feature') update_project(project, admin, default_branch: 'feature')
expect(Project.find(project.id).default_branch).to eq 'feature' expect(Project.find(project.id).default_branch).to eq 'feature'
end end
it 'does not change a default branch' do it 'does not change a default branch' do
# The branch 'unexisted-branch' does not exist. # The branch 'unexisted-branch' does not exist.
update_project(project, admin, default_branch: 'unexisted-branch') update_project(project, admin, default_branch: 'unexisted-branch')
expect(Project.find(project.id).default_branch).to eq 'master' expect(Project.find(project.id).default_branch).to eq 'master'
end
end end
end
context 'when updating a project that contains container images' do context 'when updating a project that contains container images' do
before do before do
stub_container_registry_config(enabled: true) stub_container_registry_config(enabled: true)
stub_container_registry_tags(repository: /image/, tags: %w[rc1]) stub_container_registry_tags(repository: /image/, tags: %w[rc1])
create(:container_repository, project: project, name: :image) create(:container_repository, project: project, name: :image)
end end
it 'does not allow to rename the project' do it 'does not allow to rename the project' do
result = update_project(project, admin, path: 'renamed') result = update_project(project, admin, path: 'renamed')
expect(result).to include(status: :error) expect(result).to include(status: :error)
expect(result[:message]).to match(/contains container registry tags/) expect(result[:message]).to match(/contains container registry tags/)
end end
it 'allows to update other settings' do it 'allows to update other settings' do
result = update_project(project, admin, public_builds: true) result = update_project(project, admin, public_builds: true)
expect(result[:status]).to eq :success expect(result[:status]).to eq :success
expect(project.reload.public_builds).to be true expect(project.reload.public_builds).to be true
end
end end
end
context 'when renaming a project' do context 'when renaming a project' do
let(:repository_storage) { 'default' } let(:repository_storage) { 'default' }
let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage]['path'] } let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage]['path'] }
context 'with legacy storage' do context 'with legacy storage' do
before do before do
gitlab_shell.add_repository(repository_storage, "#{user.namespace.full_path}/existing") gitlab_shell.add_repository(repository_storage, "#{user.namespace.full_path}/existing")
end end
after do
gitlab_shell.remove_repository(repository_storage_path, "#{user.namespace.full_path}/existing")
end
after do it 'does not allow renaming when new path matches existing repository on disk' do
gitlab_shell.remove_repository(repository_storage_path, "#{user.namespace.full_path}/existing") result = update_project(project, admin, path: 'existing')
expect(result).to include(status: :error)
expect(result[:message]).to match('There is already a repository with that name on disk')
expect(project).not_to be_valid
expect(project.errors.messages).to have_key(:base)
expect(project.errors.messages[:base]).to include('There is already a repository with that name on disk')
end
end end
it 'does not allow renaming when new path matches existing repository on disk' do context 'with hashed storage' do
result = update_project(project, admin, path: 'existing') let(:project) { create(:project, :repository, creator: user, namespace: user.namespace) }
expect(result).to include(status: :error) before do
expect(result[:message]).to match('There is already a repository with that name on disk') stub_application_setting(hashed_storage_enabled: true)
expect(project).not_to be_valid end
expect(project.errors.messages).to have_key(:base)
expect(project.errors.messages[:base]).to include('There is already a repository with that name on disk') it 'does not check if new path matches existing repository on disk' do
expect(project).not_to receive(:repository_with_same_path_already_exists?)
result = update_project(project, admin, path: 'existing')
expect(result).to include(status: :success)
end
end end
end end
context 'with hashed storage' do context 'when passing invalid parameters' do
let(:project) { create(:project, :repository, creator: user, namespace: user.namespace) } it 'returns an error result when record cannot be updated' do
result = update_project(project, admin, { name: 'foo&bar' })
before do expect(result).to eq({
stub_application_setting(hashed_storage_enabled: true) status: :error,
message: "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'."
})
end end
end
end
it 'does not check if new path matches existing repository on disk' do describe '#run_auto_devops_pipeline?' do
expect(project).not_to receive(:repository_with_same_path_already_exists?) subject { described_class.new(project, user, params).run_auto_devops_pipeline? }
result = update_project(project, admin, path: 'existing') context 'when neither pipeline setting is true' do
let(:params) { {} }
expect(result).to include(status: :success) it { is_expected.to eq(false) }
end
end end
end
context 'when passing invalid parameters' do context 'when run_auto_devops_pipeline_explicit is true' do
it 'returns an error result when record cannot be updated' do let(:params) { { run_auto_devops_pipeline_explicit: 'true' } }
result = update_project(project, admin, { name: 'foo&bar' })
expect(result).to eq({ it { is_expected.to eq(true) }
status: :error, end
message: "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'."
}) context 'when run_auto_devops_pipeline_implicit is true' do
let(:params) { { run_auto_devops_pipeline_implicit: 'true' } }
it { is_expected.to eq(true) }
end end
end end
...@@ -258,6 +282,8 @@ describe Projects::UpdateService, '#execute' do ...@@ -258,6 +282,8 @@ describe Projects::UpdateService, '#execute' do
end end
it 'returns an error result when record cannot be updated' do it 'returns an error result when record cannot be updated' do
admin = create(:admin)
result = update_project(project, admin, { name: 'foo&bar' }) result = update_project(project, admin, { name: 'foo&bar' })
expect(result).to eq({ status: :error, message: "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'." }) expect(result).to eq({ status: :error, message: "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'." })
......
require 'spec_helper'
describe CreatePipelineWorker do
describe '#perform' do
let(:worker) { described_class.new }
context 'when a project not found' do
it 'does not call the Service' do
expect(Ci::CreatePipelineService).not_to receive(:new)
expect { worker.perform(99, create(:user).id, 'master', :web) }.to raise_error(ActiveRecord::RecordNotFound)
end
end
context 'when a user not found' do
let(:project) { create(:project) }
it 'does not call the Service' do
expect(Ci::CreatePipelineService).not_to receive(:new)
expect { worker.perform(project.id, 99, project.default_branch, :web) }.to raise_error(ActiveRecord::RecordNotFound)
end
end
context 'when everything is ok' do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:create_pipeline_service) { instance_double(Ci::CreatePipelineService) }
it 'calls the Service' do
expect(Ci::CreatePipelineService).to receive(:new).with(project, user, ref: project.default_branch).and_return(create_pipeline_service)
expect(create_pipeline_service).to receive(:execute).with(:web, any_args)
worker.perform(project.id, user.id, project.default_branch, :web)
end
end
end
end
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