Implement the implied CI/CD config for AutoDevOps

Behind an application setting, which defaults to false, this commit
implements the implied CI/CD config. Which means that in the case we
can't find the `.gitlab-ci.yml` on the commit we want to start a
pipeline for, we fall back to an implied configuration.

For now the Bash template has been copied to
`Auto-Devops.gitlab-ci.yml` so the tests actually work.

Fixes #34777
This commit is contained in:
Zeger-Jan van de Weg 2017-08-24 13:01:33 +02:00
parent e584283822
commit 6ed490401f
No known key found for this signature in database
GPG key ID: 65F6A8D64A88ABAC
15 changed files with 194 additions and 18 deletions

View file

@ -103,6 +103,7 @@ module ApplicationSettingsHelper
:after_sign_up_text,
:akismet_api_key,
:akismet_enabled,
:auto_devops_enabled,
:clientside_sentry_dsn,
:clientside_sentry_enabled,
:container_registry_token_expire_delay,

View file

@ -338,12 +338,10 @@ module Ci
def ci_yaml_file
return @ci_yaml_file if defined?(@ci_yaml_file)
@ci_yaml_file = begin
project.repository.gitlab_ci_yml_for(sha, ci_yaml_file_path)
rescue Rugged::ReferenceError, GRPC::NotFound, GRPC::Internal
self.yaml_errors =
"Failed to load CI/CD config file at #{ci_yaml_file_path}"
nil
@ci_yaml_file = (ci_yaml_from_repo || implied_ci_yaml_file).tap do |config|
unless config
self.yaml_errors = "Failed to load CI/CD config file for #{sha}"
end
end
end
@ -430,6 +428,18 @@ module Ci
private
def implied_ci_yaml_file
if project.auto_devops_enabled?
Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps').content
end
end
def ci_yaml_from_repo
project.repository.gitlab_ci_yml_for(sha, ci_yaml_file_path)
rescue GRPC::NotFound, Rugged::ReferenceError, GRPC::Internal
nil
end
def pipeline_data
Gitlab::DataBuilder::Pipeline.build(self)
end

View file

@ -186,6 +186,8 @@ class Project < ActiveRecord::Base
has_many :active_runners, -> { active }, through: :runner_projects, source: :runner, class_name: 'Ci::Runner'
has_one :auto_devops, class_name: 'ProjectAutoDevops'
accepts_nested_attributes_for :variables, allow_destroy: true
accepts_nested_attributes_for :project_feature
accepts_nested_attributes_for :import_data
@ -464,6 +466,10 @@ class Project < ActiveRecord::Base
self[:lfs_enabled] && Gitlab.config.lfs.enabled
end
def auto_devops_enabled?
auto_devops&.enabled? || current_application_settings.auto_devops_enabled?
end
def repository_storage_path
Gitlab.config.repositories.storages[repository_storage]['path']
end

View file

@ -0,0 +1,3 @@
class ProjectAutoDevops < ActiveRecord::Base
belongs_to :project
end

View file

@ -217,7 +217,13 @@
.help-block 0 for unlimited
%fieldset
%legend Continuous Integration
%legend Continuous Integration and Continuous Deployment
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :auto_devops_enabled do
= f.check_box :auto_devops_enabled
Allow projects to get automaticly configured to use GitLab CI/CD.
.form-group
.col-sm-offset-2.col-sm-10
.checkbox

View file

@ -0,0 +1,5 @@
---
title: Allow users and administrator to configure Auto-DevOps
merge_request: 13923
author:
type: added

View file

@ -10,5 +10,10 @@
# end
#
ActiveSupport::Inflector.inflections do |inflect|
inflect.uncountable %w(award_emoji project_statistics system_note_metadata)
inflect.uncountable %w(
award_emoji
project_statistics
system_note_metadata
project_auto_devops
)
end

View file

@ -0,0 +1,15 @@
class AddAutoDevopsEnabledToApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default(:application_settings, :auto_devops_enabled, :boolean, default: false)
end
def down
remove_column(:application_settings, :auto_devops_enabled, :boolean)
end
end

View file

@ -0,0 +1,24 @@
class CreateProjectAutoDevOps < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
create_table :project_auto_devops do |t|
t.belongs_to :project, index: true
t.boolean :enabled, default: true
t.string :domain
end
add_timestamps_with_timezone(:project_auto_devops, null: false)
# No need to check for violations as its a new table
add_concurrent_foreign_key(:project_auto_devops, :projects, column: :project_id)
end
def down
drop_table(:project_auto_devops)
end
end

View file

