Commit 8db35493 authored by Mathieu Parent's avatar Mathieu Parent

Allow to define a default CI configuration path for new projects

Fixes: #20598
parent 69ed0d41
...@@ -176,6 +176,7 @@ module ApplicationSettingsHelper ...@@ -176,6 +176,7 @@ module ApplicationSettingsHelper
:container_registry_token_expire_delay, :container_registry_token_expire_delay,
:default_artifacts_expire_in, :default_artifacts_expire_in,
:default_branch_protection, :default_branch_protection,
:default_ci_config_path,
:default_group_visibility, :default_group_visibility,
:default_project_creation, :default_project_creation,
:default_project_visibility, :default_project_visibility,
......
...@@ -297,6 +297,12 @@ class ApplicationSetting < ApplicationRecord ...@@ -297,6 +297,12 @@ class ApplicationSetting < ApplicationRecord
pass: :external_auth_client_key_pass, pass: :external_auth_client_key_pass,
if: -> (setting) { setting.external_auth_client_cert.present? } if: -> (setting) { setting.external_auth_client_cert.present? }
validates :default_ci_config_path,
format: { without: %r{(\.{2}|\A/)},
message: N_('cannot include leading slash or directory traversal.') },
length: { maximum: 255 },
allow_blank: true
attr_encrypted :asset_proxy_secret_key, attr_encrypted :asset_proxy_secret_key,
mode: :per_attribute_iv, mode: :per_attribute_iv,
key: Settings.attr_encrypted_db_key_base_truncated, key: Settings.attr_encrypted_db_key_base_truncated,
......
...@@ -42,6 +42,7 @@ module ApplicationSettingImplementation ...@@ -42,6 +42,7 @@ module ApplicationSettingImplementation
container_registry_token_expire_delay: 5, container_registry_token_expire_delay: 5,
default_artifacts_expire_in: '30 days', default_artifacts_expire_in: '30 days',
default_branch_protection: Settings.gitlab['default_branch_protection'], default_branch_protection: Settings.gitlab['default_branch_protection'],
default_ci_config_path: nil,
default_group_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_group_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_project_creation: Settings.gitlab['default_project_creation'], default_project_creation: Settings.gitlab['default_project_creation'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
......
...@@ -92,6 +92,7 @@ class Project < ApplicationRecord ...@@ -92,6 +92,7 @@ class Project < ApplicationRecord
default_value_for :snippets_enabled, gitlab_config_features.snippets default_value_for :snippets_enabled, gitlab_config_features.snippets
default_value_for :only_allow_merge_if_all_discussions_are_resolved, false default_value_for :only_allow_merge_if_all_discussions_are_resolved, false
default_value_for :remove_source_branch_after_merge, true default_value_for :remove_source_branch_after_merge, true
default_value_for(:ci_config_path) { Gitlab::CurrentSettings.default_ci_config_path }
add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:projects_tokens_optional_encryption, default_enabled: true) ? :optional : :required } add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:projects_tokens_optional_encryption, default_enabled: true) ? :optional : :required }
......
...@@ -53,5 +53,11 @@ ...@@ -53,5 +53,11 @@
= s_('AdminSettings|Environment variables are protected by default') = s_('AdminSettings|Environment variables are protected by default')
.form-text.text-muted .form-text.text-muted
= s_('AdminSettings|When creating a new environment variable it will be protected by default.') = s_('AdminSettings|When creating a new environment variable it will be protected by default.')
.form-group
= f.label :ci_config_path, _('Default CI configuration path'), class: 'label-bold'
= f.text_field :default_ci_config_path, class: 'form-control', placeholder: '.gitlab-ci.yml'
%p.form-text.text-muted
= _("The default CI configuration path for new projects.").html_safe
= link_to icon('question-circle'), help_page_path('user/project/pipelines/settings', anchor: 'custom-ci-config-path'), target: '_blank'
= f.submit _('Save changes'), class: "btn btn-success" = f.submit _('Save changes'), class: "btn btn-success"
---
title: Allow to define a default CI configuration path for new projects
merge_request: 18073
author: Mathieu Parent
type: added
# frozen_string_literal: true
class DefaultCiConfigPath < ActiveRecord::Migration[5.2]
DOWNTIME = false
def up
add_column :application_settings, :default_ci_config_path, :string, limit: 255
end
def down
remove_column :application_settings, :default_ci_config_path
end
end
...@@ -351,6 +351,7 @@ ActiveRecord::Schema.define(version: 2019_11_12_115317) do ...@@ -351,6 +351,7 @@ ActiveRecord::Schema.define(version: 2019_11_12_115317) do
t.text "encrypted_eks_secret_access_key" t.text "encrypted_eks_secret_access_key"
t.string "snowplow_app_id" t.string "snowplow_app_id"
t.datetime_with_timezone "productivity_analytics_start_date" t.datetime_with_timezone "productivity_analytics_start_date"
t.string "default_ci_config_path", limit: 255
t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id" t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id"
t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id" t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id"
t.index ["instance_administration_project_id"], name: "index_applicationsettings_on_instance_administration_project_id" t.index ["instance_administration_project_id"], name: "index_applicationsettings_on_instance_administration_project_id"
......
...@@ -40,6 +40,7 @@ Example response: ...@@ -40,6 +40,7 @@ Example response:
"domain_blacklist_enabled" : false, "domain_blacklist_enabled" : false,
"domain_blacklist" : [], "domain_blacklist" : [],
"created_at" : "2016-01-04T15:44:55.176Z", "created_at" : "2016-01-04T15:44:55.176Z",
"default_ci_config_path" : null,
"default_project_visibility" : "private", "default_project_visibility" : "private",
"default_group_visibility" : "private", "default_group_visibility" : "private",
"gravatar_enabled" : true, "gravatar_enabled" : true,
...@@ -113,6 +114,7 @@ Example response: ...@@ -113,6 +114,7 @@ Example response:
"restricted_visibility_levels": [], "restricted_visibility_levels": [],
"max_attachment_size": 10, "max_attachment_size": 10,
"session_expire_delay": 10080, "session_expire_delay": 10080,
"default_ci_config_path" : null,
"default_project_visibility": "internal", "default_project_visibility": "internal",
"default_snippet_visibility": "private", "default_snippet_visibility": "private",
"default_group_visibility": "private", "default_group_visibility": "private",
...@@ -198,6 +200,7 @@ are listed in the descriptions of the relevant settings. ...@@ -198,6 +200,7 @@ are listed in the descriptions of the relevant settings.
| `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes. | | `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes. |
| `default_artifacts_expire_in` | string | no | Set the default expiration time for each job's artifacts. | | `default_artifacts_expire_in` | string | no | Set the default expiration time for each job's artifacts. |
| `default_branch_protection` | integer | no | Determine if developers can push to master. Can take: `0` _(not protected, both developers and maintainers can push new commits, force push, or delete the branch)_, `1` _(partially protected, developers and maintainers can push new commits, but cannot force push or delete the branch)_ or `2` _(fully protected, developers cannot push new commits, but maintainers can; no-one can force push or delete the branch)_ as a parameter. Default is `2`. | | `default_branch_protection` | integer | no | Determine if developers can push to master. Can take: `0` _(not protected, both developers and maintainers can push new commits, force push, or delete the branch)_, `1` _(partially protected, developers and maintainers can push new commits, but cannot force push or delete the branch)_ or `2` _(fully protected, developers cannot push new commits, but maintainers can; no-one can force push or delete the branch)_ as a parameter. Default is `2`. |
| `default_ci_config_path` | string | no | Default CI configuration path for new projects (`.gitlab-ci.yml` if not set). |
| `default_group_visibility` | string | no | What visibility level new groups receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. | | `default_group_visibility` | string | no | What visibility level new groups receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. |
| `default_project_creation` | integer | no | Default project creation protection. Can take: `0` _(No one)_, `1` _(Maintainers)_ or `2` _(Developers + Maintainers)_| | `default_project_creation` | integer | no | Default project creation protection. Can take: `0` _(No one)_, `1` _(Maintainers)_ or `2` _(Developers + Maintainers)_|
| `default_projects_limit` | integer | no | Project limit per user. Default is `100000`. | | `default_projects_limit` | integer | no | Project limit per user. Default is `100000`. |
......
...@@ -134,6 +134,19 @@ Once that time passes, the jobs will be archived and no longer able to be ...@@ -134,6 +134,19 @@ Once that time passes, the jobs will be archived and no longer able to be
retried. Make it empty to never expire jobs. It has to be no less than 1 day, retried. Make it empty to never expire jobs. It has to be no less than 1 day,
for example: <code>15 days</code>, <code>1 month</code>, <code>2 years</code>. for example: <code>15 days</code>, <code>1 month</code>, <code>2 years</code>.
## Default CI configuration path
> [Introduced](https://gitlab.com/gitlab-org/gitlab/merge_requests/18073) in GitLab 12.5.
The default CI configuration file path for new projects can be set in the Admin
area of your GitLab instance (`.gitlab-ci.yml` if not set):
1. Go to **Admin area > Settings > Continuous Integration and Deployment**.
1. Input the new path in the **Default CI configuration path** field.
1. Hit **Save changes** for the changes to take effect.
It is also possible to specify a [custom CI configuration path for a specific project](../../project/pipelines/settings.md#custom-ci-configuration-path).
<!-- ## Troubleshooting <!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues Include any troubleshooting steps that you can foresee. If you know beforehand what issues
......
...@@ -42,6 +42,7 @@ module API ...@@ -42,6 +42,7 @@ module API
optional :asset_proxy_whitelist, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Assets that match these domain(s) will NOT be proxied. Wildcards allowed. Your GitLab installation URL is automatically whitelisted.' optional :asset_proxy_whitelist, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Assets that match these domain(s) will NOT be proxied. Wildcards allowed. Your GitLab installation URL is automatically whitelisted.'
optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)' optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)'
optional :default_artifacts_expire_in, type: String, desc: "Set the default expiration time for each job's artifacts" optional :default_artifacts_expire_in, type: String, desc: "Set the default expiration time for each job's artifacts"
optional :default_ci_config_path, type: String, desc: 'The instance default CI configuration path for new projects'
optional :default_project_creation, type: Integer, values: ::Gitlab::Access.project_creation_values, desc: 'Determine if developers can create projects in the group' optional :default_project_creation, type: Integer, values: ::Gitlab::Access.project_creation_values, desc: 'Determine if developers can create projects in the group'
optional :default_branch_protection, type: Integer, values: ::Gitlab::Access.protection_values, desc: 'Determine if developers can push to master' optional :default_branch_protection, type: Integer, values: ::Gitlab::Access.protection_values, desc: 'Determine if developers can push to master'
optional :default_group_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default group visibility' optional :default_group_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default group visibility'
......
...@@ -5241,6 +5241,9 @@ msgstr "" ...@@ -5241,6 +5241,9 @@ msgstr ""
msgid "Default Branch" msgid "Default Branch"
msgstr "" msgstr ""
msgid "Default CI configuration path"
msgstr ""
msgid "Default artifacts expiration" msgid "Default artifacts expiration"
msgstr "" msgstr ""
...@@ -16840,6 +16843,9 @@ msgstr "" ...@@ -16840,6 +16843,9 @@ msgstr ""
msgid "The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository." msgid "The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository."
msgstr "" msgstr ""
msgid "The default CI configuration path for new projects."
msgstr ""
msgid "The dependency list details information about the components used within your project." msgid "The dependency list details information about the components used within your project."
msgstr "" msgstr ""
......
...@@ -2029,24 +2029,37 @@ describe Project do ...@@ -2029,24 +2029,37 @@ describe Project do
end end
describe '#ci_config_path=' do describe '#ci_config_path=' do
let(:project) { create(:project) } using RSpec::Parameterized::TableSyntax
it 'sets nil' do let(:project) { create(:project) }
project.update!(ci_config_path: nil)
expect(project.ci_config_path).to be_nil where(:default_ci_config_path, :project_ci_config_path, :expected_ci_config_path) do
nil | :notset | :default
nil | nil | :default
nil | '' | :default
nil | "cust\0om/\0/path" | 'custom//path'
'' | :notset | :default
'' | nil | :default
'' | '' | :default
'' | "cust\0om/\0/path" | 'custom//path'
'global/path' | :notset | 'global/path'
'global/path' | nil | :default
'global/path' | '' | :default
'global/path' | "cust\0om/\0/path" | 'custom//path'
end end
it 'sets a string' do with_them do
project.update!(ci_config_path: 'foo/.gitlab_ci.yml') before do
stub_application_setting(default_ci_config_path: default_ci_config_path)
expect(project.ci_config_path).to eq('foo/.gitlab_ci.yml')
end
it 'sets a string but removes all null characters' do if project_ci_config_path != :notset
project.update!(ci_config_path: "f\0oo/\0/.gitlab_ci.yml") project.ci_config_path = project_ci_config_path
end
end
expect(project.ci_config_path).to eq('foo//.gitlab_ci.yml') it 'returns the correct path' do
expect(project.ci_config_path.presence || :default).to eq(expected_ci_config_path)
end
end end
end end
......
...@@ -18,6 +18,7 @@ describe API::Settings, 'Settings' do ...@@ -18,6 +18,7 @@ describe API::Settings, 'Settings' do
expect(json_response['password_authentication_enabled']).to be_truthy expect(json_response['password_authentication_enabled']).to be_truthy
expect(json_response['plantuml_enabled']).to be_falsey expect(json_response['plantuml_enabled']).to be_falsey
expect(json_response['plantuml_url']).to be_nil expect(json_response['plantuml_url']).to be_nil
expect(json_response['default_ci_config_path']).to be_nil
expect(json_response['default_project_visibility']).to be_a String expect(json_response['default_project_visibility']).to be_a String
expect(json_response['default_snippet_visibility']).to be_a String expect(json_response['default_snippet_visibility']).to be_a String
expect(json_response['default_group_visibility']).to be_a String expect(json_response['default_group_visibility']).to be_a String
...@@ -49,6 +50,7 @@ describe API::Settings, 'Settings' do ...@@ -49,6 +50,7 @@ describe API::Settings, 'Settings' do
it "updates application settings" do it "updates application settings" do
put api("/application/settings", admin), put api("/application/settings", admin),
params: { params: {
default_ci_config_path: 'debian/salsa-ci.yml',
default_projects_limit: 3, default_projects_limit: 3,
default_project_creation: 2, default_project_creation: 2,
password_authentication_enabled_for_web: false, password_authentication_enabled_for_web: false,
...@@ -80,6 +82,7 @@ describe API::Settings, 'Settings' do ...@@ -80,6 +82,7 @@ describe API::Settings, 'Settings' do
} }
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(json_response['default_ci_config_path']).to eq('debian/salsa-ci.yml')
expect(json_response['default_projects_limit']).to eq(3) expect(json_response['default_projects_limit']).to eq(3)
expect(json_response['default_project_creation']).to eq(::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) expect(json_response['default_project_creation']).to eq(::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS)
expect(json_response['password_authentication_enabled_for_web']).to be_falsey expect(json_response['password_authentication_enabled_for_web']).to be_falsey
......
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