Skip to content
Snippets Groups Projects
Commit 6fd7149e authored by Kamil Trzcińśki's avatar Kamil Trzcińśki
Browse files

Merge branch '52447-auto-devops-at-group-level' into 'master'

Enable/disable Auto DevOps at the Group level

Closes #52447

See merge request gitlab-org/gitlab-ce!25533
parents db37b5a4 7e9348f3
No related branches found
No related tags found
No related merge requests found
Showing
with 439 additions and 13 deletions
Loading
Loading
@@ -17,6 +17,16 @@ module Groups
redirect_to group_settings_ci_cd_path
end
 
def update_auto_devops
if auto_devops_service.execute
flash[:notice] = s_('GroupSettings|Auto DevOps pipeline was updated for the group')
else
flash[:alert] = s_("GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}." % { error_messages: group.errors.full_messages })
end
redirect_to group_settings_ci_cd_path
end
private
 
def define_ci_variables
Loading
Loading
@@ -29,6 +39,14 @@ module Groups
def authorize_admin_group!
return render_404 unless can?(current_user, :admin_group, group)
end
def auto_devops_params
params.require(:group).permit(:auto_devops_enabled)
end
def auto_devops_service
Groups::AutoDevopsService.new(group, current_user, auto_devops_params)
end
end
end
end
Loading
Loading
@@ -9,4 +9,17 @@ module AutoDevopsHelper
!project.repository.gitlab_ci_yml &&
!project.ci_service
end
def badge_for_auto_devops_scope(auto_devops_receiver)
return unless auto_devops_receiver.auto_devops_enabled?
case auto_devops_receiver.first_auto_devops_config[:scope]
when :project
nil
when :group
s_('CICD|group enabled')
when :instance
s_('CICD|instance enabled')
end
end
end
Loading
Loading
@@ -11,6 +11,7 @@ class Namespace < ApplicationRecord
include IgnorableColumn
include FeatureGate
include FromUnion
include Gitlab::Utils::StrongMemoize
 
ignore_column :deleted_at
 
Loading
Loading
@@ -267,6 +268,22 @@ class Namespace < ApplicationRecord
owner.refresh_authorized_projects
end
 
def auto_devops_enabled?
first_auto_devops_config[:status]
end
def first_auto_devops_config
return { scope: :group, status: auto_devops_enabled } unless auto_devops_enabled.nil?
strong_memoize(:first_auto_devops_config) do
if has_parent?
parent.first_auto_devops_config
else
{ scope: :instance, status: Gitlab::CurrentSettings.auto_devops_enabled? }
end
end
end
private
 
def path_or_parent_changed?
Loading
Loading
Loading
Loading
@@ -631,12 +631,21 @@ class Project < ActiveRecord::Base
end
 
def has_auto_devops_implicitly_enabled?
auto_devops&.enabled.nil? &&
(Gitlab::CurrentSettings.auto_devops_enabled? || Feature.enabled?(:force_autodevops_on_by_default, self))
auto_devops_config = first_auto_devops_config
auto_devops_config[:scope] != :project && auto_devops_config[:status]
end
 
def has_auto_devops_implicitly_disabled?
auto_devops&.enabled.nil? && !(Gitlab::CurrentSettings.auto_devops_enabled? || Feature.enabled?(:force_autodevops_on_by_default, self))
auto_devops_config = first_auto_devops_config
auto_devops_config[:scope] != :project && !auto_devops_config[:status]
end
def first_auto_devops_config
return namespace.first_auto_devops_config if auto_devops&.enabled.nil?
{ scope: :project, status: auto_devops&.enabled || Feature.enabled?(:force_autodevops_on_by_default, self) }
end
 
