Commit f88659d0 authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch 'zj-auto-devops-table' into 'master'

Implement the implied CI/CD config for AutoDevOps

Closes #34777

See merge request !13923
parents 45510326 12ddc28f
......@@ -35,6 +35,7 @@ document.addEventListener('DOMContentLoaded', () => {
propsData: {
endpoint: pipelineTableViewEl.dataset.endpoint,
helpPagePath: pipelineTableViewEl.dataset.helpPagePath,
autoDevopsHelpPath: pipelineTableViewEl.dataset.helpAutoDevopsPath,
},
}).$mount();
pipelineTableViewEl.appendChild(table.$el);
......
......@@ -13,6 +13,10 @@
type: String,
required: true,
},
autoDevopsHelpPath: {
type: String,
required: true,
},
},
mixins: [
pipelinesMixin,
......@@ -95,6 +99,7 @@
<pipelines-table-component
:pipelines="state.pipelines"
:update-graph-dropdown="updateGraphDropdown"
:auto-devops-help-path="autoDevopsHelpPath"
/>
</div>
</div>
......
......@@ -243,6 +243,7 @@ import bp from './breakpoints';
propsData: {
endpoint: pipelineTableViewEl.dataset.endpoint,
helpPagePath: pipelineTableViewEl.dataset.helpPagePath,
autoDevopsHelpPath: pipelineTableViewEl.dataset.helpAutoDevopsPath,
},
}).$mount();
......
<script>
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import tooltip from '../../vue_shared/directives/tooltip';
import popover from '../../vue_shared/directives/popover';
export default {
props: {
pipeline: {
type: Object,
required: true,
export default {
props: {
pipeline: {
type: Object,
required: true,
},
autoDevopsHelpPath: {
type: String,
required: true,
},
},
},
components: {
userAvatarLink,
},
directives: {
tooltip,
},
computed: {
user() {
return this.pipeline.user;
components: {
userAvatarLink,
},
},
};
directives: {
tooltip,
popover,
},
computed: {
user() {
return this.pipeline.user;
},
popoverOptions() {
return {
html: true,
delay: { hide: 600 },
trigger: 'hover',
placement: 'top',
title: '<div class="autodevops-title">This pipeline makes use of a predefined CI/CD configuration enabled by <b>Auto DevOps.</b></div>',
content: `<a class="autodevops-link" href="${this.autoDevopsHelpPath}" target="_blank" rel="noopener noreferrer nofollow">Learn more about Auto DevOps</a>`,
};
},
},
};
</script>
<template>
<div class="table-section section-15 hidden-xs hidden-sm">
<div class="table-section section-15 hidden-xs hidden-sm pipeline-tags">
<a
:href="pipeline.path"
class="js-pipeline-url-link">
......@@ -57,6 +73,13 @@ export default {
:title="pipeline.yaml_errors">
yaml invalid
</span>
<a
v-if="pipeline.flags.auto_devops"
class="js-pipeline-url-autodevops label label-info autodevops-badge"
v-popover="popoverOptions"
role="button">
Auto DevOps
</a>
<span
v-if="pipeline.flags.stuck"
class="js-pipeline-url-stuck label label-warning">
......
......@@ -27,6 +27,7 @@
endpoint: pipelinesData.endpoint,
cssClass: pipelinesData.cssClass,
helpPagePath: pipelinesData.helpPagePath,
autoDevopsPath: pipelinesData.helpAutoDevopsPath,
newPipelinePath: pipelinesData.newPipelinePath,
canCreatePipeline: pipelinesData.canCreatePipeline,
allPath: pipelinesData.allPath,
......@@ -200,6 +201,7 @@
<pipelines-table-component
:pipelines="state.pipelines"
:update-graph-dropdown="updateGraphDropdown"
:auto-devops-help-path="autoDevopsPath"
/>
</div>
......
......@@ -17,6 +17,10 @@
required: false,
default: false,
},
autoDevopsHelpPath: {
type: String,
required: true,
},
},
components: {
pipelinesTableRowComponent,
......@@ -54,6 +58,7 @@
:key="model.id"
:pipeline="model"
:update-graph-dropdown="updateGraphDropdown"
:auto-devops-help-path="autoDevopsHelpPath"
/>
</div>
</template>
......@@ -25,6 +25,10 @@ export default {
required: false,
default: false,
},
autoDevopsHelpPath: {
type: String,
required: true,
},
},
components: {
asyncButtonComponent,
......@@ -218,7 +222,10 @@ export default {
</div>
</div>
<pipeline-url :pipeline="pipeline" />
<pipeline-url
:pipeline="pipeline"
:auto-devops-help-path="autoDevopsHelpPath"
/>
<div class="table-section section-25">
<div
......
/**
* Helper to user bootstrap popover in vue.js.
* Follow docs for html attributes: https://getbootstrap.com/docs/3.3/javascript/#static-popover
*
* @example
* import popover from 'vue_shared/directives/popover.js';
* {
* directives: [popover]
* }
* <a v-popover="{options}">popover</a>
*/
export default {
bind(el, binding) {
$(el).popover(binding.value);
},
unbind(el) {
$(el).popover('destroy');
},
};
......@@ -226,6 +226,14 @@
vertical-align: baseline;
}
a.autodevops-badge {
color: $white-light;
}
a.autodevops-link {
color: $gl-link-color;
}
.commit-row-description {
font-size: 14px;
padding: 10px 15px;
......
......@@ -202,6 +202,10 @@
.btn-group.open .dropdown-toggle {
box-shadow: none;
}
.pipeline-tags .label-container {
white-space: normal;
}
}
.stage-cell {
......@@ -932,3 +936,8 @@ button.mini-pipeline-graph-dropdown-toggle {
.pipelines-container .top-area .nav-controls > .btn:last-child {
float: none;
}
.autodevops-title {
font-weight: $gl-font-weight-normal;
line-height: 1.5;
}
......@@ -6,7 +6,7 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
end
def update
if @project.update_attributes(update_params)
if @project.update(update_params)
flash[:notice] = "Pipelines settings for '#{@project.name}' were successfully updated."
redirect_to project_settings_ci_cd_path(@project)
else
......@@ -16,14 +16,12 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
private
def create_params
params.require(:pipeline).permit(:ref)
end
def update_params
params.require(:project).permit(
:runners_token, :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
:public_builds, :auto_cancel_pending_pipelines, :ci_config_path
:runners_token, :builds_enabled, :build_allow_git_fetch,
:build_timeout_in_minutes, :build_coverage_regex, :public_builds,
:auto_cancel_pending_pipelines, :ci_config_path,
auto_devops_attributes: [:id, :domain, :enabled]
)
end
end
......@@ -8,6 +8,7 @@ module Projects
define_secret_variables
define_triggers_variables
define_badges_variables
define_auto_devops_variables
end
private
......@@ -42,6 +43,10 @@ module Projects
badge.new(@project, @ref).metadata
end
end
def define_auto_devops_variables
@auto_devops = @project.auto_devops || ProjectAutoDevops.new
end
end
end
end
......@@ -115,6 +115,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,
......
......@@ -216,6 +216,7 @@ module Ci
variables += runner.predefined_variables if runner
variables += project.container_registry_variables
variables += project.deployment_variables if has_environment?
variables += project.auto_devops_variables
variables += yaml_variables
variables += user_variables
variables += project.group.secret_variables_for(ref, project).map(&:to_runner_variable) if project.group
......
......@@ -38,6 +38,7 @@ module Ci
validates :status, presence: { unless: :importing? }
validate :valid_commit_sha, unless: :importing?
after_initialize :set_config_source, if: :new_record?
after_create :keep_around_commits, unless: :importing?
enum source: {
......@@ -50,6 +51,12 @@ module Ci
external: 6
}
enum config_source: {
unknown_source: nil,
repository_source: 1,
auto_devops_source: 2
}
state_machine :status, initial: :created do
event :enqueue do
transition created: :pending
......@@ -316,6 +323,14 @@ module Ci
builds.latest.failed_but_allowed.any?
end
def set_config_source
if ci_yaml_from_repo
self.config_source = :repository_source
elsif implied_ci_yaml_file
self.config_source = :auto_devops_source
end
end
def config_processor
return unless ci_yaml_file
return @config_processor if defined?(@config_processor)
......@@ -342,11 +357,17 @@ 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}"
@ci_yaml_file =
if auto_devops_source?
implied_ci_yaml_file
else
ci_yaml_from_repo
end
if @ci_yaml_file
@ci_yaml_file
else
self.yaml_errors = "Failed to load CI/CD config file for #{sha}"
nil
end
end
......@@ -434,6 +455,23 @@ module Ci
private
def ci_yaml_from_repo
return unless project
return unless sha
project.repository.gitlab_ci_yml_for(sha, ci_yaml_file_path)
rescue GRPC::NotFound, Rugged::ReferenceError, GRPC::Internal
nil
end
def implied_ci_yaml_file
return unless project
if project.auto_devops_enabled?
Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps').content
end
end
def pipeline_data
Gitlab::DataBuilder::Pipeline.build(self)
end
......
......@@ -187,9 +187,12 @@ 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
accepts_nested_attributes_for :auto_devops
delegate :name, to: :owner, allow_nil: true, prefix: true
delegate :members, to: :team, prefix: true
......@@ -466,6 +469,14 @@ class Project < ActiveRecord::Base
self[:lfs_enabled] && Gitlab.config.lfs.enabled
end
def auto_devops_enabled?
if auto_devops&.enabled.nil?
current_application_settings.auto_devops_enabled?
else
auto_devops.enabled?
end
end
def repository_storage_path
Gitlab.config.repositories.storages[repository_storage].try(:[], 'path')
end
......@@ -1378,6 +1389,10 @@ class Project < ActiveRecord::Base
Gitlab::Utils.slugify(full_path.to_s)
end
def has_ci?
repository.gitlab_ci_yml || auto_devops_enabled?
end
def predefined_variables
[
{ key: 'CI_PROJECT_ID', value: id.to_s, public: true },
......@@ -1423,6 +1438,12 @@ class Project < ActiveRecord::Base
deployment_service.predefined_variables
end
def auto_devops_variables
return [] unless auto_devops_enabled?
auto_devops&.variables || []
end
def append_or_update_attribute(name, value)
old_values = public_send(name.to_s) # rubocop:disable GitlabSecurity/PublicSend
......
class ProjectAutoDevops < ActiveRecord::Base
belongs_to :project
validates :domain, allow_blank: true, hostname: { allow_numeric_hostname: true }
def variables
variables = []
variables << { key: 'AUTO_DEVOPS_DOMAIN', value: domain, public: true } if domain.present?
variables
end
end
......@@ -16,6 +16,7 @@ class PipelineEntity < Grape::Entity
expose :flags do
expose :latest?, as: :latest
expose :stuck?, as: :stuck
expose :auto_devops_source?, as: :auto_devops
expose :has_yaml_errors?, as: :yaml_errors
expose :can_retry?, as: :retryable
expose :can_cancel?, as: :cancelable
......
......@@ -226,7 +226,17 @@
.help-block 0 for unlimited
%fieldset
%legend Continuous Integration
%legend Continuous Integration and Deployment
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :auto_devops_enabled do
= f.check_box :auto_devops_enabled
Enabled Auto DevOps (Beta) for projects by default
.help-block
It will automatically build, test, and deploy applications based on a predefined CI/CD configuration
= link_to icon('question-circle'), help_page_path('topics/autodevops/index.md')
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
......
......@@ -2,6 +2,7 @@
#commit-pipeline-table-view{ data: { disable_initialization: disable_initialization,
endpoint: endpoint,
"help-page-path" => help_page_path('ci/quick_start/README'),
"help-auto-devops-path" => help_page_path('topics/autodevops/index.md'),
} }
- content_for :page_specific_javascripts do
......
......@@ -5,6 +5,7 @@
#pipelines-list-vue{ data: { endpoint: project_pipelines_path(@project, format: :json),
"css-class" => container_class,
"help-page-path" => help_page_path('ci/quick_start/README'),
"help-auto-devops-path" => help_page_path('topics/autodevops/index.md'),
"new-pipeline-path" => new_project_pipeline_path(@project),
"can-create-pipeline" => can?(current_user, :create_pipeline, @project).to_s,
"all-path" => project_pipelines_path(@project),
......@@ -13,7 +14,7 @@
"finished-path" => project_pipelines_path(@project, scope: :finished),
"branches-path" => project_pipelines_path(@project, scope: :branches),
"tags-path" => project_pipelines_path(@project, scope: :tags),
"has-ci" => @repository.gitlab_ci_yml,
"has-ci" => @project.has_ci?,
"ci-lint-path" => ci_lint_path } }
= page_specific_javascript_bundle_tag('common_vue')
......
......@@ -2,11 +2,42 @@
.col-lg-12
= form_for @project, url: project_pipelines_settings_path(@project) do |f|
%fieldset.builds-feature
- unless @repository.gitlab_ci_yml
.form-group
%p Pipelines need to be configured before you can begin using Continuous Integration.
= link_to 'Get started with Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
%hr
.form-group
%p Pipelines need to have Auto DevOps enabled or have a .gitlab-ci.yml configured before you can begin using Continuous Integration and Delivery.
%h5 Auto DevOps (Beta)
%p
Auto DevOps will automatically build, test, and deploy your application based on a predefined Continious Integration and Delivery configuration.
= link_to 'Learn more about Auto DevOps', help_page_path('topics/autodevops/index.md')
= f.fields_for :auto_devops_attributes, @auto_devops do |form|
.radio
= form.label :enabled_true do
= form.radio_button :enabled, 'true'
%strong Enable Auto DevOps
%br
%span.descr
The Auto DevOps pipeline configuration will be used when there is no .gitlab-ci.yml
in the project.
.radio
= form.label :enabled_false do
= form.radio_button :enabled, 'false'
%strong Disable Auto DevOps
%br
%span.descr
A specific .gitlab-ci.yml file needs to be specified before you can begin using Continious Integration and Delivery.
.radio
= form.label :enabled do
= form.radio_button :enabled, nil
%strong
Instance default (status: #{current_application_settings.auto_devops_enabled?})
%br
%span.descr
Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific .gitlab-ci.yml file specified.
%br
%p
Define a domain used by Auto DevOps to deploy towards, this is required for deploys to succeed.
= form.text_field :domain, class: 'form-control', placeholder: 'domain.com'
%hr
.form-group.append-bottom-default
= f.label :runners_token, "Runner token", class: 'label-light'
= f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89'
......
---
title: Allow users and administrator to configure Auto-DevOps
merge_request: 13923
author:
type: added
......@@ -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
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
class CreateProjectAutoDevOps < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
create_table :project_auto_devops do |t|
t.belongs_to :project, null: false, index: { unique: true }, foreign_key: { on_delete: :cascade }
t.datetime_with_timezone :created_at, null: false
t.datetime_with_timezone :updated_at, null: false
t.boolean :enabled, default: nil, null: true
t.string :domain
end
end
def down
drop_table(:project_auto_devops)
end
end
class AddConfigSourceToPipelines < ActiveRecord::Migration
DOWNTIME = false
def change
add_column(:ci_pipelines, :config_source, :integer, allow_null: true)
end
end
......@@ -133,6 +133,7 @@ ActiveRecord::Schema.define(version: 20170905112933) do
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|
......@@ -340,6 +341,7 @@ ActiveRecord::Schema.define(version: 20170905112933) do
t.integer "auto_canceled_by_id"
t.integer "pipeline_schedule_id"
t.integer "source"
t.integer "config_source"
t.boolean "protected"
end
......@@ -1108,6 +1110,16 @@ ActiveRecord::Schema.define(version: 20170905112933) 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", null: false
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.boolean "enabled"
t.string "domain"
end
add_index "project_auto_devops", ["project_id"], name: "index_project_auto_devops_on_project_id", unique: true, using: :btree
create_table "project_features", force: :cascade do |t|
t.integer "project_id"
t.integer "merge_requests_access_level"
......@@ -1724,6 +1736,7 @@ ActiveRecord::Schema.define(version: 20170905112933) 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", 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
......
......@@ -50,6 +50,7 @@ project_tree:
- :push_event_payload
- :stages
- :statuses
- :auto_devops
- :triggers
- :pipeline_schedules
- :services
......@@ -142,4 +143,4 @@ methods:
events:
- :action
push_event_payload:
- :action
\ No newline at end of file
- :action
......@@ -14,6 +14,7 @@ module Gitlab
create_access_levels: 'ProtectedTag::CreateAccessLevel',
labels: :project_labels,
priorities: :label_priorities,
auto_devops: :project_auto_devops,
label: :project_label }.freeze
USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id created_by_id last_edited_by_id merge_user_id resolved_by_id].freeze
......
FactoryGirl.define do
factory :project_auto_devops do
project
enabled true
domain "example.com"
end
end
......@@ -41,5 +41,15 @@ feature "Pipelines settings" do
checkbox = find_field('project_auto_cancel_pending_pipelines')
expect(checkbox).to be_checked
end
scenario 'update auto devops settings' do
fill_in('project_auto_devops_attributes_domain', with: 'test.com')
page.choose('project_auto_devops_attributes_enabled_false')
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
end
end
......@@ -29,6 +29,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
propsData: {
endpoint: 'endpoint',
helpPagePath: 'foo',
autoDevopsHelpPath: 'foo',
},
}).$mount();
});
......@@ -64,6 +65,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
propsData: {
endpoint: 'endpoint',
helpPagePath: 'foo',
autoDevopsHelpPath: 'foo',
},
}).$mount();
});
......@@ -115,6 +117,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
propsData: {
endpoint: 'endpoint',
helpPagePath: 'foo',
autoDevopsHelpPath: 'foo',
},
}).$mount();
element.appendChild(this.component.$el);
......@@ -136,6 +139,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
propsData: {
endpoint: 'endpoint',
helpPagePath: 'foo',
autoDevopsHelpPath: 'foo',
},
}).$mount();
});
......
......@@ -16,6 +16,7 @@ describe('Pipeline Url Component', () => {
path: 'foo',
flags: {},
},
autoDevopsHelpPath: 'foo',
},
}).$mount();
......@@ -30,6 +31,7 @@ describe('Pipeline Url Component', () => {
path: 'foo',
flags: {},
},
autoDevopsHelpPath: 'foo',
},
}).$mount();
......@@ -50,6 +52,7 @@ describe('Pipeline Url Component', () => {
path: '/',
},
},
autoDevopsHelpPath: 'foo',
};
const component = new PipelineUrlComponent({
......@@ -73,6 +76,7 @@ describe('Pipeline Url Component', () => {
path: 'foo',
flags: {},
},
autoDevopsHelpPath: 'foo',
},
}).$mount();
......@@ -91,6 +95,7 @@ describe('Pipeline Url Component', () => {
stuck: true,
},
},
autoDevopsHelpPath: 'foo',
},
}).$mount();
......@@ -98,4 +103,26 @@ describe('Pipeline Url Component', () => {
expect(component.$el.querySelector('.js-pipeline-url-yaml').textContent).toContain('yaml invalid');
expect(component.$el.querySelector('.js-pipeline-url-stuck').textContent).toContain('stuck');
});
it('should render a badge for autodevops', () => {
const component = new PipelineUrlComponent({
propsData: {
pipeline: {
id: 1,
path: 'foo',
flags: {
latest: true,
yaml_errors: true,
stuck: true,
auto_devops: true,
},
},
autoDevopsHelpPath: 'foo',
},
}).$mount();
expect(
component.$el.querySelector('.js-pipeline-url-autodevops').textContent.trim(),
).toEqual('Auto DevOps');
});
});
......@@ -9,7 +9,7 @@ describe('Pipelines Table Row', () => {
el: document.querySelector('.test-dom-element'),
propsData: {
pipeline,
service: {},
autoDevopsHelpPath: 'foo',
},
}).$mount();
};
......
......@@ -22,6 +22,7 @@ describe('Pipelines Table', () => {
component = new PipelinesTableComponent({
propsData: {
pipelines: [],
autoDevopsHelpPath: 'foo',
},
}).$mount();
});
......@@ -47,6 +48,7 @@ describe('Pipelines Table', () => {
const component = new PipelinesTableComponent({
propsData: {
pipelines: [],
autoDevopsHelpPath: 'foo',
},
}).$mount();
expect(component.$el.querySelectorAll('.commit.gl-responsive-table-row').length).toEqual(0);
......@@ -58,6 +60,7 @@ describe('Pipelines Table', () => {
const component = new PipelinesTableComponent({
propsData: {
pipelines: [pipeline],
autoDevopsHelpPath: 'foo',
},
}).$mount();
......
......@@ -256,6 +256,7 @@ project:
- environments
- deployments
- project_feature
- auto_devops
- pages_domains
- authorized_users
- project_authorizations
......
......@@ -223,6 +223,7 @@ Ci::Pipeline:
- lock_version
- auto_canceled_by_id
- pipeline_schedule_id
- config_source
- protected
Ci::Stage:
- id
......@@ -466,3 +467,10 @@ Timelog:
- user_id
- created_at
- updated_at
ProjectAutoDevops:
- id
- enabled
- domain
- project_id
- created_at
- updated_at
......@@ -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' }
......
......@@ -1688,6 +1688,30 @@ describe Ci::Build do
{ key: 'secret', value: 'value', public: false }])
end
end
context 'when using auto devops' do
context 'and is enabled' do
before do
project.create_auto_devops!(enabled: true, domain: 'example.com')
end
it "includes AUTO_DEVOPS_DOMAIN" do
is_expected.to include(
{ key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true })
end
end
context 'and is disabled' do
before do
project.create_auto_devops!(enabled: false, domain: 'example.com')
end
it "includes AUTO_DEVOPS_DOMAIN" do
is_expected.not_to include(
{ key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true })
end
end
end
end
describe 'state transition: any => [:pending]' do
......
......@@ -799,14 +799,118 @@ describe Ci::Pipeline, :mailer do
end
end
describe '#set_config_source' do
context 'on object initialisation' do
context 'when pipelines does not contain needed data' do
let(:pipeline) do
Ci::Pipeline.new
end
it 'defines source to be unknown' do
expect(pipeline).to be_unknown_source
end
end
context 'when pipeline contains all needed data' do
let(:pipeline) do
Ci::Pipeline.new(
project: project,
sha: '1234',
ref: 'master',
source: :push)
end
context 'when the repository has a config file' do
before do
allow(project.repository).to receive(:gitlab_ci_yml_for)
.and_return('config')
end
it 'defines source to be from repository' do
expect(pipeline).to be_repository_source
end
context 'when loading an object' do
let(:new_pipeline) { Ci::Pipeline.find(pipeline.id) }
it 'does not redefine the source' do
# force to overwrite the source
pipeline.unknown_source!
expect(new_pipeline).to be_unknown_source
end
end
end
context 'when the repository does not have a config file' do
let(:implied_yml) { Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps').content }
context 'auto devops enabled' do
before do
stub_application_setting(auto_devops_enabled: true)
allow(project).to receive(:ci_config_path) { 'custom' }
end
it 'defines source to be auto devops' do
subject
expect(pipeline).to be_auto_devops_source
end
end
end
end
end
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 }
context 'the source is unknown' do
before do
pipeline.unknown_source!
end
it 'returns the configuration if found' do
allow(pipeline.project.repository).to receive(:gitlab_ci_yml_for)
.and_return('config')
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 'sets yaml errors if not found' do
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 'the source is the repository' do
before do
pipeline.repository_source!
end
pipeline.ci_yaml_file
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
end
context 'when the source is auto_devops_source' do
before do
stub_application_setting(auto_devops_enabled: true)
pipeline.auto_devops_source!
end
it 'finds the implied config' do
expect(pipeline.ci_yaml_file).to eq(implied_yml)
expect(pipeline.yaml_errors).to be_nil
end
end
end
......
require 'spec_helper'
describe ProjectAutoDevops do
set(:project) { build(:project) }
it { is_expected.to belong_to(:project) }
it { is_expected.to respond_to(:created_at) }
it { is_expected.to respond_to(:updated_at) }
describe 'variables' do
let(:auto_devops) { build_stubbed(:project_auto_devops, project: project, domain: domain) }
context 'when domain is defined' do
let(:domain) { 'example.com' }
it 'returns AUTO_DEVOPS_DOMAIN' do
expect(auto_devops.variables).to include(
{ key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true })
end
end
end
end
......@@ -53,6 +53,7 @@ describe Project do
it { is_expected.to have_one(:import_data).class_name('ProjectImportData') }
it { is_expected.to have_one(:last_event).class_name('Event') }
it { is_expected.to have_one(:forked_from_project).through(:forked_project_link) }
it { is_expected.to have_one(:auto_devops).class_name('ProjectAutoDevops') }
it { is_expected.to have_many(:commit_statuses) }
it { is_expected.to have_many(:pipelines) }
it { is_expected.to have_many(:builds) }
......@@ -2508,4 +2509,133 @@ describe Project do
end
end
end
describe '#has_ci?' do
set(:project) { create(:project) }
let(:repository) { double }
before do
expect(project).to receive(:repository) { repository }
end
context 'when has .gitlab-ci.yml' do
before do
expect(repository).to receive(:gitlab_ci_yml) { 'content' }
end
it "CI is available" do
expect(project).to have_ci
end
end
context 'when there is no .gitlab-ci.yml' do
before do
expect(repository).to receive(:gitlab_ci_yml) { nil }
end
it "CI is not available" do
expect(project).not_to have_ci
end
context 'when auto devops is enabled' do
before do
stub_application_setting(auto_devops_enabled: true)
end
it "CI is available" do
expect(project).to have_ci
end
end
end
end
describe '#auto_devops_enabled?' do
set(:project) { create(:project) }
subject { project.auto_devops_enabled? }
context 'when enabled in settings' do
before do
stub_application_setting(auto_devops_enabled: true)
end
it 'auto devops is implicitly enabled' do
expect(project.auto_devops).to be_nil
expect(project).to be_auto_devops_enabled
end
context 'when explicitly enabled' do
before do
create(:project_auto_devops, project: project)
end
it "auto devops is enabled" do
expect(project).to be_auto_devops_enabled
end
end
context 'when explicitly disabled' do
before do
create(:project_auto_devops, project: project, enabled: false)
end
it "auto devops is disabled" do
expect(project).not_to be_auto_devops_enabled
end
end
end
context 'when disabled in settings' do
before do
stub_application_setting(auto_devops_enabled: false)
end
it 'auto devops is implicitly disabled' do
expect(project.auto_devops).to be_nil
expect(project).not_to be_auto_devops_enabled
end
context 'when explicitly enabled' do
before do
create(:project_auto_devops, project: project)
end
it "auto devops is enabled" do
expect(project).to be_auto_devops_enabled
end
end
end
end
context '#auto_devops_variables' do
set(:project) { create(:project) }
subject { project.auto_devops_variables }
context 'when enabled in settings' do
before do
stub_application_setting(auto_devops_enabled: true)
end
context 'when domain is empty' do
before do
create(:project_auto_devops, project: project, domain: nil)
end
it 'variables are empty' do
is_expected.to be_empty
end
end
context 'when domain is configured' do
before do
create(:project_auto_devops, project: project, domain: 'example.com')
end
it "variables are not empty" do
is_expected.not_to be_empty
end
end
end
end
end
......@@ -36,7 +36,7 @@ describe PipelineEntity do
it 'contains flags' do
expect(subject).to include :flags
expect(subject[:flags])
.to include :latest, :stuck,
.to include :latest, :stuck, :auto_devops,
:yaml_errors, :retryable, :cancelable
end
end
......
require 'spec_helper'
describe Ci::CreatePipelineService do
let(:project) { create(:project, :repository) }
set(:project) { create(:project, :repository) }
let(:user) { create(:admin) }
let(:ref_name) { 'refs/heads/master' }
......
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