@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170824162758) do
ActiveRecord::Schema.define(version: 20170828093725) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -125,10 +125,11 @@ ActiveRecord::Schema.define(version: 20170824162758) do
t.boolean "prometheus_metrics_enabled", default: false, null: false
t.boolean "help_page_hide_commercial_content", default: false
t.string "help_page_support_url"
t.integer "performance_bar_allowed_group_id"
t.boolean "password_authentication_enabled"
t.boolean "project_export_enabled", default: true, null: false
t.integer "performance_bar_allowed_group_id"
t.boolean "hashed_storage_enabled", default: false, null: false
t.boolean "project_export_enabled", default: true, null: false
t.boolean "auto_devops_enabled", default: false, null: false
end
create_table "audit_events", force: :cascade do |t|
@ -249,6 +250,7 @@ ActiveRecord::Schema.define(version: 20170824162758) do
add_index "ci_builds", ["commit_id", "status", "type"], name: "index_ci_builds_on_commit_id_and_status_and_type", using: :btree
add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree
add_index "ci_builds", ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree
add_index "ci_builds", ["id"], name: "index_for_ci_builds_retried_migration", where: "(retried IS NULL)", using: :btree, opclasses: {"id)"=>"WHERE"}
add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree
add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree
add_index "ci_builds", ["stage_id"], name: "index_ci_builds_on_stage_id", using: :btree
@ -1116,6 +1118,16 @@ ActiveRecord::Schema.define(version: 20170824162758) do
add_index "project_authorizations", ["project_id"], name: "index_project_authorizations_on_project_id", using: :btree
add_index "project_authorizations", ["user_id", "project_id", "access_level"], name: "index_project_authorizations_on_user_id_project_id_access_level", unique: true, using: :btree
create_table "project_auto_devops", force: :cascade do |t|
t.integer "project_id"
t.boolean "enabled", default: true
t.string "domain"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "project_auto_devops", ["project_id"], name: "index_project_auto_devops_on_project_id", using: :btree
create_table "project_features", force: :cascade do |t|
t.integer "project_id"
t.integer "merge_requests_access_level"
@ -1199,6 +1211,7 @@ ActiveRecord::Schema.define(version: 20170824162758) do
t.string "repository_storage", default: "default", null: false
t.boolean "request_access_enabled", default: false, null: false
t.boolean "has_external_wiki"
t.string "ci_config_path"
t.boolean "lfs_enabled"
t.text "description_html"
t.boolean "only_allow_merge_if_all_discussions_are_resolved"
@ -1206,9 +1219,8 @@ ActiveRecord::Schema.define(version: 20170824162758) do
t.integer "auto_cancel_pending_pipelines", default: 1, null: false
t.string "import_jid"
t.integer "cached_markdown_version"
t.datetime "last_repository_updated_at"
t.string "ci_config_path"
t.text "delete_error"
t.datetime "last_repository_updated_at"
t.integer "storage_version", limit: 2
end
@ -1722,6 +1734,7 @@ ActiveRecord::Schema.define(version: 20170824162758) do
add_foreign_key "personal_access_tokens", "users"
add_foreign_key "project_authorizations", "projects", on_delete: :cascade
add_foreign_key "project_authorizations", "users", on_delete: :cascade
add_foreign_key "project_auto_devops", "projects", name: "fk_45436b12b2", on_delete: :cascade
add_foreign_key "project_features", "projects", name: "fk_18513d9b92", on_delete: :cascade
add_foreign_key "project_group_links", "projects", name: "fk_daa8cee94c", on_delete: :cascade
add_foreign_key "project_import_data", "projects", name: "fk_ffb9ee3a10", on_delete: :cascade

View file

@ -0,0 +1,6 @@
FactoryGirl.define do
factory :project_auto_devops do
project
enabled true
end
end

View file

@ -5,6 +5,7 @@ describe ApplicationSetting do
it { expect(setting).to be_valid }
it { expect(setting.uuid).to be_present }
it { expect(setting).to have_db_column(:auto_devops_enabled) }
describe 'validations' do
let(:http) { 'http://example.com' }

View file

@ -784,13 +784,47 @@ describe Ci::Pipeline, :mailer do
end
describe '#ci_yaml_file' do
it 'reports error if the file is not found' do
allow(pipeline.project).to receive(:ci_config_path) { 'custom' }
let(:implied_yml) { Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps').content }
pipeline.ci_yaml_file
context 'when AutoDevops is enabled' do
it 'returns the configuration if found' do
allow(pipeline.project.repository).to receive(:gitlab_ci_yml_for)
.and_return('config')
expect(pipeline.yaml_errors)
.to eq('Failed to load CI/CD config file at custom')
expect(pipeline.ci_yaml_file).to be_a(String)
expect(pipeline.ci_yaml_file).not_to eq(implied_yml)
expect(pipeline.yaml_errors).to be_nil
end
it 'returns the implied configuration when its not found' do
allow_any_instance_of(ApplicationSetting)
.to receive(:auto_devops_enabled?) { true }
allow(pipeline.project).to receive(:ci_config_path) { 'custom' }
expect(pipeline.ci_yaml_file).to eq(implied_yml)
end
end
context 'when AudoDevOps is disabled' do
context 'when an invalid path is given' do
it 'sets the yaml errors' do
allow(pipeline.project).to receive(:ci_config_path) { 'custom' }
expect(pipeline.ci_yaml_file).to be_nil
expect(pipeline.yaml_errors)
.to start_with('Failed to load CI/CD config file')
end
end
context 'when the config file can be found' do
it 'has no yaml_errors' do
allow(pipeline.project.repository).to receive(:gitlab_ci_yml_for)
.and_return('config')
expect(pipeline.ci_yaml_file).to eq('config')
expect(pipeline.yaml_errors).to be_nil
end
end
end
end

View file

@ -0,0 +1,12 @@
require 'spec_helper'
describe ProjectAutoDevops, type: :model do
subject { build_stubbed(:project_auto_devops) }
it { is_expected.to belong_to(:project) }
it { is_expected.to respond_to(:created_at) }
it { is_expected.to respond_to(:updated_at) }
it { is_expected.to be_enabled }
end

View file

@ -0,0 +1,35 @@
# see https://docs.gitlab.com/ce/ci/yaml/README.html for all available options
# you can delete this line if you're not using Docker
image: busybox:latest
before_script:
- echo "Before script section"
- echo "For example you might run an update here or install a build dependency"
- echo "Or perhaps you might print out some debugging details"
after_script:
- echo "After script section"
- echo "For example you might do some cleanup here"
build1:
stage: build
script:
- echo "Do your build here"
test1:
stage: test
script:
- echo "Do a test here"
- echo "For example run a test suite"
test2:
stage: test
script:
- echo "Do another parallel test here"
- echo "For example run a lint test"
deploy1:
stage: deploy
script:
- echo "Do your deploy here"