def daily_statistics_enabled?
Loading
Loading
# frozen_string_literal: true
module Groups
class AutoDevopsService < Groups::BaseService
def execute
raise Gitlab::Access::AccessDeniedError unless can?(current_user, :admin_group, group)
group.update(auto_devops_enabled: auto_devops_enabled)
end
private
def auto_devops_enabled
params[:auto_devops_enabled]
end
end
end
= form_for group, url: update_auto_devops_group_settings_ci_cd_path(group), method: :patch do |f|
= form_errors(group)
%fieldset
.form-group
.card.auto-devops-card
.card-body
.form-check
= f.check_box :auto_devops_enabled, class: 'form-check-input', checked: group.auto_devops_enabled?
= f.label :auto_devops_enabled, class: 'form-check-label' do
%strong= s_('GroupSettings|Default to Auto DevOps pipeline for all projects within this group')
%span.badge.badge-info#auto-devops-badge= badge_for_auto_devops_scope(group)
.form-text.text-muted
= s_('GroupSettings|The Auto DevOps pipeline will run if no alternative CI configuration file is found.')
= link_to _('More information'), help_page_path('topics/autodevops/index.md'), target: '_blank'
= f.submit _('Save changes'), class: 'btn btn-success prepend-top-15'
Loading
Loading
@@ -19,3 +19,17 @@
= _('Register and see your runners for this group.')
.settings-content
= render 'groups/runners/index'
%section.settings#auto-devops-settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4
= _('Auto DevOps')
%button.btn.btn-default.js-settings-toggle{ type: "button" }
= expanded ? _('Collapse') : _('Expand')
%p
- auto_devops_url = help_page_path('topics/autodevops/index')
- auto_devops_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: auto_devops_url }
= s_('GroupSettings|Auto DevOps will automatically build, test and deploy your application based on a predefined Continuous Integration and Delivery configuration. %{auto_devops_start}Learn more about Auto DevOps%{auto_devops_end}').html_safe % { auto_devops_start: auto_devops_start, auto_devops_end: '</a>'.html_safe }
.settings-content
= render 'groups/settings/ci_cd/auto_devops_form', group: @group
Loading
Loading
@@ -8,15 +8,15 @@
.card.auto-devops-card
.card-body
.form-check
= form.check_box :enabled, class: 'form-check-input js-toggle-extra-settings', checked: @project.auto_devops_enabled?
= form.check_box :enabled, class: 'form-check-input js-toggle-extra-settings', checked: auto_devops_enabled
= form.label :enabled, class: 'form-check-label' do
%strong= s_('CICD|Default to Auto DevOps pipeline')
- if @project.has_auto_devops_implicitly_enabled?
%span.badge.badge-info.js-instance-default-badge= s_('CICD|instance enabled')
- if auto_devops_enabled
%span.badge.badge-info.js-instance-default-badge= badge_for_auto_devops_scope(@project)
.form-text.text-muted
= s_('CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found.')
= link_to _('More information'), help_page_path('topics/autodevops/index.md'), target: '_blank'
.card-footer.js-extra-settings{ class: @project.auto_devops_enabled? || 'hidden' }
.card-footer.js-extra-settings{ class: auto_devops_enabled || 'hidden' }
%p.settings-message.text-center
- kubernetes_cluster_link = help_page_path('user/project/clusters/index')
- kubernetes_cluster_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: kubernetes_cluster_link }
Loading
Loading
Loading
Loading
@@ -26,7 +26,7 @@
= s_('CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration.')
= link_to s_('CICD|Learn more about Auto DevOps'), help_page_path('topics/autodevops/index.md')
.settings-content
= render 'autodevops_form'
= render 'autodevops_form', auto_devops_enabled: @project.auto_devops_enabled?
 
= render_if_exists 'projects/settings/ci_cd/protected_environments', expanded: expanded
 
Loading
Loading
---
title: Enable/disable Auto DevOps at the Group level
merge_request: 25533
author:
type: added
Loading
Loading
@@ -31,6 +31,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
namespace :settings do
resource :ci_cd, only: [:show], controller: 'ci_cd' do
put :reset_registration_token
patch :update_auto_devops
end
end
 
Loading
Loading
# frozen_string_literal: true
class AddAutoDevOpsEnabledToNamespaces < ActiveRecord::Migration[5.0]
DOWNTIME = false
def change
add_column :namespaces, :auto_devops_enabled, :boolean
end
end
Loading
Loading
@@ -1377,6 +1377,7 @@ ActiveRecord::Schema.define(version: 20190301182457) do
t.integer "cached_markdown_version"
t.string "runners_token"
t.string "runners_token_encrypted"
t.boolean "auto_devops_enabled"
t.index ["created_at"], name: "index_namespaces_on_created_at", using: :btree
t.index ["name", "parent_id"], name: "index_namespaces_on_name_and_parent_id", unique: true, using: :btree
t.index ["name"], name: "index_namespaces_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
Loading
Loading
Loading
Loading
@@ -1332,6 +1332,9 @@ msgstr ""
msgid "CICD|You must add a %{kubernetes_cluster_start}Kubernetes cluster integration%{kubernetes_cluster_end} to this project with a domain in order for your deployment strategy to work correctly."
msgstr ""
 
msgid "CICD|group enabled"
msgstr ""
msgid "CICD|instance enabled"
msgstr ""
 
Loading
Loading
@@ -3828,18 +3831,33 @@ msgstr ""
msgid "Group: %{group_name}"
msgstr ""
 
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
msgid "GroupSettings|Auto DevOps will automatically build, test and deploy your application based on a predefined Continuous Integration and Delivery configuration. %{auto_devops_start}Learn more about Auto DevOps%{auto_devops_end}"
msgstr ""
msgid "GroupSettings|Badges"
msgstr ""
 
msgid "GroupSettings|Customize your group badges."
msgstr ""
 
msgid "GroupSettings|Default to Auto DevOps pipeline for all projects within this group"
msgstr ""
msgid "GroupSettings|Learn more about badges."
msgstr ""
 
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
 
msgid "GroupSettings|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
msgstr ""
msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
msgstr ""
 
Loading
Loading
Loading
Loading
@@ -66,4 +66,77 @@ describe Groups::Settings::CiCdController do
end
end
end
describe 'PATCH #update_auto_devops' do
let(:auto_devops_param) { '1' }
subject do
patch :update_auto_devops, params: {
group_id: group,
group: { auto_devops_enabled: auto_devops_param }
}
end
context 'when user does not have enough permission' do
before do
group.add_maintainer(user)
end
it { is_expected.to have_gitlab_http_status(404) }
end
context 'when user has enough privileges' do
before do
group.add_owner(user)
end
it { is_expected.to redirect_to(group_settings_ci_cd_path) }
context 'when service execution went wrong' do
before do
allow_any_instance_of(Groups::AutoDevopsService).to receive(:execute).and_return(false)
allow_any_instance_of(Group).to receive_message_chain(:errors, :full_messages)
.and_return(['Error 1'])
subject
end
it 'returns a flash alert' do
expect(response).to set_flash[:alert]
.to eq("There was a problem updating Auto DevOps pipeline: [\"Error 1\"].")
end
end
context 'when service execution was successful' do
it 'returns a flash notice' do
subject
expect(response).to set_flash[:notice]
.to eq('Auto DevOps pipeline was updated for the group')
end
end
context 'when changing auto devops value' do
before do
subject
group.reload
end
context 'when explicitly enabling auto devops' do
it 'should update group attribute' do
expect(group.auto_devops_enabled).to eq(true)
end
end
context 'when explicitly disabling auto devops' do
let(:auto_devops_param) { '0' }
it 'should update group attribute' do
expect(group.auto_devops_enabled).to eq(false)
end
end
end
end
end
end
Loading
Loading
@@ -36,5 +36,13 @@ FactoryBot.define do
trait :nested do
parent factory: :group
end
trait :auto_devops_enabled do
auto_devops_enabled true
end
trait :auto_devops_disabled do
auto_devops_enabled false
end
end
end
Loading
Loading
@@ -271,6 +271,10 @@ FactoryBot.define do
trait :auto_devops do
association :auto_devops, factory: :project_auto_devops
end
trait :auto_devops_disabled do
association :auto_devops, factory: [:project_auto_devops, :disabled]
end
end
 
# Project with empty repository
Loading
Loading
Loading
Loading
@@ -5,8 +5,8 @@ require 'spec_helper'
describe 'Group CI/CD settings' do
include WaitForRequests
 
let(:user) {create(:user)}
let(:group) {create(:group)}
let(:user) { create(:user) }
let(:group) { create(:group) }
 
before do
group.add_owner(user)
Loading
Loading
@@ -36,4 +36,45 @@ describe 'Group CI/CD settings' do
end
end
end
describe 'Auto DevOps form' do
before do
stub_application_setting(auto_devops_enabled: true)
end
context 'as owner first visiting group settings' do
it 'should see instance enabled badge' do
visit group_settings_ci_cd_path(group)
page.within '#auto-devops-settings' do
expect(page).to have_content('instance enabled')
end
end
end
context 'when Auto DevOps group has been enabled' do
it 'should see group enabled badge' do
group.update!(auto_devops_enabled: true)
visit group_settings_ci_cd_path(group)
page.within '#auto-devops-settings' do
expect(page).to have_content('group enabled')
end
end
end
context 'when Auto DevOps group has been disabled' do
it 'should not see a badge' do
group.update!(auto_devops_enabled: false)
visit group_settings_ci_cd_path(group)
page.within '#auto-devops-settings' do
expect(page).not_to have_content('instance enabled')
expect(page).not_to have_content('group enabled')
end
end
end
end
end
Loading
Loading
@@ -110,6 +110,37 @@ describe "Projects > Settings > Pipelines settings" do
expect(page).not_to have_content('instance enabled')
end
end
context 'when auto devops is turned on group level' do
before do
project.update!(namespace: create(:group, :auto_devops_enabled))
end
it 'renders group enabled badge' do
visit project_settings_ci_cd_path(project)
page.within '#autodevops-settings' do
expect(page).to have_content('group enabled')
expect(find_field('project_auto_devops_attributes_enabled')).to be_checked
end
end
end
context 'when auto devops is turned on group parent level', :nested_groups do
before do
group = create(:group, parent: create(:group, :auto_devops_enabled))
project.update!(namespace: group)
end
it 'renders group enabled badge' do
visit project_settings_ci_cd_path(project)
page.within '#autodevops-settings' do
expect(page).to have_content('group enabled')
expect(find_field('project_auto_devops_attributes_enabled')).to be_checked
end
end
end
end
end
 
Loading
Loading
Loading
Loading
@@ -29,11 +29,11 @@ describe AutoDevopsHelper do
end
 
context 'when the banner is disabled by feature flag' do
it 'allows the feature flag to disable' do
before do
Feature.get(:auto_devops_banner_disabled).enable
expect(subject).to be(false)
end
it { is_expected.to be_falsy }
end
 
context 'when dismissed' do
Loading
Loading
@@ -90,4 +90,136 @@ describe AutoDevopsHelper do
it { is_expected.to eq(false) }
end
end
describe '#badge_for_auto_devops_scope' do
subject { helper.badge_for_auto_devops_scope(receiver) }
context 'when receiver is a group' do
context 'when explicitly enabled' do
let(:receiver) { create(:group, :auto_devops_enabled) }
it { is_expected.to eq('group enabled') }
end
context 'when explicitly disabled' do
let(:receiver) { create(:group, :auto_devops_disabled) }
it { is_expected.to be_nil }
end
context 'when auto devops is implicitly enabled' do
let(:receiver) { create(:group) }
context 'by instance' do
before do
stub_application_setting(auto_devops_enabled: true)
end
it { is_expected.to eq('instance enabled') }
end
context 'with groups', :nested_groups do
before do
receiver.update(parent: parent)
end
context 'when auto devops is enabled on parent' do
let(:parent) { create(:group, :auto_devops_enabled) }
it { is_expected.to eq('group enabled') }
end
context 'when auto devops is enabled on parent group' do
let(:root_parent) { create(:group, :auto_devops_enabled) }
let(:parent) { create(:group, parent: root_parent) }
it { is_expected.to eq('group enabled') }
end
context 'when auto devops disabled set on parent group' do
let(:root_parent) { create(:group, :auto_devops_disabled) }
let(:parent) { create(:group, parent: root_parent) }
it { is_expected.to be_nil }
end
end
end
end
context 'when receiver is a project' do
context 'when auto devops is enabled at project level' do
let(:receiver) { create(:project, :auto_devops) }
it { is_expected.to be_nil }
end
context 'when auto devops is disabled at project level' do
let(:receiver) { create(:project, :auto_devops_disabled) }
it { is_expected.to be_nil }
end
context 'when auto devops is implicitly enabled' do
let(:receiver) { create(:project) }
context 'by instance' do
before do
stub_application_setting(auto_devops_enabled: true)
end
it { is_expected.to eq('instance enabled') }
end
context 'with groups', :nested_groups do
let(:receiver) { create(:project, :repository, namespace: group) }
before do
stub_application_setting(auto_devops_enabled: false)
end
context 'when auto devops is enabled on group level' do
let(:group) { create(:group, :auto_devops_enabled) }
it { is_expected.to eq('group enabled') }
end
context 'when auto devops is enabled on root group' do
let(:root_parent) { create(:group, :auto_devops_enabled) }
let(:group) { create(:group, parent: root_parent) }
it { is_expected.to eq('group enabled') }
end
end
end
context 'when auto devops is implicitly disabled' do
let(:receiver) { create(:project) }
context 'by instance' do
before do
stub_application_setting(auto_devops_enabled: false)
end
it { is_expected.to be_nil }
end
context 'with groups', :nested_groups do
let(:receiver) { create(:project, :repository, namespace: group) }
context 'when auto devops is disabled on group level' do
let(:group) { create(:group, :auto_devops_disabled) }
it { is_expected.to be_nil }
end
context 'when root group is enabled and parent disabled' do
let(:root_parent) { create(:group, :auto_devops_enabled) }
let(:group) { create(:group, :auto_devops_disabled, parent: root_parent) }
it { is_expected.to be_nil }
end
end
end
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment