Commit 98b294e0 authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'master' into 4310-new-components-rows

* master: (61 commits)
  Update CHANGELOG.md for 10.6.1
  Update CHANGELOG-EE.md for 10.6.1-ee
  Put forms from expandable sections into fieldset to ensure proper animation
  Remove gray background from app setting expandable sections
  Improve application settings page according to feedback
  Fix failing spec in ee/spec/controllers/projects/variables_controller_spec.rb
  Prevent unnecessary, excessive updates
  Re-arrange deployment_platform to avoid cop complainments
  Make #base_pipeline returning the latest available pipeline for MR's base commit
  fix conflict
  Bump `state_machines-activerecord` to `0.5.1`
  Docs organize main page by product category
  ProtectedBranch API omits empty declared params
  Fix deployment_platform not memoizing values in correct way
  Move UI of several application settings to expandable block
  add SHA1 fingerprint requirement
  Make stage seed instance variables less ambiguous
  Remove outdates TODOs from pipeline class
  Remove N+1 query for Noteable association.
  Disable cursor for review mode in the IDE
  ...
parents 7bed22b0 1aec54db
Please view this file on the master branch, on stable branches it's out of date.
## 10.6.1 (2018-03-27)
### Fixed (8 changes)
- Fix LDAP group sync permission override UI. !5003
- Hard failing a mirror no longer fails for a blocked user's personal project. !5063
- Geo - Avoid rescheduling the same project again in a backfill condition. !5069
- Mark disabled wikis as fully synced. !5104
- Fix excessive updates to file_registry when wiki is disabled. !5119
- Geo: Recovery from temporary directory doesn't work if the namespace directory doesn't exist.
- Define a chat responder for the Slack app.
- Resolve "undefined method 'log_transfer_error'".
### Added (1 change)
- Also log Geo Prometheus metrics from primary. !5058
### Other (1 change)
- Update Epic documentation to include labels.
## 10.6.0 (2018-03-22)
### Security (2 changes)
......
......@@ -2,6 +2,27 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 10.6.1 (2018-03-27)
### Security (1 change)
- Bump rails-html-sanitizer to 1.0.4.
### Fixed (3 changes)
- Prevent auto-retry AccessDenied error from stopping transition to failed. !17862
- Fix 500 error when trying to resolve non-ASCII conflicts in the editor. !17962
- Don't capture trailing punctuation when autolinking. !17965
### Performance (1 change)
- Add indexes for user activity queries. !17890
### Other (1 change)
- Add documentation for runner IP address (#44232). !17837
## 10.6.0 (2018-03-22)
### Security (4 changes)
......
......@@ -172,7 +172,7 @@ group :unicorn do
end
# State machine
gem 'state_machines-activerecord', '~> 0.4.0'
gem 'state_machines-activerecord', '~> 0.5.1'
# Issue tags
gem 'acts-as-taggable-on', '~> 5.0'
......
......@@ -927,13 +927,13 @@ GEM
sqlite3 (1.3.13)
sshkey (1.9.0)
stackprof (0.2.10)
state_machines (0.4.0)
state_machines-activemodel (0.4.0)
activemodel (>= 4.1, < 5.1)
state_machines (>= 0.4.0)
state_machines-activerecord (0.4.0)
activerecord (>= 4.1, < 5.1)
state_machines-activemodel (>= 0.3.0)
state_machines (0.5.0)
state_machines-activemodel (0.5.1)
activemodel (>= 4.1, < 6.0)
state_machines (>= 0.5.0)
state_machines-activerecord (0.5.1)
activerecord (>= 4.1, < 6.0)
state_machines-activemodel (>= 0.5.0)
stringex (2.7.1)
sys-filesystem (1.1.6)
ffi
......@@ -1232,7 +1232,7 @@ DEPENDENCIES
sprockets (~> 3.7.0)
sshkey (~> 1.9.0)
stackprof (~> 0.2.10)
state_machines-activerecord (~> 0.4.0)
state_machines-activerecord (~> 0.5.1)
sys-filesystem (~> 1.1.6)
test-prof (~> 0.2.5)
test_after_commit (~> 1.1)
......
......@@ -43,8 +43,8 @@ GEM
i18n (~> 0.7)
minitest (~> 5.1)
tzinfo (~> 1.1)
acts-as-taggable-on (4.0.0)
activerecord (>= 4.0)
acts-as-taggable-on (5.0.0)
activerecord (>= 4.2.8)
adamantium (0.2.0)
ice_nine (~> 0.11.0)
memoizable (~> 0.4.0)
......@@ -178,10 +178,10 @@ GEM
docile (1.1.5)
domain_name (0.5.20170404)
unf (>= 0.0.5, < 1.0.0)
doorkeeper (4.2.6)
doorkeeper (4.3.1)
railties (>= 4.2)
doorkeeper-openid_connect (1.2.0)
doorkeeper (~> 4.0)
doorkeeper-openid_connect (1.3.0)
doorkeeper (~> 4.3)
json-jwt (~> 1.6)
dropzonejs-rails (0.7.4)
rails (> 3.1)
......@@ -220,13 +220,13 @@ GEM
path_expander (~> 1.0)
ruby_parser (~> 3.0)
sexp_processor (~> 4.0)
flipper (0.11.0)
flipper-active_record (0.11.0)
flipper (0.13.0)
flipper-active_record (0.13.0)
activerecord (>= 3.2, < 6)
flipper (~> 0.11.0)
flipper-active_support_cache_store (0.11.0)
flipper (~> 0.13.0)
flipper-active_support_cache_store (0.13.0)
activesupport (>= 3.2, < 6)
flipper (~> 0.11.0)
flipper (~> 0.13.0)
flowdock (0.7.1)
httparty (~> 0.7)
multi_json
......@@ -235,7 +235,7 @@ GEM
fog-json (~> 1.0)
ipaddress (~> 0.8)
xml-simple (~> 1.1)
fog-aws (1.4.1)
fog-aws (2.0.1)
fog-core (~> 1.38)
fog-json (~> 1.0)
fog-xml (~> 0.1)
......@@ -267,7 +267,7 @@ GEM
nokogiri (>= 1.5.11, < 2.0.0)
font-awesome-rails (4.7.0.3)
railties (>= 3.2, < 5.2)
foreman (0.78.0)
foreman (0.84.0)
thor (~> 0.19.1)
formatador (0.2.5)
fuubar (2.2.0)
......@@ -283,7 +283,7 @@ GEM
text (>= 1.3.0)
gettext_i18n_rails (1.8.0)
fast_gettext (>= 0.9.0)
gettext_i18n_rails_js (1.2.0)
gettext_i18n_rails_js (1.3.0)
gettext (>= 3.0.2)
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
......@@ -337,9 +337,9 @@ GEM
json
multi_json
request_store (>= 1.0)
google-api-client (0.13.6)
google-api-client (0.19.8)
addressable (~> 2.5, >= 2.5.1)
googleauth (~> 0.5)
googleauth (>= 0.5, < 0.7.0)
httpclient (>= 2.8.1, < 3.0)
mime-types (~> 3.0)
representable (~> 3.0)
......@@ -404,7 +404,7 @@ GEM
html2text (0.2.1)
nokogiri (~> 1.6)
htmlentities (4.3.4)
http (0.9.8)
http (2.2.2)
addressable (~> 2.3)
http-cookie (~> 1.0)
http-form_data (~> 1.0.1)
......@@ -427,10 +427,6 @@ GEM
multipart-post
oauth (~> 0.5, >= 0.5.0)
jquery-atwho-rails (1.3.2)
jquery-rails (4.3.1)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (1.8.6)
json-jwt (1.9.2)
activesupport
......@@ -454,13 +450,12 @@ GEM
kaminari-core (= 1.1.1)
kaminari-core (1.1.1)
kgio (2.11.2)
knapsack (1.11.1)
knapsack (1.16.0)
rake
timecop (>= 0.1.0)
kubeclient (2.2.0)
http (= 0.9.8)
recursive-open-struct (= 1.0.0)
rest-client
kubeclient (3.0.0)
http (~> 2.2.2)
recursive-open-struct (~> 1.0.4)
rest-client (~> 2.0)
launchy (2.4.3)
addressable (~> 2.3)
letter_opener (1.6.0)
......@@ -477,7 +472,7 @@ GEM
toml (= 0.1.2)
with_env (> 1.0)
xml-simple
licensee (8.7.0)
licensee (8.9.2)
rugged (~> 0.24)
little-plugger (1.1.4)
locale (2.1.2)
......@@ -514,7 +509,7 @@ GEM
mustermann (~> 1.0.0)
mysql2 (0.4.10)
net-ldap (0.16.1)
net-ssh (4.1.0)
net-ssh (4.2.0)
netrc (0.11.0)
nio4r (2.2.0)
nokogiri (1.8.2)
......@@ -527,11 +522,10 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
octokit (4.6.2)
octokit (4.8.0)
sawyer (~> 0.8.0, >= 0.5.3)
oj (2.17.5)
omniauth (1.4.3)
hashie (>= 1.2, < 4)
omniauth (1.8.1)
hashie (>= 3.4.6, < 3.6.0)
rack (>= 1.6.2, < 3)
omniauth-auth0 (1.4.2)
omniauth-oauth2 (~> 1.1)
......@@ -570,9 +564,9 @@ GEM
omniauth (~> 1.2)
omniauth-oauth2-generic (0.2.4)
omniauth-oauth2 (~> 1.0)
omniauth-saml (1.7.0)
omniauth (~> 1.3)
ruby-saml (~> 1.4)
omniauth-saml (1.10.0)
omniauth (~> 1.3, >= 1.3.2)
ruby-saml (~> 1.7)
omniauth-shibboleth (1.2.1)
omniauth (>= 1.0.0)
omniauth-twitter (1.2.1)
......@@ -598,8 +592,6 @@ GEM
railties (>= 4.0.0)
peek-gc (0.0.2)
peek
peek-host (1.0.0)
peek
peek-mysql2 (1.1.0)
atomic (>= 1.0.0)
mysql2
......@@ -713,7 +705,7 @@ GEM
re2 (1.1.1)
recaptcha (3.4.0)
json
recursive-open-struct (1.0.0)
recursive-open-struct (1.0.5)
redcarpet (3.4.0)
redis (3.3.5)
redis-actionpack (5.0.2)
......@@ -805,7 +797,7 @@ GEM
i18n
ruby-fogbugz (0.2.1)
crack (~> 0.4)
ruby-prof (0.16.2)
ruby-prof (0.17.0)
ruby-progressbar (1.9.0)
ruby-saml (1.7.2)
nokogiri (>= 1.5.10)
......@@ -846,7 +838,7 @@ GEM
selenium-webdriver (3.11.0)
childprocess (~> 0.5)
rubyzip (~> 1.2)
sentry-raven (2.5.3)
sentry-raven (2.7.2)
faraday (>= 0.7.6, < 1.0)
settingslogic (2.0.9)
sexp_processor (4.10.1)
......@@ -903,12 +895,12 @@ GEM
sshkey (1.9.0)
stackprof (0.2.11)
state_machines (0.5.0)
state_machines-activemodel (0.5.0)
activemodel (>= 4.1, < 5.2)
state_machines-activemodel (0.5.1)
activemodel (>= 4.1, < 6.0)
state_machines (>= 0.5.0)
state_machines-activerecord (0.4.1)
activerecord (>= 4.1, < 5.2)
state_machines-activemodel (>= 0.3.0)
state_machines-activerecord (0.5.1)
activerecord (>= 4.1, < 6.0)
state_machines-activemodel (>= 0.5.0)
stringex (2.8.4)
sys-filesystem (1.1.9)
ffi
......@@ -998,7 +990,7 @@ DEPENDENCIES
RedCloth (~> 4.3.2)
ace-rails-ap (~> 4.1.0)
activerecord_sane_schema_dumper (= 1.0)
acts-as-taggable-on (~> 4.0)
acts-as-taggable-on (~> 5.0)
addressable (~> 2.5.2)
akismet (~> 2.0)
allocations (~> 1.0)
......@@ -1038,8 +1030,8 @@ DEPENDENCIES
devise (~> 4.2)
devise-two-factor (~> 3.0.0)
diffy (~> 3.1.0)
doorkeeper (~> 4.2.0)
doorkeeper-openid_connect (~> 1.2.0)
doorkeeper (~> 4.3)
doorkeeper-openid_connect (~> 1.3)
dropzonejs-rails (~> 0.7.1)
email_reply_trimmer (~> 0.1)
email_spec (~> 1.6.0)
......@@ -1048,24 +1040,24 @@ DEPENDENCIES
fast_blank
ffaker (~> 2.4)
flay (~> 2.10.0)
flipper (~> 0.11.0)
flipper-active_record (~> 0.11.0)
flipper-active_support_cache_store (~> 0.11.0)
flipper (~> 0.13.0)
flipper-active_record (~> 0.13.0)
flipper-active_support_cache_store (~> 0.13.0)
fog-aliyun (~> 0.2.0)
fog-aws (~> 1.4)
fog-aws (~> 2.0)
fog-core (~> 1.44)
fog-google (~> 0.5)
fog-local (~> 0.3)
fog-openstack (~> 0.1)
fog-rackspace (~> 0.1.1)
font-awesome-rails (~> 4.7)
foreman (~> 0.78.0)
foreman (~> 0.84.0)
fuubar (~> 2.2.0)
gemnasium-gitlab-service (~> 0.2)
gemojione (~> 3.3)
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0)
gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 0.88.0)
github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1)
......@@ -1075,7 +1067,7 @@ DEPENDENCIES
gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.4)
gon (~> 6.1.0)
google-api-client (~> 0.13.6)
google-api-client (~> 0.19.8)
google-protobuf (= 3.5.1)
gpgme
grape (~> 1.0)
......@@ -1094,15 +1086,14 @@ DEPENDENCIES
influxdb (~> 0.2)
jira-ruby (~> 1.4)
jquery-atwho-rails (~> 1.3.2)
jquery-rails (~> 4.3.1)
json-schema (~> 2.8.0)
jwt (~> 1.5.6)
kaminari (~> 1.0)
knapsack (~> 1.11.0)
kubeclient (~> 2.2.0)
knapsack (~> 1.16)
kubeclient (~> 3.0)
letter_opener_web (~> 1.3.0)
license_finder (~> 3.1)
licensee (~> 8.7.0)
licensee (~> 8.9)
lograge (~> 0.5)
loofah (~> 2.0.3)
mail_room (~> 0.9.1)
......@@ -1111,12 +1102,11 @@ DEPENDENCIES
mousetrap-rails (~> 1.4.6)
mysql2 (~> 0.4.10)
net-ldap
net-ssh (~> 4.1.0)
net-ssh (~> 4.2.0)
nokogiri (~> 1.8.2)
oauth2 (~> 1.4)
octokit (~> 4.6.2)
oj (~> 2.17.4)
omniauth (~> 1.4.2)
octokit (~> 4.8)
omniauth (~> 1.8)
omniauth-auth0 (~> 1.4.1)
omniauth-authentiq (~> 0.3.1)
omniauth-azure-oauth2 (~> 0.0.9)
......@@ -1127,14 +1117,13 @@ DEPENDENCIES
omniauth-google-oauth2 (~> 0.5.2)
omniauth-kerberos (~> 0.3.0)
omniauth-oauth2-generic (~> 0.2.2)
omniauth-saml (~> 1.7.0)
omniauth-saml (~> 1.10)
omniauth-shibboleth (~> 1.2.0)
omniauth-twitter (~> 1.2.0)
omniauth_crowd (~> 2.2.0)
org-ruby (~> 0.9.12)
peek (~> 1.0.1)
peek-gc (~> 0.0.2)
peek-host (~> 1.0.0)
peek-mysql2 (~> 1.1.0)
peek-performance_bar (~> 1.3.0)
peek-pg (~> 1.3.0)
......@@ -1177,7 +1166,7 @@ DEPENDENCIES
rubocop (~> 0.52.1)
rubocop-rspec (~> 1.22.1)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.16.2)
ruby-prof (~> 0.17.0)
ruby_parser (~> 3.8)
rufus-scheduler (~> 3.4)
rugged (~> 0.26.0)
......@@ -1187,7 +1176,7 @@ DEPENDENCIES
seed-fu (~> 2.3.7)
select2-rails (~> 3.5.9)
selenium-webdriver (~> 3.5)
sentry-raven (~> 2.5.3)
sentry-raven (~> 2.7)
settingslogic (~> 2.0.9)
sham_rack (~> 1.3.6)
shoulda-matchers (~> 3.1.2)
......@@ -1205,7 +1194,7 @@ DEPENDENCIES
sprockets (~> 3.7.0)
sshkey (~> 1.9.0)
stackprof (~> 0.2.10)
state_machines-activerecord (~> 0.4.0)
state_machines-activerecord (~> 0.5.1)
sys-filesystem (~> 1.1.6)
test-prof (~> 0.2.5)
test_after_commit (~> 1.1)
......
......@@ -33,7 +33,7 @@ export default class VariableList {
selector: '.js-ci-variable-input-key',
default: '',
},
value: {
secret_value: {
selector: '.js-ci-variable-input-value',
default: '',
},
......@@ -105,7 +105,7 @@ export default class VariableList {
setupToggleButtons($row[0]);
// Reset the resizable textarea
$row.find(this.inputMap.value.selector).css('height', '');
$row.find(this.inputMap.secret_value.selector).css('height', '');
const $environmentSelect = $row.find('.js-variable-environment-toggle');
if ($environmentSelect.length) {
......
// ECMAScript polyfills
import 'core-js/fn/array/fill';
import 'core-js/fn/array/find';
import 'core-js/fn/array/find-index';
import 'core-js/fn/array/from';
......
......@@ -65,6 +65,10 @@ export default class Editor {
(this.instance = this.monaco.editor.createDiffEditor(domElement, {
...defaultEditorOptions,
readOnly: true,
quickSuggestions: false,
occurrencesHighlight: false,
renderLineHighlight: 'none',
hideCursorInOverviewRuler: true,
})),
);
......
import initSettingsPanels from '~/settings_panels';
document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels
initSettingsPanels();
});
......@@ -290,6 +290,10 @@
.margin-view-overlays .delete-sign {
opacity: 0.4;
}
.cursors-layer {
display: none;
}
}
}
......
......@@ -39,7 +39,7 @@ module Groups
end
def variable_params_attributes
%i[id key value protected _destroy]
%i[id key secret_value protected _destroy]
end
def authorize_admin_build!
......
......@@ -92,7 +92,7 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController
def schedule_params
params.require(:schedule)
.permit(:description, :cron, :cron_timezone, :ref, :active,
variables_attributes: [:id, :key, :value, :_destroy] )
variables_attributes: [:id, :key, :secret_value, :_destroy] )
end
def authorize_play_pipeline_schedule!
......
......@@ -38,6 +38,6 @@ class Projects::VariablesController < Projects::ApplicationController
end
def variable_params_attributes
%i[id key value protected _destroy]
%i[id key secret_value protected _destroy]
end
end
......@@ -6,6 +6,8 @@ module Ci
belongs_to :group
alias_attribute :secret_value, :value
validates :key, uniqueness: {
scope: :group_id,
message: "(%{value}) has already been taken"
......
......@@ -6,6 +6,7 @@ module Ci
include AfterCommitQueue
include Presentable
include Gitlab::OptimisticLocking
include Gitlab::Utils::StrongMemoize
prepend ::EE::Ci::Pipeline
......@@ -375,21 +376,23 @@ module Ci
def stage_seeds
return [] unless config_processor
@stage_seeds ||= config_processor.stage_seeds(self)
strong_memoize(:stage_seeds) do
seeds = config_processor.stages_attributes.map do |attributes|
Gitlab::Ci::Pipeline::Seed::Stage.new(self, attributes)
end
seeds.select(&:included?)
end
end
def seeds_size
@seeds_size ||= stage_seeds.sum(&:size)
stage_seeds.sum(&:size)
end
def has_kubernetes_active?
project.deployment_platform&.active?
end
def has_stage_seeds?
stage_seeds.any?
end
def has_warnings?
builds.latest.failed_but_allowed.any?
end
......@@ -402,6 +405,9 @@ module Ci
end
end
##
# TODO, setting yaml_errors should be moved to the pipeline creation chain.
#
def config_processor
return unless ci_yaml_file
return @config_processor if defined?(@config_processor)
......@@ -490,6 +496,14 @@ module Ci
end
end
def protected_ref?
strong_memoize(:protected_ref) { project.protected_for?(ref) }
end
def legacy_trigger
strong_memoize(:legacy_trigger) { trigger_requests.first }
end
def predefined_variables
Gitlab::Ci::Variables::Collection.new
.append(key: 'CI_PIPELINE_ID', value: id.to_s)
......
......@@ -5,6 +5,8 @@ module Ci
belongs_to :pipeline_schedule
alias_attribute :secret_value, :value
validates :key, uniqueness: { scope: :pipeline_schedule_id }
end
end
......@@ -7,6 +7,8 @@ module Ci
belongs_to :project
alias_attribute :secret_value, :value
validates :key, uniqueness: {
scope: [:project_id, :environment_scope],
message: "(%{value}) has already been taken"
......
......@@ -11,6 +11,7 @@ module Clusters
Applications::Prometheus.application_name => Applications::Prometheus,
Applications::Runner.application_name => Applications::Runner
}.freeze
DEFAULT_ENVIRONMENT = '*'.freeze
belongs_to :user
......@@ -52,6 +53,7 @@ module Clusters
scope :enabled, -> { where(enabled: true) }
scope :disabled, -> { where(enabled: false) }
scope :default_environment, -> { where(environment_scope: DEFAULT_ENVIRONMENT) }
def status_name
if provider
......
module DeploymentPlatform
# EE would override this and utilize the extra argument
# EE would override this and utilize environment argument
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def deployment_platform(environment: nil)
@deployment_platform ||=
find_cluster_platform_kubernetes ||
find_kubernetes_service_integration ||
build_cluster_and_deployment_platform
@deployment_platform ||= {}
@deployment_platform[environment] ||= find_deployment_platform(environment)
end
private
def find_cluster_platform_kubernetes
clusters.find_by(enabled: true)&.platform_kubernetes
def find_deployment_platform(environment)
find_cluster_platform_kubernetes(environment: environment) ||
find_kubernetes_service_integration ||
build_cluster_and_deployment_platform
end
# EE would override this and utilize environment argument
def find_cluster_platform_kubernetes(environment: nil)
clusters.enabled.default_environment
.last&.platform_kubernetes
end
def find_kubernetes_service_integration
......
......@@ -52,12 +52,12 @@ class Event < ActiveRecord::Base
belongs_to :target, -> {
# If the association for "target" defines an "author" association we want to
# eager-load this so Banzai & friends don't end up performing N+1 queries to
# get the authors of notes, issues, etc.
if reflections['events'].active_record.reflect_on_association(:author)
includes(:author)
else
self
# get the authors of notes, issues, etc. (likewise for "noteable").
incs = %i(author noteable).select do |a|
reflections['events'].active_record.reflect_on_association(a)
end
incs.reduce(self) { |obj, a| obj.includes(a) }
}, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
has_one :push_event_payload
......
......@@ -1107,7 +1107,9 @@ class MergeRequest < ActiveRecord::Base
end
def base_pipeline
@base_pipeline ||= project.pipelines.find_by(sha: merge_request_diff&.base_commit_sha)
@base_pipeline ||= project.pipelines
.order(id: :desc)
.find_by(sha: diff_base_sha)
end
def update_project_counter_caches
......
......@@ -9,6 +9,7 @@ module Ci
Gitlab::Ci::Pipeline::Chain::Validate::Config,
Gitlab::Ci::Pipeline::Chain::Skip,
EE::Gitlab::Ci::Pipeline::Chain::Limit::Size,
Gitlab::Ci::Pipeline::Chain::Populate,
Gitlab::Ci::Pipeline::Chain::Create,
EE::Gitlab::Ci::Pipeline::Chain::Limit::Activity].freeze
......
module Ci
class CreatePipelineStagesService < BaseService
def execute(pipeline)
pipeline.stage_seeds.each do |seed|
seed.user = current_user
seed.create! do |build|
##
# Create the environment before the build starts. This sets its slug and
# makes it available as an environment variable
#
if build.has_environment?
environment_name = build.expanded_environment_name
project.environments.find_or_create_by(name: environment_name)
end
end
end
end
end
end
......@@ -18,8 +18,8 @@ module Ci
pipeline = Ci::CreatePipelineService.new(project, trigger.owner, ref: params[:ref])
.execute(:trigger, ignore_skip_ci: true) do |pipeline|
pipeline.trigger_requests.create!(trigger: trigger)
create_pipeline_variables!(pipeline)
pipeline.trigger_requests.build(trigger: trigger)
pipeline.variables.build(variables)
end
if pipeline.persisted?
......@@ -37,13 +37,14 @@ module Ci
pipeline = Ci::CreatePipelineService.new(project, job.user, ref: params[:ref])
.execute(:pipeline, ignore_skip_ci: true) do |pipeline|
job.sourced_pipelines.create!(
source = job.sourced_pipelines.build(
source_pipeline: job.pipeline,
source_project: job.project,
pipeline: pipeline,
project: project)
create_pipeline_variables!(pipeline)
pipeline.source_pipeline = source
pipeline.variables.build(variables)
end
if pipeline.persisted?
......@@ -60,19 +61,15 @@ module Ci
end
def job_from_token
return @job if defined?(@job)
@job = Ci::Build.find_by_token(params[:token].to_s)
strong_memoize(:job) do
Ci::Build.find_by_token(params[:token].to_s)
end
end
def create_pipeline_variables!(pipeline)
return unless params[:variables]
variables = params[:variables].map do |key, value|
def variables
params[:variables].to_h.map do |key, value|
{ key: key, value: value }
end
pipeline.variables.create!(variables)
end
end
end
= form_for @application_setting, url: admin_application_settings_path, html: { class: 'form-horizontal fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :gravatar_enabled do
= f.check_box :gravatar_enabled
Gravatar enabled
.form-group
= f.label :default_projects_limit, class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :default_projects_limit, class: 'form-control'
.form-group
= f.label :max_attachment_size, 'Maximum attachment size (MB)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :max_attachment_size, class: 'form-control'
= render 'repository_size_limit_setting', form: f
.form-group
= f.label :session_expire_delay, 'Session duration (minutes)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :session_expire_delay, class: 'form-control'
%span.help-block#session_expire_delay_help_block GitLab restart is required to apply changes
.form-group
= f.label :user_oauth_applications, 'User OAuth applications', class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :user_oauth_applications do
= f.check_box :user_oauth_applications
Allow users to register any application to use GitLab as an OAuth provider
.form-group
= f.label :user_default_external, 'New users set to external', class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :user_default_external do
= f.check_box :user_default_external
Newly registered users will by default be external
- if ::Gitlab.dev_env_or_com?
.form-group
= f.label :check_namespace_plan, 'Check feature availability on namespace plan', class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :check_namespace_plan do
= f.check_box :check_namespace_plan
Enabling this will only make licensed EE features available to projects if the project namespace's plan
includes the feature or if the project is public.
= f.submit 'Save changes', class: 'btn btn-success'
= form_for @application_setting, url: admin_application_settings_path, html: { class: 'form-horizontal fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
%legend Visibility and Access Controls
.form-group
= f.label :default_branch_protection, class: 'control-label col-sm-2'
.col-sm-10
= f.select :default_branch_protection, options_for_select(Gitlab::Access.protection_options, @application_setting.default_branch_protection), {}, class: 'form-control'
= render partial: 'admin/application_settings/ee/project_creation_level', locals: { form: f, application_setting: @application_setting }
.form-group.visibility-level-setting
= f.label :default_project_visibility, class: 'control-label col-sm-2'
.col-sm-10
= render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: Project.new)
.form-group.visibility-level-setting
= f.label :default_snippet_visibility, class: 'control-label col-sm-2'
.col-sm-10
= render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new)
.form-group.visibility-level-setting
= f.label :default_group_visibility, class: 'control-label col-sm-2'
.col-sm-10
= render('shared/visibility_radios', model_method: :default_group_visibility, form: f, selected_level: @application_setting.default_group_visibility, form_model: Group.new)
.form-group
= f.label :restricted_visibility_levels, class: 'control-label col-sm-2'
.col-sm-10
- checkbox_name = 'application_setting[restricted_visibility_levels][]'
= hidden_field_tag(checkbox_name)
- restricted_level_checkboxes('restricted-visibility-help', checkbox_name).each do |level|
.checkbox
= level
%span.help-block#restricted-visibility-help
Selected levels cannot be used by non-admin users for projects or snippets.
If the public level is restricted, user profiles are only visible to logged in users.
.form-group
= f.label :import_sources, class: 'control-label col-sm-2'
.col-sm-10
- import_sources_checkboxes('import-sources-help').each do |source|
.checkbox= source
%span.help-block#import-sources-help
Enabled sources for code import during project creation. OmniAuth must be configured for GitHub
= link_to "(?)", help_page_path("integration/github")
, Bitbucket
= link_to "(?)", help_page_path("integration/bitbucket")
and GitLab.com
= link_to "(?)", help_page_path("integration/gitlab")
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :project_export_enabled do
= f.check_box :project_export_enabled
Project export enabled
-# EE-only
- if ldap_enabled?
.form-group
= f.label :allow_group_owners_to_manage_ldap, 'LDAP settings', class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :allow_group_owners_to_manage_ldap do
= f.check_box :allow_group_owners_to_manage_ldap
Allow group owners to manage LDAP-related settings
%span.help-block
If checked, group owners can manage LDAP group links and LDAP member overrides
= link_to icon('question-circle'), help_page_path('administration/auth/ldap-ee')
.form-group
%label.control-label.col-sm-2 Enabled Git access protocols
.col-sm-10
= select(:application_setting, :enabled_git_access_protocol, [['Both SSH and HTTP(S)', nil], ['Only SSH', 'ssh'], ['Only HTTP(S)', 'http']], {}, class: 'form-control')
%span.help-block#clone-protocol-help
Allow only the selected protocols to be used for Git access.
- ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type|
- field_name = :"#{type}_key_restriction"
.form-group
= f.label field_name, "#{type.upcase} SSH keys", class: 'control-label col-sm-2'
.col-sm-10
= f.select field_name, key_restriction_options_for_select(type), {}, class: 'form-control'
%fieldset
%legend Account and Limit Settings
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :gravatar_enabled do
= f.check_box :gravatar_enabled
Gravatar enabled
.form-group
= f.label :default_projects_limit, class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :default_projects_limit, class: 'form-control'
.form-group
= f.label :max_attachment_size, 'Maximum attachment size (MB)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :max_attachment_size, class: 'form-control'
= render 'repository_size_limit_setting', form: f
.form-group
= f.label :session_expire_delay, 'Session duration (minutes)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :session_expire_delay, class: 'form-control'
%span.help-block#session_expire_delay_help_block GitLab restart is required to apply changes
.form-group
= f.label :user_oauth_applications, 'User OAuth applications', class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :user_oauth_applications do
= f.check_box :user_oauth_applications
Allow users to register any application to use GitLab as an OAuth provider
.form-group
= f.label :user_default_external, 'New users set to external', class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :user_default_external do
= f.check_box :user_default_external
Newly registered users will by default be external
- if ::Gitlab.dev_env_or_com?
.form-group
= f.label :check_namespace_plan, 'Check feature availability on namespace plan', class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :check_namespace_plan do
= f.check_box :check_namespace_plan
Enabling this will only make licensed EE features available to projects if the project namespace's plan
includes the feature or if the project is public.
- if License.feature_available?(:repository_mirrors)
= render partial: 'repository_mirrors_form', locals: { f: f }
%fieldset
%legend Sign-up Restrictions
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :signup_enabled do
= f.check_box :signup_enabled
Sign-up enabled
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :send_user_confirmation_email do
= f.check_box :send_user_confirmation_email
Send confirmation email on sign-up
.form-group
= f.label :domain_whitelist, 'Whitelisted domains for sign-ups', class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :domain_whitelist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8
.help-block ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com
.form-group
= f.label :domain_blacklist_enabled, 'Domain Blacklist', class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :domain_blacklist_enabled do
= f.check_box :domain_blacklist_enabled
Enable domain blacklist for sign ups
.form-group
.col-sm-offset-2.col-sm-10
.radio
= label_tag :blacklist_type_file do
= radio_button_tag :blacklist_type, :file
.option-title
Upload blacklist file
.radio
= label_tag :blacklist_type_raw do
= radio_button_tag :blacklist_type, :raw, @application_setting.domain_blacklist.present? || @application_setting.domain_blacklist.blank?
.option-title
Enter blacklist manually
.form-group.blacklist-file
= f.label :domain_blacklist_file, 'Blacklist file', class: 'control-label col-sm-2'
.col-sm-10
= f.file_field :domain_blacklist_file, class: 'form-control', accept: '.txt,.conf'
.help-block Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines or commas for multiple entries.
.form-group.blacklist-raw
= f.label :domain_blacklist, 'Blacklisted domains for sign-ups', class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :domain_blacklist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8
.help-block Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com
.form-group
= f.label :after_sign_up_text, class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :after_sign_up_text, class: 'form-control', rows: 4
.help-block Markdown enabled
%fieldset
%legend Sign-in Restrictions
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :password_authentication_enabled_for_web do
= f.check_box :password_authentication_enabled_for_web
Password authentication enabled for web interface
.help-block
When disabled, an external authentication provider must be used.
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :password_authentication_enabled_for_git do
= f.check_box :password_authentication_enabled_for_git
Password authentication enabled for Git over HTTP(S)
.help-block
When disabled, a Personal Access Token
- if Gitlab::Auth::LDAP::Config.enabled?
or LDAP password
must be used to authenticate.
- if omniauth_enabled? && button_based_providers.any?
.form-group
= f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth sign-in sources', class: 'control-label col-sm-2'
.col-sm-10
.btn-group{ data: { toggle: 'buttons' } }
- oauth_providers_checkboxes.each do |source|
= source
.form-group
= f.label :two_factor_authentication, 'Two-factor authentication', class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :require_two_factor_authentication do
= f.check_box :require_two_factor_authentication
Require all users to setup Two-factor authentication
.form-group
= f.label :two_factor_authentication, 'Two-factor grace period (hours)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :two_factor_grace_period, min: 0, class: 'form-control', placeholder: '0'
.help-block Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication
.form-group
= f.label :home_page_url, 'Home page URL', class: 'control-label col-sm-2'
.col-sm-10
= f.text_field :home_page_url, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'home_help_block'
%span.help-block#home_help_block We will redirect non-logged in users to this page
.form-group
= f.label :after_sign_out_path, class: 'control-label col-sm-2'
.col-sm-10
= f.text_field :after_sign_out_path, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'after_sign_out_path_help_block'
%span.help-block#after_sign_out_path_help_block We will redirect users to this page after they sign out
.form-group
= f.label :sign_in_text, class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :sign_in_text, class: 'form-control', rows: 4
.help-block Markdown enabled
%fieldset
%legend Help Page
.form-group
= f.label :help_text, class: 'control-label'
.col-sm-10
= f.text_area :help_text, class: 'form-control', rows: 4
.help-block Markdown enabled
.form-group
= f.label :help_page_text, class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :help_page_text, class: 'form-control', rows: 4
.help-block Markdown enabled
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :help_page_hide_commercial_content do
= f.check_box :help_page_hide_commercial_content
Hide marketing-related entries from help
.form-group
= f.label :help_page_support_url, 'Support page URL', class: 'control-label col-sm-2'
.col-sm-10
= f.text_field :help_page_support_url, class: 'form-control', placeholder: 'http://company.example.com/getting-help', :'aria-describedby' => 'support_help_block'
%span.help-block#support_help_block Alternate support URL for help page
%fieldset
%legend Pages
.form-group
= f.label :max_pages_size, 'Maximum size of pages (MB)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :max_pages_size, class: 'form-control'
.help-block 0 for unlimited
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :pages_domain_verification_enabled do
= f.check_box :pages_domain_verification_enabled
Require users to prove ownership of custom domains
.help-block
Domain verification is an essential security measure for public GitLab
sites. Users are required to demonstrate they control a domain before
it is enabled
= link_to icon('question-circle'), help_page_path('user/project/pages/getting_started_part_three.md', anchor: 'dns-txt-record')
%fieldset
%legend Continuous Integration and Deployment
.form-group
......
= form_for @application_setting, url: admin_application_settings_path, html: { class: 'form-horizontal fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
= f.label :help_text, class: 'control-label'
.col-sm-10
= f.text_area :help_text, class: 'form-control', rows: 4
.help-block Markdown enabled
.form-group
= f.label :help_page_text, class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :help_page_text, class: 'form-control', rows: 4
.help-block Markdown enabled
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :help_page_hide_commercial_content do
= f.check_box :help_page_hide_commercial_content
Hide marketing-related entries from help
.form-group
= f.label :help_page_support_url, 'Support page URL', class: 'control-label col-sm-2'
.col-sm-10
= f.text_field :help_page_support_url, class: 'form-control', placeholder: 'http://company.example.com/getting-help', :'aria-describedby' => 'support_help_block'
%span.help-block#support_help_block Alternate support URL for help page
= f.submit 'Save changes', class: "btn btn-success"
= form_for @application_setting, url: admin_application_settings_path, html: { class: 'form-horizontal fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
= f.label :max_pages_size, 'Maximum size of pages (MB)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :max_pages_size, class: 'form-control'
.help-block 0 for unlimited
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :pages_domain_verification_enabled do
= f.check_box :pages_domain_verification_enabled
Require users to prove ownership of custom domains
.help-block
Domain verification is an essential security measure for public GitLab
sites. Users are required to demonstrate they control a domain before
it is enabled
= link_to icon('question-circle'), help_page_path('user/project/pages/getting_started_part_three.md', anchor: 'dns-txt-record')
= f.submit 'Save changes', class: "btn btn-success"
= form_for @application_setting, url: admin_application_settings_path, html: { class: 'form-horizontal fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :password_authentication_enabled_for_web do
= f.check_box :password_authentication_enabled_for_web
Password authentication enabled for web interface
.help-block
When disabled, an external authentication provider must be used.
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :password_authentication_enabled_for_git do
= f.check_box :password_authentication_enabled_for_git
Password authentication enabled for Git over HTTP(S)
.help-block
When disabled, a Personal Access Token
- if Gitlab::Auth::LDAP::Config.enabled?
or LDAP password
must be used to authenticate.
- if omniauth_enabled? && button_based_providers.any?
.form-group
= f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth sign-in sources', class: 'control-label col-sm-2'
.col-sm-10
.btn-group{ data: { toggle: 'buttons' } }
- oauth_providers_checkboxes.each do |source|
= source
.form-group
= f.label :two_factor_authentication, 'Two-factor authentication', class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :require_two_factor_authentication do
= f.check_box :require_two_factor_authentication
Require all users to setup Two-factor authentication
.form-group
= f.label :two_factor_authentication, 'Two-factor grace period (hours)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :two_factor_grace_period, min: 0, class: 'form-control', placeholder: '0'
.help-block Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication
.form-group
= f.label :home_page_url, 'Home page URL', class: 'control-label col-sm-2'
.col-sm-10
= f.text_field :home_page_url, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'home_help_block'
%span.help-block#home_help_block We will redirect non-logged in users to this page
.form-group
= f.label :after_sign_out_path, class: 'control-label col-sm-2'
.col-sm-10
= f.text_field :after_sign_out_path, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'after_sign_out_path_help_block'
%span.help-block#after_sign_out_path_help_block We will redirect users to this page after they sign out
.form-group
= f.label :sign_in_text, class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :sign_in_text, class: 'form-control', rows: 4
.help-block Markdown enabled
= f.submit 'Save changes', class: "btn btn-success"
= form_for @application_setting, url: admin_application_settings_path, html: { class: 'form-horizontal fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :signup_enabled do
= f.check_box :signup_enabled
Sign-up enabled
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :send_user_confirmation_email do
= f.check_box :send_user_confirmation_email
Send confirmation email on sign-up
.form-group
= f.label :domain_whitelist, 'Whitelisted domains for sign-ups', class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :domain_whitelist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8
.help-block ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com
.form-group
= f.label :domain_blacklist_enabled, 'Domain Blacklist', class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :domain_blacklist_enabled do
= f.check_box :domain_blacklist_enabled
Enable domain blacklist for sign ups
.form-group
.col-sm-offset-2.col-sm-10
.radio
= label_tag :blacklist_type_file do
= radio_button_tag :blacklist_type, :file
.option-title
Upload blacklist file
.radio
= label_tag :blacklist_type_raw do
= radio_button_tag :blacklist_type, :raw, @application_setting.domain_blacklist.present? || @application_setting.domain_blacklist.blank?
.option-title
Enter blacklist manually
.form-group.blacklist-file
= f.label :domain_blacklist_file, 'Blacklist file', class: 'control-label col-sm-2'
.col-sm-10
= f.file_field :domain_blacklist_file, class: 'form-control', accept: '.txt,.conf'
.help-block Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines or commas for multiple entries.
.form-group.blacklist-raw
= f.label :domain_blacklist, 'Blacklisted domains for sign-ups', class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :domain_blacklist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8
.help-block Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com
.form-group
= f.label :after_sign_up_text, class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :after_sign_up_text, class: 'form-control', rows: 4
.help-block Markdown enabled
= f.submit 'Save changes', class: "btn btn-success"
= form_for @application_setting, url: admin_application_settings_path, html: { class: 'form-horizontal fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
= f.label :default_branch_protection, class: 'control-label col-sm-2'
.col-sm-10
= f.select :default_branch_protection, options_for_select(Gitlab::Access.protection_options, @application_setting.default_branch_protection), {}, class: 'form-control'
= render partial: 'admin/application_settings/ee/project_creation_level', locals: { form: f, application_setting: @application_setting }
.form-group.visibility-level-setting
= f.label :default_project_visibility, class: 'control-label col-sm-2'
.col-sm-10
= render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: Project.new)
.form-group.visibility-level-setting
= f.label :default_snippet_visibility, class: 'control-label col-sm-2'
.col-sm-10
= render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new)
.form-group.visibility-level-setting
= f.label :default_group_visibility, class: 'control-label col-sm-2'
.col-sm-10
= render('shared/visibility_radios', model_method: :default_group_visibility, form: f, selected_level: @application_setting.default_group_visibility, form_model: Group.new)
.form-group
= f.label :restricted_visibility_levels, class: 'control-label col-sm-2'
.col-sm-10
- checkbox_name = 'application_setting[restricted_visibility_levels][]'
= hidden_field_tag(checkbox_name)
- restricted_level_checkboxes('restricted-visibility-help', checkbox_name).each do |level|
.checkbox
= level
%span.help-block#restricted-visibility-help
Selected levels cannot be used by non-admin users for projects or snippets.
If the public level is restricted, user profiles are only visible to logged in users.
.form-group
= f.label :import_sources, class: 'control-label col-sm-2'
.col-sm-10
- import_sources_checkboxes('import-sources-help').each do |source|
.checkbox= source
%span.help-block#import-sources-help
Enabled sources for code import during project creation. OmniAuth must be configured for GitHub
= link_to "(?)", help_page_path("integration/github")
, Bitbucket
= link_to "(?)", help_page_path("integration/bitbucket")
and GitLab.com
= link_to "(?)", help_page_path("integration/gitlab")
.form-group
= f.label :default_branch_protection, class: 'control-label col-sm-2'
.col-sm-10
= f.select :default_branch_protection, options_for_select(Gitlab::Access.protection_options, @application_setting.default_branch_protection), {}, class: 'form-control'
-# EE-only
- if ldap_enabled?
.form-group
= f.label :allow_group_owners_to_manage_ldap, 'LDAP settings', class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :allow_group_owners_to_manage_ldap do
= f.check_box :allow_group_owners_to_manage_ldap
Allow group owners to manage LDAP-related settings
%span.help-block
If checked, group owners can manage LDAP group links and LDAP member overrides
= link_to icon('question-circle'), help_page_path('administration/auth/ldap-ee')
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :project_export_enabled do
= f.check_box :project_export_enabled
Project export enabled
.form-group
%label.control-label.col-sm-2 Enabled Git access protocols
.col-sm-10
= select(:application_setting, :enabled_git_access_protocol, [['Both SSH and HTTP(S)', nil], ['Only SSH', 'ssh'], ['Only HTTP(S)', 'http']], {}, class: 'form-control')
%span.help-block#clone-protocol-help
Allow only the selected protocols to be used for Git access.
- ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type|
- field_name = :"#{type}_key_restriction"
.form-group
= f.label field_name, "#{type.upcase} SSH keys", class: 'control-label col-sm-2'
.col-sm-10
= f.select field_name, key_restriction_options_for_select(type), {}, class: 'form-control'
= f.submit 'Save changes', class: "btn btn-success"
- breadcrumb_title "Settings"
- page_title "Settings"
- @content_class = "limit-container-width" unless fluid_layout
- expanded = Rails.env.test?
%h3.page-title Settings
%hr
= render 'form'
%section.settings.as-visibility-access.no-animate#js-visibility-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
= _('Visibility and access controls')
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p
= _('Set default and restrict visibility levels. Configure import sources and git access protocol.')
.settings-content
= render 'visibility_and_access'
%section.settings.as-account-limit.no-animate#js-account-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
= _('Account and limit settings')
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p
= _('Session expiration, projects limit and attachment size.')
.settings-content
= render 'account_and_limit'
%section.settings.as-signup.no-animate#js-signup-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
= _('Sign-up restrictions')
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p
= _('Configure the way a user creates a new account.')
.settings-content
= render 'signup'
%section.settings.as-signin.no-animate#js-signin-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
= _('Sign-in restrictions')
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p
= _('Set requirements for a user to sign-in. Enable mandatory two-factor authentication.')
.settings-content
= render 'signin'
%section.settings.as-help-page.no-animate#js-help-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
= _('Help page')
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p
= _('Help page text and support page url.')
.settings-content
= render 'help_page'
%section.settings.as-pages.no-animate#js-pages-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
= _('Pages')
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p
= _('Size and domain settings for static websites')
.settings-content
= render 'pages'
.prepend-top-20
= render 'form'
......@@ -10,7 +10,7 @@
- id_input_name = "#{form_field}[variables_attributes][][id]"
- destroy_input_name = "#{form_field}[variables_attributes][][_destroy]"
- key_input_name = "#{form_field}[variables_attributes][][key]"
- value_input_name = "#{form_field}[variables_attributes][][value]"
- value_input_name = "#{form_field}[variables_attributes][][secret_value]"
- protected_input_name = "#{form_field}[variables_attributes][][protected]"
%li.js-row.ci-variable-row{ data: { is_persisted: "#{!id.nil?}" } }
......
---
title: Use specific names for filtered CI variable controller parameters
merge_request: 17796
author:
type: other
---
title: Add documentation for runner IP address (#44232)
merge_request: 17837
author:
type: other
---
title: Fix 500 error when trying to resolve non-ASCII conflicts in the editor
merge_request: 17962
author:
type: fixed
---
title: Don't capture trailing punctuation when autolinking
merge_request: 17965
author:
type: fixed
---
title: Remove N+1 query for Noteable association.
merge_request: 17956
author:
type: performance
---
title: Add indexes for user activity queries.
merge_request: 17890
author:
type: performance
---
title: Bump `state_machines-activerecord` to 0.5.1
merge_request: 17924
author: blackst0ne
type: other
---
title: Prevent auto-retry AccessDenied error from stopping transition to failed
merge_request: 17862
author:
type: fixed
---
title: Bump rails-html-sanitizer to 1.0.4
merge_request:
author:
type: security
# Geo configuration (GitLab Omnibus)
>**Note:**
NOTE: **Note:**
This is the documentation for the Omnibus GitLab packages. For installations
from source, follow the [**Geo nodes configuration for installations
from source**][configuration-source] guide.
## Configuring a new secondary node
>**Note:**
NOTE: **Note:**
This is the final step in setting up a secondary Geo node. Stages of the
setup process must be completed in the documented order.
Before attempting the steps in this stage, [complete all prior stages][setup-geo-omnibus].
......@@ -20,9 +20,9 @@ You are encouraged to first read through all the steps before executing them
in your testing/production environment.
> **Notes:**
- **Do not** setup any custom authentication in the secondary nodes, this will be
> - **Do not** setup any custom authentication in the secondary nodes, this will be
handled by the primary node.
- Any change that requires access to the **Admin Area** needs to be done in the
> - Any change that requires access to the **Admin Area** needs to be done in the
primary node, as the secondary node is a read-only replica.
### Step 1. Manually replicate secret GitLab values
......
# Geo configuration (source)
>**Note:**
NOTE: **Note:**
This is the documentation for installations from source. For installations
using the Omnibus GitLab packages, follow the
[**Omnibus Geo nodes configuration**][configuration] guide.
## Configuring a new secondary node
>**Note:**
NOTE: **Note:**
This is the final step in setting up a secondary Geo node. Stages of the setup
process must be completed in the documented order. Before attempting the steps
in this stage, [complete all prior stages][setup-geo-source].
......@@ -20,7 +20,7 @@ You are encouraged to first read through all the steps before executing them
in your testing/production environment.
>**Notes:**
NOTE: **Notes:**
- **Do not** setup any custom authentication in the secondary nodes, this will be
handled by the primary node.
- **Do not** add anything in the secondaries Geo nodes admin area
......
# Geo database replication (GitLab Omnibus)
>**Note:**
NOTE: **Note:**
This is the documentation for the Omnibus GitLab packages. For installations
from source, follow the
[**database replication for installations from source**][database-source] guide.
>**Note:**
NOTE: **Note:**
If your GitLab installation uses external PostgreSQL, the Omnibus roles
will not be able to perform all necessary configuration steps. Refer to the
section on [External PostreSQL][external postgresql] for additional instructions.
>**Note:**
NOTE: **Note:**
The stages of the setup process must be completed in the documented order.
Before attempting the steps in this stage, [complete all prior stages][toc].
......@@ -28,7 +28,7 @@ The GitLab primary node where the write operations happen will connect to
the primary database server, and the secondary nodes which are read-only will
connect to the secondary database servers (which are also read-only).
>**Note:**
NOTE: **Note:**
In database documentation you may see "primary" being referenced as "master"
and "secondary" as either "slave" or "standby" server (read-only).
......@@ -261,7 +261,8 @@ The following guide assumes that:
gitlab-ctl stop sidekiq
```
> **Note**: This step is important so we don't try to execute anything before the node is fully configured.
NOTE: **Note**:
This step is important so we don't try to execute anything before the node is fully configured.
1. [Check TCP connectivity][rake-maintenance] to the primary's PostgreSQL server:
......@@ -269,7 +270,8 @@ The following guide assumes that:
gitlab-rake gitlab:tcp_check[1.2.3.4,5432]
```
> **Note**: If this step fails, you may be using the wrong IP address, or a firewall may
NOTE: **Note**:
If this step fails, you may be using the wrong IP address, or a firewall may
be preventing access to the server. Check the IP address, paying close
attention to the difference between public and private addresses and ensure
that, if a firewall is present, the secondary is permitted to connect to the
......@@ -368,7 +370,7 @@ The directories used are the defaults that are set up in Omnibus. If you have
changed any defaults or are using a source installation, configure it as you
see fit replacing the directories and paths.
>**Warning:**
CAUTION: **Warning:**
Make sure to run this on the **secondary** server as it removes all PostgreSQL's
data before running `pg_basebackup`.
......@@ -384,8 +386,8 @@ data before running `pg_basebackup`.
name as shown in the commands below.
1. Execute the command below to start a backup/restore and begin the replication
>**Warning:** Each Geo secondary must have its own unique replication slot name.
Using the same slot name between two secondaries will break PostgreSQL replication.
CAUTION: **Warning:** Each Geo secondary must have its own unique replication slot name.
Using the same slot name between two secondaries will break PostgreSQL replication.
```bash
gitlab-ctl replicate-geo-database --slot-name=secondary_example --host=1.2.3.4
......
# Geo database replication (source)
>**Note:**
NOTE: **Note:**
This is the documentation for installations from source. For installations
using the Omnibus GitLab packages, follow the
[**database replication for Omnibus GitLab**][database] guide.
>**Note:**
NOTE: **Note:**
The stages of the setup process must be completed in the documented order.
Before attempting the steps in this stage, [complete all prior stages][toc].
......@@ -22,7 +22,7 @@ The GitLab primary node where the write operations happen will connect to
primary database server, and the secondary ones which are read-only will
connect to secondary database servers (which are read-only too).
>**Note:**
NOTE: **Note:**
In many databases documentation you will see "primary" being referenced as "master"
and "secondary" as either "slave" or "standby" server (read-only).
......@@ -91,10 +91,11 @@ The following guide assumes that:
1. Set up TLS support for the PostgreSQL primary server
> **Warning**: Only skip this step if you **know** that PostgreSQL traffic
> between the primary and secondary will be secured through some other
> means, e.g., a known-safe physical network path or a site-to-site VPN that
> you have configured.
CAUTION: **Warning**:
Only skip this step if you **know** that PostgreSQL traffic
between the primary and secondary will be secured through some other
means, e.g., a known-safe physical network path or a site-to-site VPN that
you have configured.
If you are replicating your database across the open Internet, it is
**essential** that the connection is TLS-secured. Correctly configured, this
......@@ -141,6 +142,7 @@ The following guide assumes that:
hot_standby = on
```
NOTE: **Note**:
Be sure to set `max_replication_slots` to the number of Geo secondary
nodes that you may potentially have (at least 1).
......@@ -302,7 +304,7 @@ needed files for streaming replication.
The directories used are the defaults for Debian/Ubuntu. If you have changed
any defaults, configure it as you see fit replacing the directories and paths.
>**Warning:**
CAUTION: **Warning:**
Make sure to run this on the **secondary** server as it removes all PostgreSQL's
data before running `pg_basebackup`.
......
......@@ -102,9 +102,10 @@ in your SAML IdP:
installation to generate the correct value).
1. Change the values of `idp_cert_fingerprint`, `idp_sso_target_url`,
`name_identifier_format` to match your IdP. Check
`name_identifier_format` to match your IdP. If a fingerprint is used it must
be a SHA1 fingerprint; check
[the omniauth-saml documentation](https://github.com/omniauth/omniauth-saml)
for details on these options.
for more details on these options.
1. Change the value of `issuer` to a unique name, which will identify the application
to the IdP.
......@@ -362,6 +363,7 @@ need to be validated using a fingerprint, a certificate or a validator.
For this you need take the following into account:
- If a fingerprint is used, it must be the SHA1 fingerprint
- If no certificate is provided in the settings, a fingerprint or fingerprint
validator needs to be provided and the response from the server must contain
a certificate (`<ds:KeyInfo><ds:X509Data><ds:X509Certificate>`)
......
......@@ -2,15 +2,12 @@ module EE
module DeploymentPlatform
extend ::Gitlab::Utils::Override
override :deployment_platform
def deployment_platform(environment: nil)
override :find_cluster_platform_kubernetes
def find_cluster_platform_kubernetes(environment: nil)
return super unless environment && feature_available?(:multiple_clusters)
@deployment_platform ||= # rubocop:disable Gitlab/ModuleWithInstanceVariables
clusters.enabled.on_environment(environment.name)
.last&.platform_kubernetes
super # Wildcard or KubernetesService
clusters.enabled.on_environment(environment.name)
.last&.platform_kubernetes
end
end
end
......@@ -33,14 +33,15 @@ module Geo
return if registry.project.wiki_enabled?
registry.last_wiki_sync_failure = nil
registry.last_wiki_synced_at = DateTime.now
registry.last_wiki_successful_sync_at = DateTime.now
registry.resync_wiki = false
registry.wiki_retry_count = nil
registry.wiki_retry_at = nil
registry.force_to_redownload_wiki = false
if registry.changed?
if registry.changed? || registry.last_wiki_synced_at.nil? || registry.last_wiki_successful_sync_at.nil?
registry.last_wiki_synced_at = DateTime.now
registry.last_wiki_successful_sync_at = DateTime.now
success = registry.save
log_info("#{success ? 'Successfully marked' : 'Failed to mark'} disabled wiki as synced", registry_id: registry.id, project_id: registry.project_id)
end
......
---
title: Resolve "undefined method 'log_transfer_error'"
merge_request:
author:
type: fixed
---
title: Fixes incorrect assignation of cluster details
merge_request: 5047
author:
type: fixed
---
title: Hard failing a mirror no longer fails for a blocked user's personal project
merge_request: 5063
author:
type: fixed
---
title: 'Geo: Recovery from temporary directory doesn''t work if the namespace directory
doesn''t exist'
merge_request:
author:
type: fixed
---
title: Define a chat responder for the Slack app
merge_request:
author:
type: fixed
---
title: Geo - Avoid rescheduling the same project again in a backfill condition
merge_request: 5069
author:
type: fixed
---
title: Update Epic documentation to include labels
merge_request:
author:
type: other
---
title: Fix LDAP group sync permission override UI
merge_request: 5003
author:
type: fixed
---
title: Mark disabled wikis as fully synced
merge_request: 5104
author:
type: fixed
---
title: Also log Geo Prometheus metrics from primary
merge_request: 5058
author:
type: added
......@@ -55,24 +55,22 @@ module Gitlab
)
service.execute(:chat) do |pipeline|
create_environment_variables(pipeline)
create_chat_data(pipeline)
build_environment_variables(pipeline)
build_chat_data(pipeline)
end
end
# pipeline - The `Ci::Pipeline` to create the environment variables for.
def create_environment_variables(pipeline)
pipeline.variables.create!(
[
{ key: 'CHAT_INPUT', value: arguments },
{ key: 'CHAT_CHANNEL', value: channel }
]
def build_environment_variables(pipeline)
pipeline.variables.build(
[{ key: 'CHAT_INPUT', value: arguments },
{ key: 'CHAT_CHANNEL', value: channel }]
)
end
# pipeline - The `Ci::Pipeline` to create the chat data for.
def create_chat_data(pipeline)
pipeline.create_chat_data!(
def build_chat_data(pipeline)
pipeline.build_chat_data(
chat_name_id: chat_name.id,
response_url: response_url
)
......
......@@ -19,13 +19,13 @@ describe Projects::VariablesController do
let(:variable_attributes) do
{ id: variable.id,
key: variable.key,
value: variable.value,
secret_value: variable.value,
protected: variable.protected?.to_s,
environment_scope: variable.environment_scope }
end
let(:new_variable_attributes) do
{ key: 'new_key',
value: 'dummy_value',
secret_value: 'dummy_value',
protected: 'false',
environment_scope: 'new_scope' }
end
......
......@@ -17,7 +17,7 @@ describe 'Project show page', :feature do
it '"Kubernetes cluster" button linked to clusters page' do
create(:cluster, :provided_by_gcp, projects: [project])
create(:cluster, :provided_by_gcp, projects: [project])
create(:cluster, :provided_by_gcp, :production_environment, projects: [project])
visit project_path(project)
......
......@@ -4,56 +4,56 @@ describe EE::DeploymentPlatform do
describe '#deployment_platform' do
let(:project) { create(:project) }
context 'when environment is specified' do
let(:environment) { create(:environment, project: project, name: 'review/name') }
let!(:default_cluster) { create(:cluster, :provided_by_user, projects: [project], environment_scope: '*') }
let!(:cluster) { create(:cluster, :provided_by_user, environment_scope: 'review/*', projects: [project]) }
subject { project.deployment_platform(environment: environment) }
shared_examples 'matching environment scope' do
context 'when multiple clusters is available' do
before do
stub_licensed_features(multiple_clusters: true)
end
shared_examples 'matching environment scope' do
context 'when multiple clusters license is available' do
before do
stub_licensed_features(multiple_clusters: true)
end
it 'returns environment specific cluster' do
is_expected.to eq(cluster.platform_kubernetes)
end
it 'returns environment specific cluster' do
is_expected.to eq(cluster.platform_kubernetes)
end
end
context 'when multiple clusters is unavailable' do
before do
stub_licensed_features(multiple_clusters: false)
end
context 'when multiple clusters licence is unavailable' do
before do
stub_licensed_features(multiple_clusters: false)
end
it 'returns a kubernetes platform' do
is_expected.to be_kind_of(Clusters::Platforms::Kubernetes)
end
it 'returns a kubernetes platform' do
is_expected.to be_kind_of(Clusters::Platforms::Kubernetes)
end
end
end
shared_examples 'not matching environment scope' do
context 'when multiple clusters is available' do
before do
stub_licensed_features(multiple_clusters: true)
end
shared_examples 'not matching environment scope' do
context 'when multiple clusters license is available' do
before do
stub_licensed_features(multiple_clusters: true)
end
it 'returns default cluster' do
is_expected.to eq(default_cluster.platform_kubernetes)
end
it 'returns default cluster' do
is_expected.to eq(default_cluster.platform_kubernetes)
end
end
context 'when multiple clusters is unavailable' do
before do
stub_licensed_features(multiple_clusters: false)
end
context 'when multiple clusters license is unavailable' do
before do
stub_licensed_features(multiple_clusters: false)
end
it 'returns a kubernetes platform' do
is_expected.to be_kind_of(Clusters::Platforms::Kubernetes)
end
it 'returns a kubernetes platform' do
is_expected.to be_kind_of(Clusters::Platforms::Kubernetes)
end
end
end
context 'when environment is specified' do
let!(:default_cluster) { create(:cluster, :provided_by_user, projects: [project], environment_scope: '*') }
let!(:cluster) { create(:cluster, :provided_by_user, environment_scope: 'review/*', projects: [project]) }
let(:environment) { create(:environment, project: project, name: 'review/name') }
subject { project.deployment_platform(environment: environment) }
context 'when environment scope is exactly matched' do
before do
......@@ -133,5 +133,21 @@ describe EE::DeploymentPlatform do
end
end
end
context 'with multiple clusters and multiple environments' do
let!(:cluster_1) { create(:cluster, :provided_by_user, projects: [project], environment_scope: 'staging/*') }
let!(:cluster_2) { create(:cluster, :provided_by_user, projects: [project], environment_scope: 'test/*') }
let(:environment_1) { create(:environment, project: project, name: 'staging/name') }
let(:environment_2) { create(:environment, project: project, name: 'test/name') }
before do
stub_licensed_features(multiple_clusters: true)
end
it 'should return the appropriate cluster' do
expect(project.deployment_platform(environment: environment_1)).to eq(cluster_1.platform_kubernetes)
expect(project.deployment_platform(environment: environment_2)).to eq(cluster_2.platform_kubernetes)
end
end
end
end
......@@ -9,11 +9,16 @@ module Gitlab
::Ci::Pipeline.transaction do
pipeline.save!
@command.seeds_block&.call(pipeline)
::Ci::CreatePipelineStagesService
.new(project, current_user)
.execute(pipeline)
##
# Create environments before the pipeline starts.
#
pipeline.builds.each do |build|
if build.has_environment?
project.environments.find_or_create_by(
name: build.expanded_environment_name
)
end
end
end
rescue ActiveRecord::RecordInvalid => e
error("Failed to persist the pipeline: #{e}")
......
module Gitlab
module Ci
module Pipeline
module Chain
class Populate < Chain::Base
include Chain::Helpers
PopulateError = Class.new(StandardError)
def perform!
##
# Populate pipeline with block argument of CreatePipelineService#execute.
#
@command.seeds_block&.call(pipeline)
##
# Populate pipeline with all stages and builds from pipeline seeds.
#
pipeline.stage_seeds.each do |stage|
stage.user = current_user
pipeline.stages << stage.to_resource
stage.seeds.each do |build|
pipeline.builds << build.to_resource
end
end
if pipeline.stages.none?
return error('No stages / jobs for this pipeline.')
end
if pipeline.invalid?
return error('Failed to build the pipeline!')
end
raise Populate::PopulateError if pipeline.persisted?
end
def break?
pipeline.errors.any?
end
end
end
end
end
end
......@@ -16,11 +16,7 @@ module Gitlab
@pipeline.drop!(:config_error)
end
return error(@pipeline.yaml_errors)
end
unless @pipeline.has_stage_seeds?
return error('No stages / jobs for this pipeline.')
error(@pipeline.yaml_errors)
end
end
......
module Gitlab
module Ci
module Pipeline
module Seed
class Base
def attributes
raise NotImplementedError
end
def included?
raise NotImplementedError
end
def to_resource
raise NotImplementedError
end
end
end
end
end
end
module Gitlab
module Ci
module Pipeline
module Seed
class Build < Seed::Base
include Gitlab::Utils::StrongMemoize
delegate :dig, to: :@attributes
def initialize(pipeline, attributes)
@pipeline = pipeline
@attributes = attributes
@only = attributes.delete(:only)
@except = attributes.delete(:except)
end
def user=(current_user)
@attributes.merge!(user: current_user)
end
def included?
strong_memoize(:inclusion) do
only_specs = Gitlab::Ci::Build::Policy.fabricate(@only)
except_specs = Gitlab::Ci::Build::Policy.fabricate(@except)
only_specs.all? { |spec| spec.satisfied_by?(@pipeline) } &&
except_specs.none? { |spec| spec.satisfied_by?(@pipeline) }
end
end
def attributes
@attributes.merge(
pipeline: @pipeline,
project: @pipeline.project,
ref: @pipeline.ref,
tag: @pipeline.tag,
trigger_request: @pipeline.legacy_trigger,
protected: @pipeline.protected_ref?
)
end
def to_resource
strong_memoize(:resource) do
::Ci::Build.new(attributes)
end
end
end
end
end
end
end
module Gitlab
module Ci
module Pipeline
module Seed
class Stage < Seed::Base
include Gitlab::Utils::StrongMemoize
delegate :size, to: :seeds
delegate :dig, to: :seeds
def initialize(pipeline, attributes)
@pipeline = pipeline
@attributes = attributes
@builds = attributes.fetch(:builds).map do |attributes|
Seed::Build.new(@pipeline, attributes)
end
end
def user=(current_user)
@builds.each { |seed| seed.user = current_user }
end
def attributes
{ name: @attributes.fetch(:name),
pipeline: @pipeline,
project: @pipeline.project }
end
def seeds
strong_memoize(:seeds) do
@builds.select(&:included?)
end
end
def included?
seeds.any?
end
def to_resource
strong_memoize(:stage) do
::Ci::Stage.new(attributes).tap do |stage|
seeds.each { |seed| stage.builds << seed.to_resource }
end
end
end
end
end
end
end
end
module Gitlab
module Ci
module Stage
class Seed
include ::Gitlab::Utils::StrongMemoize
attr_reader :pipeline
delegate :project, to: :pipeline
delegate :size, to: :@jobs
def initialize(pipeline, stage, jobs)
@pipeline = pipeline
@stage = { name: stage }
@jobs = jobs.to_a.dup
end
def user=(current_user)
@jobs.map! do |attributes|
attributes.merge(user: current_user)
end
end
def stage
@stage.merge(project: project)
end
def builds
trigger = pipeline.trigger_requests.first
@jobs.map do |attributes|
attributes.merge(project: project,
ref: pipeline.ref,
tag: pipeline.tag,
trigger_request: trigger,
protected: protected_ref?)
end
end
def create!
pipeline.stages.create!(stage).tap do |stage|
builds_attributes = builds.map do |attributes|
attributes.merge(stage_id: stage.id)
end
pipeline.builds.create!(builds_attributes).each do |build|
yield build if block_given?
end
end
end
private
def protected_ref?
strong_memoize(:protected_ref) do
project.protected_for?(pipeline.ref)
end
end
end
end
end
end
......@@ -27,7 +27,7 @@ module Gitlab
end
def build_attributes(name)
job = @jobs[name.to_sym] || {}
job = @jobs.fetch(name.to_sym, {})
{ stage_idx: @stages.index(job[:stage]),
stage: job[:stage],
......@@ -53,30 +53,24 @@ module Gitlab
}.compact }
end
def pipeline_stage_builds(stage, pipeline)
selected_jobs = @jobs.select do |_, job|
next unless job[:stage] == stage
only_specs = Gitlab::Ci::Build::Policy
.fabricate(job.fetch(:only, {}))
except_specs = Gitlab::Ci::Build::Policy
.fabricate(job.fetch(:except, {}))
only_specs.all? { |spec| spec.satisfied_by?(pipeline) } &&
except_specs.none? { |spec| spec.satisfied_by?(pipeline) }
end
selected_jobs.map { |_, job| build_attributes(job[:name]) }
def stage_builds_attributes(stage)
@jobs.values
.select { |job| job[:stage] == stage }
.map { |job| build_attributes(job[:name]) }
end
def stage_seeds(pipeline)
seeds = @stages.uniq.map do |stage|
builds = pipeline_stage_builds(stage, pipeline)
def stages_attributes
@stages.uniq.map do |stage|
seeds = stage_builds_attributes(stage).map do |attributes|
job = @jobs.fetch(attributes[:name].to_sym)
Gitlab::Ci::Stage::Seed.new(pipeline, stage, builds) if builds.any?
end
attributes
.merge(only: job.fetch(:only, {}))
.merge(except: job.fetch(:except, {}))
end
seeds.compact
{ name: stage, index: @stages.index(stage), builds: seeds }
end
end
def self.validation_message(content, opts = {})
......
......@@ -18,7 +18,7 @@ describe Projects::ClustersController do
context 'when project has one or more clusters' do
let(:project) { create(:project) }
let!(:enabled_cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
let!(:disabled_cluster) { create(:cluster, :disabled, :provided_by_gcp, projects: [project]) }
let!(:disabled_cluster) { create(:cluster, :disabled, :provided_by_gcp, :production_environment, projects: [project]) }
it 'lists available clusters' do
go
......@@ -32,7 +32,7 @@ describe Projects::ClustersController do
before do
allow(Clusters::Cluster).to receive(:paginates_per).and_return(1)
create_list(:cluster, 2, :provided_by_gcp, projects: [project])
create_list(:cluster, 2, :provided_by_gcp, :production_environment, projects: [project])
get :index, namespace_id: project.namespace, project_id: project, page: last_page
end
......@@ -420,7 +420,7 @@ describe Projects::ClustersController do
context 'when cluster is provided by GCP' do
context 'when cluster is created' do
let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
let!(:cluster) { create(:cluster, :provided_by_gcp, :production_environment, projects: [project]) }
it "destroys and redirects back to clusters list" do
expect { go }
......@@ -434,7 +434,7 @@ describe Projects::ClustersController do
end
context 'when cluster is being created' do
let!(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) }
let!(:cluster) { create(:cluster, :providing_by_gcp, :production_environment, projects: [project]) }
it "destroys and redirects back to clusters list" do
expect { go }
......@@ -448,7 +448,7 @@ describe Projects::ClustersController do
end
context 'when cluster is provided by user' do
let!(:cluster) { create(:cluster, :provided_by_user, projects: [project]) }
let!(:cluster) { create(:cluster, :provided_by_user, :production_environment, projects: [project]) }
it "destroys and redirects back to clusters list" do
expect { go }
......@@ -463,7 +463,7 @@ describe Projects::ClustersController do
end
describe 'security' do
set(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
set(:cluster) { create(:cluster, :provided_by_gcp, :production_environment, projects: [project]) }
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
......
......@@ -80,7 +80,7 @@ describe Projects::PipelineSchedulesController do
context 'when variables_attributes has one variable' do
let(:schedule) do
basic_param.merge({
variables_attributes: [{ key: 'AAA', value: 'AAA123' }]
variables_attributes: [{ key: 'AAA', secret_value: 'AAA123' }]
})
end
......@@ -101,7 +101,8 @@ describe Projects::PipelineSchedulesController do
context 'when variables_attributes has two variables and duplicated' do
let(:schedule) do
basic_param.merge({
variables_attributes: [{ key: 'AAA', value: 'AAA123' }, { key: 'AAA', value: 'BBB123' }]
variables_attributes: [{ key: 'AAA', secret_value: 'AAA123' },
{ key: 'AAA', secret_value: 'BBB123' }]
})
end
......@@ -152,7 +153,7 @@ describe Projects::PipelineSchedulesController do
context 'when params include one variable' do
let(:schedule) do
basic_param.merge({
variables_attributes: [{ key: 'AAA', value: 'AAA123' }]
variables_attributes: [{ key: 'AAA', secret_value: 'AAA123' }]
})
end
......@@ -169,7 +170,8 @@ describe Projects::PipelineSchedulesController do
context 'when params include two duplicated variables' do
let(:schedule) do
basic_param.merge({
variables_attributes: [{ key: 'AAA', value: 'AAA123' }, { key: 'AAA', value: 'BBB123' }]
variables_attributes: [{ key: 'AAA', secret_value: 'AAA123' },
{ key: 'AAA', secret_value: 'BBB123' }]
})
end
......@@ -194,7 +196,7 @@ describe Projects::PipelineSchedulesController do
context 'when adds a new variable' do
let(:schedule) do
basic_param.merge({
variables_attributes: [{ key: 'AAA', value: 'AAA123' }]
variables_attributes: [{ key: 'AAA', secret_value: 'AAA123' }]
})
end
......@@ -209,7 +211,7 @@ describe Projects::PipelineSchedulesController do
context 'when adds a new duplicated variable' do
let(:schedule) do
basic_param.merge({
variables_attributes: [{ key: 'CCC', value: 'AAA123' }]
variables_attributes: [{ key: 'CCC', secret_value: 'AAA123' }]
})
end
......@@ -224,7 +226,7 @@ describe Projects::PipelineSchedulesController do
context 'when updates a variable' do
let(:schedule) do
basic_param.merge({
variables_attributes: [{ id: pipeline_schedule_variable.id, value: 'new_value' }]
variables_attributes: [{ id: pipeline_schedule_variable.id, secret_value: 'new_value' }]
})
end
......@@ -252,7 +254,7 @@ describe Projects::PipelineSchedulesController do
let(:schedule) do
basic_param.merge({
variables_attributes: [{ id: pipeline_schedule_variable.id, _destroy: true },
{ key: 'CCC', value: 'CCC123' }]
{ key: 'CCC', secret_value: 'CCC123' }]
})
end
......
......@@ -2,11 +2,10 @@ FactoryBot.define do
factory :cluster, class: Clusters::Cluster do
user
name 'test-cluster'
sequence(:environment_scope) { |n| "production#{n}/*" }
trait :project do
before(:create) do |cluster, evaluator|
cluster.projects << create(:project)
cluster.projects << create(:project, :repository)
end
end
......@@ -33,5 +32,9 @@ FactoryBot.define do
trait :disabled do
enabled false
end
trait :production_environment do
sequence(:environment_scope) { |n| "production#{n}/*" }
end
end
end
......@@ -55,14 +55,19 @@ feature 'Admin disables Git access protocol' do
end
def disable_http_protocol
visit admin_application_settings_path
find('#application_setting_enabled_git_access_protocol').find(:xpath, 'option[2]').select_option
click_on 'Save'
switch_git_protocol(2)
end
def disable_ssh_protocol
switch_git_protocol(3)
end
def switch_git_protocol(value)
visit admin_application_settings_path
find('#application_setting_enabled_git_access_protocol').find(:xpath, 'option[3]').select_option
click_on 'Save'
page.within('.as-visibility-access') do
find('#application_setting_enabled_git_access_protocol').find(:xpath, "option[#{value}]").select_option
click_on 'Save'
end
end
end
......@@ -10,18 +10,21 @@ feature 'Admin updates settings' do
end
scenario 'Change visibility settings' do
choose "application_setting_default_project_visibility_20"
click_button 'Save'
page.within('.as-visibility-access') do
choose "application_setting_default_project_visibility_20"
click_button 'Save changes'
end
expect(page).to have_content "Application settings saved successfully"
end
scenario 'Uncheck all restricted visibility levels' do
find('#application_setting_visibility_level_0').set(false)
find('#application_setting_visibility_level_10').set(false)
find('#application_setting_visibility_level_20').set(false)
click_button 'Save'
page.within('.as-visibility-access') do
find('#application_setting_visibility_level_0').set(false)
find('#application_setting_visibility_level_10').set(false)
find('#application_setting_visibility_level_20').set(false)
click_button 'Save changes'
end
expect(page).to have_content "Application settings saved successfully"
expect(find('#application_setting_visibility_level_0')).not_to be_checked
......@@ -35,8 +38,10 @@ feature 'Admin updates settings' do
allow(Gitlab::Auth::LDAP::Config).to receive(:enabled?).and_return(true)
visit admin_application_settings_path
find('#application_setting_allow_group_owners_to_manage_ldap').set(false)
click_button 'Save'
page.within('.as-visibility-access') do
find('#application_setting_allow_group_owners_to_manage_ldap').set(false)
click_button 'Save'
end
expect(page).to have_content('Application settings saved successfully')
expect(find('#application_setting_allow_group_owners_to_manage_ldap')).not_to be_checked
......@@ -52,21 +57,59 @@ feature 'Admin updates settings' do
end
end
scenario 'Change application settings' do
uncheck 'Gravatar enabled'
fill_in 'Home page URL', with: 'https://about.gitlab.com/'
fill_in 'Help page text', with: 'Example text'
check 'Hide marketing-related entries from help'
fill_in 'Support page URL', with: 'http://example.com/help'
uncheck 'Project export enabled'
click_button 'Save'
scenario 'Change Visibility and Access Controls' do
page.within('.as-visibility-access') do
uncheck 'Project export enabled'
click_button 'Save changes'
end
expect(Gitlab::CurrentSettings.project_export_enabled).to be_falsey
expect(page).to have_content "Application settings saved successfully"
end
scenario 'Change Account and Limit Settings' do
page.within('.as-account-limit') do
uncheck 'Gravatar enabled'
click_button 'Save changes'
end
expect(Gitlab::CurrentSettings.gravatar_enabled).to be_falsey
expect(page).to have_content "Application settings saved successfully"
end
scenario 'Change Sign-in restrictions' do
page.within('.as-signin') do
fill_in 'Home page URL', with: 'https://about.gitlab.com/'
click_button 'Save changes'
end
expect(Gitlab::CurrentSettings.home_page_url).to eq "https://about.gitlab.com/"
expect(page).to have_content "Application settings saved successfully"
end
scenario 'Change Help page' do
page.within('.as-help-page') do
fill_in 'Help page text', with: 'Example text'
check 'Hide marketing-related entries from help'
fill_in 'Support page URL', with: 'http://example.com/help'
click_button 'Save changes'
end
expect(Gitlab::CurrentSettings.help_page_text).to eq "Example text"
expect(Gitlab::CurrentSettings.help_page_hide_commercial_content).to be_truthy
expect(Gitlab::CurrentSettings.help_page_support_url).to eq "http://example.com/help"
expect(Gitlab::CurrentSettings.project_export_enabled).to be_falsey
expect(page).to have_content "Application settings saved successfully"
end
scenario 'Change Pages settings' do
page.within('.as-pages') do
fill_in 'Maximum size of pages (MB)', with: 15
check 'Require users to prove ownership of custom domains'
click_button 'Save changes'
end
expect(Gitlab::CurrentSettings.max_pages_size).to eq 15
expect(Gitlab::CurrentSettings.pages_domain_verification_enabled?).to be_truthy
expect(page).to have_content "Application settings saved successfully"
end
......@@ -106,18 +149,22 @@ feature 'Admin updates settings' do
context 'sign-in restrictions', :js do
it 'de-activates oauth sign-in source' do
find('input#application_setting_enabled_oauth_sign_in_sources_[value=gitlab]').send_keys(:return)
page.within('.as-signin') do
find('input#application_setting_enabled_oauth_sign_in_sources_[value=gitlab]').send_keys(:return)
expect(find('.btn', text: 'GitLab.com')).not_to have_css('.active')
expect(find('.btn', text: 'GitLab.com')).not_to have_css('.active')
end
end
end
scenario 'Change Keys settings' do
select 'Are forbidden', from: 'RSA SSH keys'
select 'Are allowed', from: 'DSA SSH keys'
select 'Must be at least 384 bits', from: 'ECDSA SSH keys'
select 'Are forbidden', from: 'ED25519 SSH keys'
click_on 'Save'
page.within('.as-visibility-access') do
select 'Are forbidden', from: 'RSA SSH keys'
select 'Are allowed', from: 'DSA SSH keys'
select 'Must be at least 384 bits', from: 'ECDSA SSH keys'
select 'Are forbidden', from: 'ED25519 SSH keys'
click_on 'Save changes'
end
forbidden = ApplicationSetting::FORBIDDEN_KEY_VALUE.to_s
......
......@@ -160,9 +160,9 @@ feature 'Pipeline Schedules', :js do
click_link 'New schedule'
fill_in_schedule_form
all('[name="schedule[variables_attributes][][key]"]')[0].set('AAA')
all('[name="schedule[variables_attributes][][value]"]')[0].set('AAA123')
all('[name="schedule[variables_attributes][][secret_value]"]')[0].set('AAA123')
all('[name="schedule[variables_attributes][][key]"]')[1].set('BBB')
all('[name="schedule[variables_attributes][][value]"]')[1].set('BBB123')
all('[name="schedule[variables_attributes][][secret_value]"]')[1].set('BBB123')
save_pipeline_schedule
end
......
......@@ -6,7 +6,7 @@ describe ClustersFinder do
describe '#execute' do
let(:enabled_cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
let(:disabled_cluster) { create(:cluster, :disabled, :provided_by_gcp, projects: [project]) }
let(:disabled_cluster) { create(:cluster, :disabled, :provided_by_gcp, :production_environment, projects: [project]) }
subject { described_class.new(project, user, scope).execute }
......
......@@ -20,7 +20,7 @@ describe('NativeFormVariableList', () => {
it('should clear out the `name` attribute on the inputs for the last empty row on form submission (avoid BE validation)', () => {
const $row = $wrapper.find('.js-row');
expect($row.find('.js-ci-variable-input-key').attr('name')).toBe('schedule[variables_attributes][][key]');
expect($row.find('.js-ci-variable-input-value').attr('name')).toBe('schedule[variables_attributes][][value]');
expect($row.find('.js-ci-variable-input-value').attr('name')).toBe('schedule[variables_attributes][][secret_value]');
$wrapper.closest('form').trigger('trigger-submit');
......
......@@ -74,6 +74,10 @@ describe('Multi-file editor library', () => {
},
readOnly: true,
scrollBeyondLastLine: false,
quickSuggestions: false,
occurrencesHighlight: false,
renderLineHighlight: 'none',
hideCursorInOverviewRuler: true,
},
);
});
......
......@@ -5,23 +5,23 @@ describe Gitlab::Ci::Pipeline::Chain::Create do
set(:user) { create(:user) }
let(:pipeline) do
build(:ci_pipeline_with_one_job, project: project,
ref: 'master')
build(:ci_empty_pipeline, project: project, ref: 'master')
end
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(
project: project,
current_user: user, seeds_block: nil)
project: project, current_user: user)
end
let(:step) { described_class.new(pipeline, command) }
before do
step.perform!
end
context 'when pipeline is ready to be saved' do
before do
pipeline.stages.build(name: 'test', project: project)
step.perform!
end
it 'saves a pipeline' do
expect(pipeline).to be_persisted
end
......@@ -32,6 +32,7 @@ describe Gitlab::Ci::Pipeline::Chain::Create do
it 'creates stages' do
expect(pipeline.reload.stages).to be_one
expect(pipeline.stages.first).to be_persisted
end
end
......@@ -40,6 +41,10 @@ describe Gitlab::Ci::Pipeline::Chain::Create do
build(:ci_pipeline, project: project, ref: nil)
end
before do
step.perform!
end
it 'breaks the chain' do
expect(step.break?).to be true
end
......@@ -49,18 +54,4 @@ describe Gitlab::Ci::Pipeline::Chain::Create do
.to include /Failed to persist the pipeline/
end
end
context 'when there is a seed block present' do
let(:seeds) { spy('pipeline seeds') }
let(:command) do
double('command', project: project,
current_user: user,
seeds_block: seeds)
end
it 'executes the block' do
expect(seeds).to have_received(:call).with(pipeline)
end
end
end
require 'spec_helper'
describe Gitlab::Ci::Pipeline::Chain::Populate do
set(:project) { create(:project, :repository) }
set(:user) { create(:user) }
let(:pipeline) do
build(:ci_pipeline_with_one_job, project: project,
ref: 'master')
end
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(
project: project,
current_user: user,
seeds_block: nil)
end
let(:step) { described_class.new(pipeline, command) }
context 'when pipeline doesn not have seeds block' do
before do
step.perform!
end
it 'does not persist the pipeline' do
expect(pipeline).not_to be_persisted
end
it 'does not break the chain' do
expect(step.break?).to be false
end
it 'populates pipeline with stages' do
expect(pipeline.stages).to be_one
expect(pipeline.stages.first).not_to be_persisted
end
it 'populates pipeline with builds' do
expect(pipeline.builds).to be_one
expect(pipeline.builds.first).not_to be_persisted
expect(pipeline.stages.first.builds).to be_one
expect(pipeline.stages.first.builds.first).not_to be_persisted
end
end
context 'when pipeline is empty' do
let(:config) do
{ rspec: {
script: 'ls',
only: ['something']
} }
end
let(:pipeline) do
build(:ci_pipeline, project: project, config: config)
end
before do
step.perform!
end
it 'breaks the chain' do
expect(step.break?).to be true
end
it 'appends an error about missing stages' do
expect(pipeline.errors.to_a)
.to include 'No stages / jobs for this pipeline.'
end
end
context 'when pipeline has validation errors' do
let(:pipeline) do
build(:ci_pipeline, project: project, ref: nil)
end
before do
step.perform!
end
it 'breaks the chain' do
expect(step.break?).to be true
end
it 'appends validation error' do
expect(pipeline.errors.to_a)
.to include 'Failed to build the pipeline!'
end
end
context 'when there is a seed blocks present' do
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(
project: project,
current_user: user,
seeds_block: seeds_block)
end
context 'when seeds block builds some resources' do
let(:seeds_block) do
->(pipeline) { pipeline.variables.build(key: 'VAR', value: '123') }
end
it 'populates pipeline with resources described in the seeds block' do
step.perform!
expect(pipeline).not_to be_persisted
expect(pipeline.variables).not_to be_empty
expect(pipeline.variables.first).not_to be_persisted
expect(pipeline.variables.first.key).to eq 'VAR'
expect(pipeline.variables.first.value).to eq '123'
end
end
context 'when seeds block tries to persist some resources' do
let(:seeds_block) do
->(pipeline) { pipeline.variables.create!(key: 'VAR', value: '123') }
end
it 'raises exception' do
expect { step.perform! }.to raise_error(ActiveRecord::RecordNotSaved)
end
end
end
context 'when pipeline gets persisted during the process' do
let(:pipeline) { create(:ci_pipeline, project: project) }
it 'raises error' do
expect { step.perform! }.to raise_error(described_class::PopulateError)
end
end
context 'when using only/except build policies' do
let(:config) do
{ rspec: { script: 'rspec', stage: 'test', only: ['master'] },
prod: { script: 'cap prod', stage: 'deploy', only: ['tags'] } }
end
let(:pipeline) do
build(:ci_pipeline, ref: 'master', project: project, config: config)
end
it 'populates pipeline according to used policies' do
step.perform!
expect(pipeline.stages.size).to eq 1
expect(pipeline.builds.size).to eq 1
expect(pipeline.builds.first.name).to eq 'rspec'
end
end
end
......@@ -76,28 +76,6 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Config do
end
end
context 'when pipeline has no stages / jobs' do
let(:config) do
{ rspec: {
script: 'ls',
only: ['something']
} }
end
let(:pipeline) do
build(:ci_pipeline, project: project, config: config)
end
it 'appends an error about missing stages' do
expect(pipeline.errors.to_a)
.to include 'No stages / jobs for this pipeline.'
end
it 'breaks the chain' do
expect(step.break?).to be true
end
end
context 'when pipeline contains configuration validation errors' do
let(:config) { { rspec: {} } }
......
require 'spec_helper'
describe Gitlab::Ci::Pipeline::Seed::Build do
let(:pipeline) { create(:ci_empty_pipeline) }
let(:attributes) do
{ name: 'rspec',
ref: 'master',
commands: 'rspec' }
end
subject do
described_class.new(pipeline, attributes)
end
describe '#attributes' do
it 'returns hash attributes of a build' do
expect(subject.attributes).to be_a Hash
expect(subject.attributes)
.to include(:name, :project, :ref, :commands)
end
end
describe '#user=' do
let(:user) { build(:user) }
it 'assignes user to a build' do
subject.user = user
expect(subject.attributes).to include(user: user)
end
end
describe '#to_resource' do
it 'returns a valid build resource' do
expect(subject.to_resource).to be_a(::Ci::Build)
expect(subject.to_resource).to be_valid
end
it 'memoizes a resource object' do
build = subject.to_resource
expect(build.object_id).to eq subject.to_resource.object_id
end
it 'can not be persisted without explicit assignment' do
build = subject.to_resource
pipeline.save!
expect(build).not_to be_persisted
end
end
describe 'applying only/except policies' do
context 'when no branch policy is specified' do
let(:attributes) { { name: 'rspec' } }
it { is_expected.to be_included }
end
context 'when branch policy does not match' do
context 'when using only' do
let(:attributes) { { name: 'rspec', only: { refs: ['deploy'] } } }
it { is_expected.not_to be_included }
end
context 'when using except' do
let(:attributes) { { name: 'rspec', except: { refs: ['deploy'] } } }
it { is_expected.to be_included }
end
end
context 'when branch regexp policy does not match' do
context 'when using only' do
let(:attributes) { { name: 'rspec', only: { refs: ['/^deploy$/'] } } }
it { is_expected.not_to be_included }
end
context 'when using except' do
let(:attributes) { { name: 'rspec', except: { refs: ['/^deploy$/'] } } }
it { is_expected.to be_included }
end
end
context 'when branch policy matches' do
context 'when using only' do
let(:attributes) { { name: 'rspec', only: { refs: %w[deploy master] } } }
it { is_expected.to be_included }
end
context 'when using except' do
let(:attributes) { { name: 'rspec', except: { refs: %w[deploy master] } } }
it { is_expected.not_to be_included }
end
end
context 'when keyword policy matches' do
context 'when using only' do
let(:attributes) { { name: 'rspec', only: { refs: ['branches'] } } }
it { is_expected.to be_included }
end
context 'when using except' do
let(:attributes) { { name: 'rspec', except: { refs: ['branches'] } } }
it { is_expected.not_to be_included }
end
end
context 'when keyword policy does not match' do
context 'when using only' do
let(:attributes) { { name: 'rspec', only: { refs: ['tags'] } } }
it { is_expected.not_to be_included }
end
context 'when using except' do
let(:attributes) { { name: 'rspec', except: { refs: ['tags'] } } }
it { is_expected.to be_included }
end
end
context 'when keywords and pipeline source policy matches' do
possibilities = [%w[pushes push],
%w[web web],
%w[triggers trigger],
%w[schedules schedule],
%w[api api],
%w[external external]]
context 'when using only' do
possibilities.each do |keyword, source|
context "when using keyword `#{keyword}` and source `#{source}`" do
let(:pipeline) do
build(:ci_empty_pipeline, ref: 'deploy', tag: false, source: source)
end
let(:attributes) { { name: 'rspec', only: { refs: [keyword] } } }
it { is_expected.to be_included }
end
end
end
context 'when using except' do
possibilities.each do |keyword, source|
context "when using keyword `#{keyword}` and source `#{source}`" do
let(:pipeline) do
build(:ci_empty_pipeline, ref: 'deploy', tag: false, source: source)
end
let(:attributes) { { name: 'rspec', except: { refs: [keyword] } } }
it { is_expected.not_to be_included }
end
end
end
end
context 'when keywords and pipeline source does not match' do
possibilities = [%w[pushes web],
%w[web push],
%w[triggers schedule],
%w[schedules external],
%w[api trigger],
%w[external api]]
context 'when using only' do
possibilities.each do |keyword, source|
context "when using keyword `#{keyword}` and source `#{source}`" do
let(:pipeline) do
build(:ci_empty_pipeline, ref: 'deploy', tag: false, source: source)
end
let(:attributes) { { name: 'rspec', only: { refs: [keyword] } } }
it { is_expected.not_to be_included }
end
end
end
context 'when using except' do
possibilities.each do |keyword, source|
context "when using keyword `#{keyword}` and source `#{source}`" do
let(:pipeline) do
build(:ci_empty_pipeline, ref: 'deploy', tag: false, source: source)
end
let(:attributes) { { name: 'rspec', except: { refs: [keyword] } } }
it { is_expected.to be_included }
end
end
end
end
context 'when repository path matches' do
context 'when using only' do
let(:attributes) do
{ name: 'rspec', only: { refs: ["branches@#{pipeline.project_full_path}"] } }
end
it { is_expected.to be_included }
end
context 'when using except' do
let(:attributes) do
{ name: 'rspec', except: { refs: ["branches@#{pipeline.project_full_path}"] } }
end
it { is_expected.not_to be_included }
end
end
context 'when repository path does not matches' do
context 'when using only' do
let(:attributes) do
{ name: 'rspec', only: { refs: ['branches@fork'] } }
end
it { is_expected.not_to be_included }
end
context 'when using except' do
let(:attributes) do
{ name: 'rspec', except: { refs: ['branches@fork'] } }
end
it { is_expected.to be_included }
end
end
end
end
require 'spec_helper'
describe Gitlab::Ci::Stage::Seed do
describe Gitlab::Ci::Pipeline::Seed::Stage do
let(:pipeline) { create(:ci_empty_pipeline) }
let(:builds) do
[{ name: 'rspec' }, { name: 'spinach' }]
let(:attributes) do
{ name: 'test',
index: 0,
builds: [{ name: 'rspec' },
{ name: 'spinach' },
{ name: 'deploy', only: { refs: ['feature'] } }] }
end
subject do
described_class.new(pipeline, 'test', builds)
described_class.new(pipeline, attributes)
end
describe '#size' do
......@@ -17,20 +21,46 @@ describe Gitlab::Ci::Stage::Seed do
end
end
describe '#stage' do
describe '#attributes' do
it 'returns hash attributes of a stage' do
expect(subject.stage).to be_a Hash
expect(subject.stage).to include(:name, :project)
expect(subject.attributes).to be_a Hash
expect(subject.attributes).to include(:name, :project)
end
end
describe '#builds' do
it 'returns hash attributes of all builds' do
expect(subject.builds.size).to eq 2
expect(subject.builds).to all(include(ref: 'master'))
expect(subject.builds).to all(include(tag: false))
expect(subject.builds).to all(include(project: pipeline.project))
expect(subject.builds)
describe '#included?' do
context 'when it contains builds seeds' do
let(:attributes) do
{ name: 'test',
index: 0,
builds: [{ name: 'deploy', only: { refs: ['master'] } }] }
end
it { is_expected.to be_included }
end
context 'when it does not contain build seeds' do
let(:attributes) do
{ name: 'test',
index: 0,
builds: [{ name: 'deploy', only: { refs: ['feature'] } }] }
end
it { is_expected.not_to be_included }
end
end
describe '#seeds' do
it 'returns build seeds' do
expect(subject.seeds).to all(be_a Gitlab::Ci::Pipeline::Seed::Build)
end
it 'returns build seeds including valid attributes' do
expect(subject.seeds.size).to eq 2
expect(subject.seeds.map(&:attributes)).to all(include(ref: 'master'))
expect(subject.seeds.map(&:attributes)).to all(include(tag: false))
expect(subject.seeds.map(&:attributes)).to all(include(project: pipeline.project))
expect(subject.seeds.map(&:attributes))
.to all(include(trigger_request: pipeline.trigger_requests.first))
end
......@@ -40,17 +70,27 @@ describe Gitlab::Ci::Stage::Seed do
end
it 'returns protected builds' do
expect(subject.builds).to all(include(protected: true))
expect(subject.seeds.map(&:attributes)).to all(include(protected: true))
end
end
context 'when a ref is unprotected' do
context 'when a ref is not protected' do
before do
allow_any_instance_of(Project).to receive(:protected_for?).and_return(false)
end
it 'returns unprotected builds' do
expect(subject.builds).to all(include(protected: false))
expect(subject.seeds.map(&:attributes)).to all(include(protected: false))
end
end
it 'filters seeds using only/except policies' do
expect(subject.seeds.map(&:attributes)).to satisfy do |seeds|
seeds.any? { |hash| hash.fetch(:name) == 'rspec' }
end
expect(subject.seeds.map(&:attributes)).not_to satisfy do |seeds|
seeds.any? { |hash| hash.fetch(:name) == 'deploy' }
end
end
end
......@@ -61,13 +101,13 @@ describe Gitlab::Ci::Stage::Seed do
it 'assignes relevant pipeline attributes' do
subject.user = user
expect(subject.builds).to all(include(user: user))
expect(subject.seeds.map(&:attributes)).to all(include(user: user))
end
end
describe '#create!' do
it 'creates all stages and builds' do
subject.create!
describe '#to_resource' do
it 'builds a valid stage object with all builds' do
subject.to_resource.save!
expect(pipeline.reload.stages.count).to eq 1
expect(pipeline.reload.builds.count).to eq 2
......@@ -79,5 +119,15 @@ describe Gitlab::Ci::Stage::Seed do
expect(pipeline.stages)
.to all(satisfy { |stage| stage.project.present? })
end
it 'can not be persisted without explicit pipeline assignment' do
stage = subject.to_resource
pipeline.save!
expect(stage).not_to be_persisted
expect(pipeline.reload.stages.count).to eq 0
expect(pipeline.reload.builds.count).to eq 0
end
end
end
......@@ -18,6 +18,34 @@ module Gitlab
describe '#build_attributes' do
subject { described_class.new(config).build_attributes(:rspec) }
describe 'attributes list' do
let(:config) do
YAML.dump(
before_script: ['pwd'],
rspec: { script: 'rspec' }
)
end
it 'returns valid build attributes' do
expect(subject).to eq({
stage: "test",
stage_idx: 1,
name: "rspec",
commands: "pwd\nrspec",
coverage_regex: nil,
tag_list: [],
options: {
before_script: ["pwd"],
script: ["rspec"]
},
allow_failure: false,
when: "on_success",
environment: nil,
yaml_variables: []
})
end
end
describe 'coverage entry' do
describe 'code coverage regexp' do
let(:config) do
......@@ -105,512 +133,118 @@ module Gitlab
end
end
describe '#stage_seeds' do
context 'when no refs policy is specified' do
let(:config) do
YAML.dump(production: { stage: 'deploy', script: 'cap prod' },
rspec: { stage: 'test', script: 'rspec' },
spinach: { stage: 'test', script: 'spinach' })
end
let(:pipeline) { create(:ci_empty_pipeline) }
it 'correctly fabricates a stage seeds object' do
seeds = subject.stage_seeds(pipeline)
expect(seeds.size).to eq 2
expect(seeds.first.stage[:name]).to eq 'test'
expect(seeds.second.stage[:name]).to eq 'deploy'
expect(seeds.first.builds.dig(0, :name)).to eq 'rspec'
expect(seeds.first.builds.dig(1, :name)).to eq 'spinach'
expect(seeds.second.builds.dig(0, :name)).to eq 'production'
end
end
context 'when refs policy is specified' do
let(:config) do
YAML.dump(production: { stage: 'deploy', script: 'cap prod', only: ['master'] },
spinach: { stage: 'test', script: 'spinach', only: ['tags'] })
end
let(:pipeline) do
create(:ci_empty_pipeline, ref: 'feature', tag: true)
end
it 'returns stage seeds only assigned to master to master' do
seeds = subject.stage_seeds(pipeline)
expect(seeds.size).to eq 1
expect(seeds.first.stage[:name]).to eq 'test'
expect(seeds.first.builds.dig(0, :name)).to eq 'spinach'
end
end
context 'when source policy is specified' do
let(:config) do
YAML.dump(production: { stage: 'deploy', script: 'cap prod', only: ['triggers'] },
spinach: { stage: 'test', script: 'spinach', only: ['schedules'] })
end
let(:pipeline) do
create(:ci_empty_pipeline, source: :schedule)
end
it 'returns stage seeds only assigned to schedules' do
seeds = subject.stage_seeds(pipeline)
expect(seeds.size).to eq 1
expect(seeds.first.stage[:name]).to eq 'test'
expect(seeds.first.builds.dig(0, :name)).to eq 'spinach'
end
describe '#stages_attributes' do
let(:config) do
YAML.dump(
rspec: { script: 'rspec', stage: 'test', only: ['branches'] },
prod: { script: 'cap prod', stage: 'deploy', only: ['tags'] }
)
end
context 'when kubernetes policy is specified' do
let(:config) do
YAML.dump(
spinach: { stage: 'test', script: 'spinach' },
production: {
stage: 'deploy',
script: 'cap',
only: { kubernetes: 'active' }
}
)
end
context 'when kubernetes is active' do
shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
it 'returns seeds for kubernetes dependent job' do
seeds = subject.stage_seeds(pipeline)
expect(seeds.size).to eq 2
expect(seeds.first.builds.dig(0, :name)).to eq 'spinach'
expect(seeds.second.builds.dig(0, :name)).to eq 'production'
end
end
context 'when user configured kubernetes from Integration > Kubernetes' do
let(:project) { create(:kubernetes_project) }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
context 'when user configured kubernetes from CI/CD > Clusters' do
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
end
context 'when kubernetes is not active' do
it 'does not return seeds for kubernetes dependent job' do
seeds = subject.stage_seeds(pipeline)
expect(seeds.size).to eq 1
expect(seeds.first.builds.dig(0, :name)).to eq 'spinach'
end
end
let(:attributes) do
[{ name: "build",
index: 0,
builds: [] },
{ name: "test",
index: 1,
builds:
[{ stage_idx: 1,
stage: "test",
commands: "rspec",
tag_list: [],
name: "rspec",
allow_failure: false,
when: "on_success",
environment: nil,
coverage_regex: nil,
yaml_variables: [],
options: { script: ["rspec"] },
only: { refs: ["branches"] },
except: {} }] },
{ name: "deploy",
index: 2,
builds:
[{ stage_idx: 2,
stage: "deploy",
commands: "cap prod",
tag_list: [],
name: "prod",
allow_failure: false,
when: "on_success",
environment: nil,
coverage_regex: nil,
yaml_variables: [],
options: { script: ["cap prod"] },
only: { refs: ["tags"] },
except: {} }] }]
end
it 'returns stages seed attributes' do
expect(subject.stages_attributes).to eq attributes
end
end
describe "#pipeline_stage_builds" do
let(:type) { 'test' }
describe 'only / except policies validations' do
context 'when `only` has an invalid value' do
let(:config) { { rspec: { script: "rspec", type: "test", only: only } } }
let(:processor) { Gitlab::Ci::YamlProcessor.new(YAML.dump(config)) }
it "returns builds if no branch specified" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec" }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "master")).size).to eq(1)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "master")).first).to eq({
stage: "test",
stage_idx: 1,
name: "rspec",
commands: "pwd\nrspec",
coverage_regex: nil,
tag_list: [],
options: {
before_script: ["pwd"],
script: ["rspec"]
},
allow_failure: false,
when: "on_success",
environment: nil,
yaml_variables: []
})
end
describe 'only' do
it "does not return builds if only has another branch" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", only: ["deploy"] }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "master")).size).to eq(0)
end
it "does not return builds if only has regexp with another branch" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", only: ["/^deploy$/"] }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "master")).size).to eq(0)
end
it "returns builds if only has specified this branch" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", only: ["master"] }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "master")).size).to eq(1)
end
it "returns builds if only has a list of branches including specified" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, only: %w(master deploy) }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "deploy")).size).to eq(1)
end
it "returns builds if only has a branches keyword specified" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, only: ["branches"] }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "deploy")).size).to eq(1)
end
it "does not return builds if only has a tags keyword" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, only: ["tags"] }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "deploy")).size).to eq(0)
end
context 'when it is integer' do
let(:only) { 1 }
it "returns builds if only has special keywords specified and source matches" do
possibilities = [{ keyword: 'pushes', source: 'push' },
{ keyword: 'web', source: 'web' },
{ keyword: 'triggers', source: 'trigger' },
{ keyword: 'schedules', source: 'schedule' },
{ keyword: 'api', source: 'api' },
{ keyword: 'external', source: 'external' }]
possibilities.each do |possibility|
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, only: [possibility[:keyword]] }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: 'deploy', tag: false, source: possibility[:source])).size).to eq(1)
it do
expect { processor }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError,
'jobs:rspec:only has to be either an array of conditions or a hash')
end
end
it "does not return builds if only has special keywords specified and source doesn't match" do
possibilities = [{ keyword: 'pushes', source: 'web' },
{ keyword: 'web', source: 'push' },
{ keyword: 'triggers', source: 'schedule' },
{ keyword: 'schedules', source: 'external' },
{ keyword: 'api', source: 'trigger' },
{ keyword: 'external', source: 'api' }]
possibilities.each do |possibility|
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, only: [possibility[:keyword]] }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
context 'when it is an array of integers' do
let(:only) { [1, 1] }
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: 'deploy', tag: false, source: possibility[:source])).size).to eq(0)
it do
expect { processor }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError,
'jobs:rspec:only config should be an array of strings or regexps')
end
end
it "returns builds if only has current repository path" do
seed_pipeline = pipeline(ref: 'deploy')
config = YAML.dump({
before_script: ["pwd"],
rspec: {
script: "rspec",
type: type,
only: ["branches@#{seed_pipeline.project_full_path}"]
}
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
context 'when it is invalid regex' do
let(:only) { ["/*invalid/"] }
expect(config_processor.pipeline_stage_builds(type, seed_pipeline).size).to eq(1)
end
it "does not return builds if only has different repository path" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, only: ["branches@fork"] }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "deploy")).size).to eq(0)
end
it "returns build only for specified type" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: "test", only: %w(master deploy) },
staging: { script: "deploy", type: "deploy", only: %w(master deploy) },
production: { script: "deploy", type: "deploy", only: ["master@path", "deploy"] }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds("deploy", pipeline(ref: "deploy")).size).to eq(2)
expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "deploy")).size).to eq(1)
expect(config_processor.pipeline_stage_builds("deploy", pipeline(ref: "master")).size).to eq(1)
end
context 'for invalid value' do
let(:config) { { rspec: { script: "rspec", type: "test", only: only } } }
let(:processor) { Gitlab::Ci::YamlProcessor.new(YAML.dump(config)) }
context 'when it is integer' do
let(:only) { 1 }
it do
expect { processor }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError,
'jobs:rspec:only has to be either an array of conditions or a hash')
end
end
context 'when it is an array of integers' do
let(:only) { [1, 1] }
it do
expect { processor }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError,
'jobs:rspec:only config should be an array of strings or regexps')
end
end
context 'when it is invalid regex' do
let(:only) { ["/*invalid/"] }
it do
expect { processor }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError,
'jobs:rspec:only config should be an array of strings or regexps')
end
it do
expect { processor }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError,
'jobs:rspec:only config should be an array of strings or regexps')
end
end
end
describe 'except' do
it "returns builds if except has another branch" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", except: ["deploy"] }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "master")).size).to eq(1)
end
it "returns builds if except has regexp with another branch" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", except: ["/^deploy$/"] }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
context 'when `except` has an invalid value' do
let(:config) { { rspec: { script: "rspec", except: except } } }
let(:processor) { Gitlab::Ci::YamlProcessor.new(YAML.dump(config)) }
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "master")).size).to eq(1)
end
it "does not return builds if except has specified this branch" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", except: ["master"] }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "master")).size).to eq(0)
end
it "does not return builds if except has a list of branches including specified" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, except: %w(master deploy) }
})
context 'when it is integer' do
let(:except) { 1 }
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "deploy")).size).to eq(0)
end
it "does not return builds if except has a branches keyword specified" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, except: ["branches"] }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "deploy")).size).to eq(0)
end
it "returns builds if except has a tags keyword" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, except: ["tags"] }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "deploy")).size).to eq(1)
end
it "does not return builds if except has special keywords specified and source matches" do
possibilities = [{ keyword: 'pushes', source: 'push' },
{ keyword: 'web', source: 'web' },
{ keyword: 'triggers', source: 'trigger' },
{ keyword: 'schedules', source: 'schedule' },
{ keyword: 'api', source: 'api' },
{ keyword: 'external', source: 'external' }]
possibilities.each do |possibility|
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, except: [possibility[:keyword]] }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: 'deploy', tag: false, source: possibility[:source])).size).to eq(0)
it do
expect { processor }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError,
'jobs:rspec:except has to be either an array of conditions or a hash')
end
end
it "returns builds if except has special keywords specified and source doesn't match" do
possibilities = [{ keyword: 'pushes', source: 'web' },
{ keyword: 'web', source: 'push' },
{ keyword: 'triggers', source: 'schedule' },
{ keyword: 'schedules', source: 'external' },
{ keyword: 'api', source: 'trigger' },
{ keyword: 'external', source: 'api' }]
possibilities.each do |possibility|
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, except: [possibility[:keyword]] }
})
context 'when it is an array of integers' do
let(:except) { [1, 1] }
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: 'deploy', tag: false, source: possibility[:source])).size).to eq(1)
it do
expect { processor }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError,
'jobs:rspec:except config should be an array of strings or regexps')
end
end
it "does not return builds if except has current repository path" do
seed_pipeline = pipeline(ref: 'deploy')
config = YAML.dump({
before_script: ["pwd"],
rspec: {
script: "rspec",
type: type,
except: ["branches@#{seed_pipeline.project_full_path}"]
}
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, seed_pipeline).size).to eq(0)
end
it "returns builds if except has different repository path" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, except: ["branches@fork"] }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds(type, pipeline(ref: "deploy")).size).to eq(1)
end
it "returns build except specified type" do
master_pipeline = pipeline(ref: 'master')
test_pipeline = pipeline(ref: 'test')
deploy_pipeline = pipeline(ref: 'deploy')
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: "test", except: ["master", "deploy", "test@#{test_pipeline.project_full_path}"] },
staging: { script: "deploy", type: "deploy", except: ["master"] },
production: { script: "deploy", type: "deploy", except: ["master@#{master_pipeline.project_full_path}"] }
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds("deploy", deploy_pipeline).size).to eq(2)
expect(config_processor.pipeline_stage_builds("test", test_pipeline).size).to eq(0)
expect(config_processor.pipeline_stage_builds("deploy", master_pipeline).size).to eq(0)
end
context 'for invalid value' do
let(:config) { { rspec: { script: "rspec", except: except } } }
let(:processor) { Gitlab::Ci::YamlProcessor.new(YAML.dump(config)) }
context 'when it is invalid regex' do
let(:except) { ["/*invalid/"] }
context 'when it is integer' do
let(:except) { 1 }
it do
expect { processor }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError,
'jobs:rspec:except has to be either an array of conditions or a hash')
end
end
context 'when it is an array of integers' do
let(:except) { [1, 1] }
it do
expect { processor }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError,
'jobs:rspec:except config should be an array of strings or regexps')
end
end
context 'when it is invalid regex' do
let(:except) { ["/*invalid/"] }
it do
expect { processor }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError,
'jobs:rspec:except config should be an array of strings or regexps')
end
it do
expect { processor }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError,
'jobs:rspec:except config should be an array of strings or regexps')
end
end
end
......@@ -620,7 +254,7 @@ module Gitlab
let(:config_data) { YAML.dump(config) }
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(config_data) }
subject { config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first }
subject { config_processor.stage_builds_attributes('test').first }
describe "before_script" do
context "in global context" do
......@@ -703,8 +337,8 @@ module Gitlab
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).size).to eq(1)
expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first).to eq({
expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first).to eq({
stage: "test",
stage_idx: 1,
name: "rspec",
......@@ -738,8 +372,8 @@ module Gitlab
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).size).to eq(1)
expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first).to eq({
expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first).to eq({
stage: "test",
stage_idx: 1,
name: "rspec",
......@@ -771,8 +405,8 @@ module Gitlab
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).size).to eq(1)
expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first).to eq({
expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first).to eq({
stage: "test",
stage_idx: 1,
name: "rspec",
......@@ -800,8 +434,8 @@ module Gitlab
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).size).to eq(1)
expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first).to eq({
expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first).to eq({
stage: "test",
stage_idx: 1,
name: "rspec",
......@@ -946,8 +580,8 @@ module Gitlab
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
builds = config_processor.stage_builds_attributes("test")
builds = config_processor.pipeline_stage_builds("test", pipeline(ref: "master"))
expect(builds.size).to eq(1)
expect(builds.first[:when]).to eq(when_state)
end
......@@ -978,8 +612,8 @@ module Gitlab
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).size).to eq(1)
expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first[:options][:cache]).to eq(
expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first[:options][:cache]).to eq(
paths: ["logs/", "binaries/"],
untracked: true,
key: 'key',
......@@ -997,8 +631,8 @@ module Gitlab
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).size).to eq(1)
expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first[:options][:cache]).to eq(
expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first[:options][:cache]).to eq(
paths: ["logs/", "binaries/"],
untracked: true,
key: 'key',
......@@ -1017,8 +651,8 @@ module Gitlab
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).size).to eq(1)
expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first[:options][:cache]).to eq(
expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first[:options][:cache]).to eq(
paths: ["test/"],
untracked: false,
key: 'local',
......@@ -1046,8 +680,8 @@ module Gitlab
config_processor = Gitlab::Ci::YamlProcessor.new(config)
expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).size).to eq(1)
expect(config_processor.pipeline_stage_builds("test", pipeline(ref: "master")).first).to eq({
expect(config_processor.stage_builds_attributes("test").size).to eq(1)
expect(config_processor.stage_builds_attributes("test").first).to eq({
stage: "test",
stage_idx: 1,
name: "rspec",
......@@ -1083,8 +717,8 @@ module Gitlab
})
config_processor = Gitlab::Ci::YamlProcessor.new(config)
builds = config_processor.stage_builds_attributes("test")
builds = config_processor.pipeline_stage_builds("test", pipeline(ref: "master"))
expect(builds.size).to eq(1)
expect(builds.first[:options][:artifacts][:when]).to eq(when_state)
end
......@@ -1099,7 +733,7 @@ module Gitlab
end
let(:processor) { Gitlab::Ci::YamlProcessor.new(YAML.dump(config)) }
let(:builds) { processor.pipeline_stage_builds('deploy', pipeline(ref: 'master')) }
let(:builds) { processor.stage_builds_attributes('deploy') }
context 'when a production environment is specified' do
let(:environment) { 'production' }
......@@ -1256,7 +890,7 @@ module Gitlab
describe "Hidden jobs" do
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(config) }
subject { config_processor.pipeline_stage_builds("test", pipeline(ref: "master")) }
subject { config_processor.stage_builds_attributes("test") }
shared_examples 'hidden_job_handling' do
it "doesn't create jobs that start with dot" do
......@@ -1304,7 +938,7 @@ module Gitlab
describe "YAML Alias/Anchor" do
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(config) }
subject { config_processor.pipeline_stage_builds("build", pipeline(ref: "master")) }
subject { config_processor.stage_builds_attributes("build") }
shared_examples 'job_templates_handling' do
it "is correctly supported for jobs" do
......@@ -1344,13 +978,13 @@ module Gitlab
context 'when template is a job' do
let(:config) do
<<EOT
job1: &JOBTMPL
stage: build
script: execute-script-for-job
<<~EOT
job1: &JOBTMPL
stage: build
script: execute-script-for-job
job2: *JOBTMPL
EOT
job2: *JOBTMPL
EOT
end
it_behaves_like 'job_templates_handling'
......@@ -1358,15 +992,15 @@ EOT
context 'when template is a hidden job' do
let(:config) do
<<EOT
.template: &JOBTMPL
stage: build
script: execute-script-for-job
<<~EOT
.template: &JOBTMPL
stage: build
script: execute-script-for-job
job1: *JOBTMPL
job1: *JOBTMPL
job2: *JOBTMPL
EOT
job2: *JOBTMPL
EOT
end
it_behaves_like 'job_templates_handling'
......@@ -1374,18 +1008,18 @@ EOT
context 'when job adds its own keys to a template definition' do
let(:config) do
<<EOT
.template: &JOBTMPL
stage: build
job1:
<<: *JOBTMPL
script: execute-script-for-job
job2:
<<: *JOBTMPL
script: execute-script-for-job
EOT
<<~EOT
.template: &JOBTMPL
stage: build
job1:
<<: *JOBTMPL
script: execute-script-for-job
job2:
<<: *JOBTMPL
script: execute-script-for-job
EOT
end
it_behaves_like 'job_templates_handling'
......@@ -1724,10 +1358,6 @@ EOT
it { is_expected.to be_nil }
end
end
def pipeline(**attributes)
build_stubbed(:ci_empty_pipeline, **attributes)
end
end
end
end
......@@ -181,6 +181,24 @@ describe Ci::Pipeline, :mailer do
end
end
describe '#protected_ref?' do
it 'delegates method to project' do
expect(pipeline).not_to be_protected_ref
end
end
describe '#legacy_trigger' do
let(:trigger_request) { create(:ci_trigger_request) }
before do
pipeline.trigger_requests << trigger_request
end
it 'returns first trigger request' do
expect(pipeline.legacy_trigger).to eq trigger_request
end
end
describe '#auto_canceled?' do
subject { pipeline.auto_canceled? }
......@@ -219,144 +237,262 @@ describe Ci::Pipeline, :mailer do
end
describe 'pipeline stages' do
before do
create(:commit_status, pipeline: pipeline,
stage: 'build',
name: 'linux',
stage_idx: 0,
status: 'success')
create(:commit_status, pipeline: pipeline,
stage: 'build',
name: 'mac',
stage_idx: 0,
status: 'failed')
create(:commit_status, pipeline: pipeline,
stage: 'deploy',
name: 'staging',
stage_idx: 2,
status: 'running')
create(:commit_status, pipeline: pipeline,
stage: 'test',
name: 'rspec',
stage_idx: 1,
status: 'success')
end
describe '#stage_seeds' do
let(:project) { create(:project, :repository) }
let(:pipeline) do
build(:ci_pipeline, project: project, config: { rspec: { script: 'rake' } })
end
let(:pipeline) { build(:ci_pipeline, project: project, config: config) }
let(:config) { { rspec: { script: 'rake' } } }
it 'returns preseeded stage seeds object' do
expect(pipeline.stage_seeds).to all(be_a Gitlab::Ci::Stage::Seed)
expect(pipeline.stage_seeds)
.to all(be_a Gitlab::Ci::Pipeline::Seed::Base)
expect(pipeline.stage_seeds.count).to eq 1
end
end
describe '#seeds_size' do
let(:project) { create(:project, :repository) }
let(:pipeline) { build(:ci_pipeline_with_one_job, project: project) }
context 'when no refs policy is specified' do
let(:config) do
{ production: { stage: 'deploy', script: 'cap prod' },
rspec: { stage: 'test', script: 'rspec' },
spinach: { stage: 'test', script: 'spinach' } }
end
it 'correctly fabricates a stage seeds object' do
seeds = pipeline.stage_seeds
it 'returns number of jobs in stage seeds' do
expect(pipeline.seeds_size).to eq 1
expect(seeds.size).to eq 2
expect(seeds.first.attributes[:name]).to eq 'test'
expect(seeds.second.attributes[:name]).to eq 'deploy'
expect(seeds.dig(0, 0, :name)).to eq 'rspec'
expect(seeds.dig(0, 1, :name)).to eq 'spinach'
expect(seeds.dig(1, 0, :name)).to eq 'production'
end
end
end
describe '#legacy_stages' do
subject { pipeline.legacy_stages }
context 'when refs policy is specified' do
let(:pipeline) do
build(:ci_pipeline, ref: 'feature', tag: true, project: project, config: config)
end
context 'stages list' do
it 'returns ordered list of stages' do
expect(subject.map(&:name)).to eq(%w[build test deploy])
let(:config) do
{ production: { stage: 'deploy', script: 'cap prod', only: ['master'] },
spinach: { stage: 'test', script: 'spinach', only: ['tags'] } }
end
it 'returns stage seeds only assigned to master to master' do
seeds = pipeline.stage_seeds
expect(seeds.size).to eq 1
expect(seeds.first.attributes[:name]).to eq 'test'
expect(seeds.dig(0, 0, :name)).to eq 'spinach'
end
end
context 'stages with statuses' do
let(:statuses) do
subject.map { |stage| [stage.name, stage.status] }
context 'when source policy is specified' do
let(:pipeline) do
build(:ci_pipeline, source: :schedule, project: project, config: config)
end
it 'returns list of stages with correct statuses' do
expect(statuses).to eq([%w(build failed),
%w(test success),
%w(deploy running)])
let(:config) do
{ production: { stage: 'deploy', script: 'cap prod', only: ['triggers'] },
spinach: { stage: 'test', script: 'spinach', only: ['schedules'] } }
end
context 'when commit status is retried' do
before do
create(:commit_status, pipeline: pipeline,
stage: 'build',
name: 'mac',
stage_idx: 0,
status: 'success')
it 'returns stage seeds only assigned to schedules' do
seeds = pipeline.stage_seeds
expect(seeds.size).to eq 1
expect(seeds.first.attributes[:name]).to eq 'test'
expect(seeds.dig(0, 0, :name)).to eq 'spinach'
end
end
context 'when kubernetes policy is specified' do
let(:config) do
{
spinach: { stage: 'test', script: 'spinach' },
production: {
stage: 'deploy',
script: 'cap',
only: { kubernetes: 'active' }
}
}
end
pipeline.process!
context 'when kubernetes is active' do
shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
it 'returns seeds for kubernetes dependent job' do
seeds = pipeline.stage_seeds
expect(seeds.size).to eq 2
expect(seeds.dig(0, 0, :name)).to eq 'spinach'
expect(seeds.dig(1, 0, :name)).to eq 'production'
end
end
it 'ignores the previous state' do
expect(statuses).to eq([%w(build success),
%w(test success),
%w(deploy running)])
context 'when user configured kubernetes from Integration > Kubernetes' do
let(:project) { create(:kubernetes_project, :repository) }
let(:pipeline) { build(:ci_pipeline, project: project, config: config) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
end
end
context 'when there is a stage with warnings' do
before do
create(:commit_status, pipeline: pipeline,
stage: 'deploy',
name: 'prod:2',
stage_idx: 2,
status: 'failed',
allow_failure: true)
context 'when user configured kubernetes from CI/CD > Clusters' do
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
let(:pipeline) { build(:ci_pipeline, project: project, config: config) }
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
end
it 'populates stage with correct number of warnings' do
deploy_stage = pipeline.legacy_stages.third
context 'when kubernetes is not active' do
it 'does not return seeds for kubernetes dependent job' do
seeds = pipeline.stage_seeds
expect(deploy_stage).not_to receive(:statuses)
expect(deploy_stage).to have_warnings
expect(seeds.size).to eq 1
expect(seeds.dig(0, 0, :name)).to eq 'spinach'
end
end
end
end
describe '#stages_count' do
it 'returns a valid number of stages' do
expect(pipeline.stages_count).to eq(3)
end
end
describe '#seeds_size' do
context 'when refs policy is specified' do
let(:project) { create(:project, :repository) }
let(:config) do
{ production: { stage: 'deploy', script: 'cap prod', only: ['master'] },
spinach: { stage: 'test', script: 'spinach', only: ['tags'] } }
end
let(:pipeline) do
build(:ci_pipeline, ref: 'feature', tag: true, project: project, config: config)
end
describe '#stages_names' do
it 'returns a valid names of stages' do
expect(pipeline.stages_names).to eq(%w(build test deploy))
it 'returns real seeds size' do
expect(pipeline.seeds_size).to eq 1
end
end
end
end
describe '#legacy_stage' do
subject { pipeline.legacy_stage('test') }
context 'with status in stage' do
describe 'legacy stages' do
before do
create(:commit_status, pipeline: pipeline, stage: 'test')
create(:commit_status, pipeline: pipeline,
stage: 'build',
name: 'linux',
stage_idx: 0,
status: 'success')
create(:commit_status, pipeline: pipeline,
stage: 'build',
name: 'mac',
stage_idx: 0,
status: 'failed')
create(:commit_status, pipeline: pipeline,
stage: 'deploy',
name: 'staging',
stage_idx: 2,
status: 'running')
create(:commit_status, pipeline: pipeline,
stage: 'test',
name: 'rspec',
stage_idx: 1,
status: 'success')
end
describe '#legacy_stages' do
subject { pipeline.legacy_stages }
context 'stages list' do
it 'returns ordered list of stages' do
expect(subject.map(&:name)).to eq(%w[build test deploy])
end
end
context 'stages with statuses' do
let(:statuses) do
subject.map { |stage| [stage.name, stage.status] }
end
it 'returns list of stages with correct statuses' do
expect(statuses).to eq([%w(build failed),
%w(test success),
%w(deploy running)])
end
context 'when commit status is retried' do
before do
create(:commit_status, pipeline: pipeline,
stage: 'build',
name: 'mac',
stage_idx: 0,
status: 'success')
pipeline.process!
end
it 'ignores the previous state' do
expect(statuses).to eq([%w(build success),
%w(test success),
%w(deploy running)])
end
end
end
context 'when there is a stage with warnings' do
before do
create(:commit_status, pipeline: pipeline,
stage: 'deploy',
name: 'prod:2',
stage_idx: 2,
status: 'failed',
allow_failure: true)
end
it 'populates stage with correct number of warnings' do
deploy_stage = pipeline.legacy_stages.third
expect(deploy_stage).not_to receive(:statuses)
expect(deploy_stage).to have_warnings
end
end
end
describe '#stages_count' do
it 'returns a valid number of stages' do
expect(pipeline.stages_count).to eq(3)
end
end
it { expect(subject).to be_a Ci::LegacyStage }
it { expect(subject.name).to eq 'test' }
it { expect(subject.statuses).not_to be_empty }
describe '#stages_names' do
it 'returns a valid names of stages' do
expect(pipeline.stages_names).to eq(%w(build test deploy))
end
end
end
context 'without status in stage' do
before do
create(:commit_status, pipeline: pipeline, stage: 'build')
describe '#legacy_stage' do
subject { pipeline.legacy_stage('test') }
context 'with status in stage' do
before do
create(:commit_status, pipeline: pipeline, stage: 'test')
end
it { expect(subject).to be_a Ci::LegacyStage }
it { expect(subject.name).to eq 'test' }
it { expect(subject.statuses).not_to be_empty }
end
it 'return stage object' do
is_expected.to be_nil
context 'without status in stage' do
before do
create(:commit_status, pipeline: pipeline, stage: 'build')
end
it 'return stage object' do
is_expected.to be_nil
end
end
end
end
......@@ -595,21 +731,6 @@ describe Ci::Pipeline, :mailer do
end
end
describe '#has_stage_seeds?' do
context 'when pipeline has stage seeds' do
let(:project) { create(:project, :repository) }
subject { build(:ci_pipeline_with_one_job, project: project) }
it { is_expected.to have_stage_seeds }
end
context 'when pipeline does not have stage seeds' do
subject { create(:ci_pipeline_without_jobs) }
it { is_expected.not_to have_stage_seeds }
end
end
describe '#has_warnings?' do
subject { pipeline.has_warnings? }
......
......@@ -378,7 +378,7 @@ describe Environment do
shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
it 'returns the terminals from the deployment service' do
expect(project.deployment_platform)
expect(project.deployment_platform(environment: environment))
.to receive(:terminals).with(environment)
.and_return(:fake_terminals)
......@@ -419,7 +419,7 @@ describe Environment do
end
it 'returns the rollout status from the deployment service' do
expect(project.deployment_platform)
expect(project.deployment_platform(environment: environment))
.to receive(:rollout_status).with(environment)
.and_return(:fake_rollout_status)
......
......@@ -2404,6 +2404,26 @@ describe MergeRequest do
end
end
describe '#base_pipeline' do
let(:pipeline_arguments) do
{
project: project,
ref: merge_request.target_branch,
sha: merge_request.diff_base_sha
}
end
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
let!(:first_pipeline) { create(:ci_pipeline_without_jobs, pipeline_arguments) }
let!(:last_pipeline) { create(:ci_pipeline_without_jobs, pipeline_arguments) }
it 'returns latest pipeline' do
expect(merge_request.base_pipeline).to eq(last_pipeline)
end
end
describe '#has_commits?' do
before do
allow(subject.merge_request_diff).to receive(:commits_count)
......
......@@ -339,7 +339,7 @@ describe ProjectPresenter do
it 'returns link to clusters page if more than one exists' do
project.add_master(user)
create(:cluster, projects: [project])
create(:cluster, :production_environment, projects: [project])
create(:cluster, projects: [project])
expect(presenter.kubernetes_cluster_anchor_data).to eq(OpenStruct.new(enabled: true,
......
......@@ -185,6 +185,12 @@ describe API::Triggers do
expect(response).to have_http_status(201)
expect(Ci::Pipeline.last.source).to eq('pipeline')
expect(Ci::Pipeline.last.triggered_by_pipeline).not_to be_nil
expect(Ci::Sources::Pipeline.last).to have_attributes(
pipeline_id: (a_value > 0),
source_pipeline_id: (a_value > 0),
source_job_id: (a_value > 0),
source_project_id: (a_value > 0)
)
end
context 'when build is complete' do
......
......@@ -81,7 +81,7 @@ describe Clusters::CreateService do
end
context 'when project has a cluster' do
let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
let!(:cluster) { create(:cluster, :provided_by_gcp, :production_environment, projects: [project]) }
before do
allow(project).to receive(:feature_available?).and_call_original
......
......@@ -16,19 +16,19 @@ shared_examples 'PATCH #update updates variables' do
let(:variable_attributes) do
{ id: variable.id,
key: variable.key,
value: variable.value,
secret_value: variable.value,
protected: variable.protected?.to_s }
end
let(:new_variable_attributes) do
{ key: 'new_key',
value: 'dummy_value',
secret_value: 'dummy_value',
protected: 'false' }
end
context 'with invalid new variable parameters' do
let(:variables_attributes) do
[
variable_attributes.merge(value: 'other_value'),
variable_attributes.merge(secret_value: 'other_value'),
new_variable_attributes.merge(key: '...?')
]
end
......@@ -52,7 +52,7 @@ shared_examples 'PATCH #update updates variables' do
let(:variables_attributes) do
[
new_variable_attributes,
new_variable_attributes.merge(value: 'other_value')
new_variable_attributes.merge(secret_value: 'other_value')
]
end
......@@ -74,7 +74,7 @@ shared_examples 'PATCH #update updates variables' do
context 'with valid new variable parameters' do
let(:variables_attributes) do
[
variable_attributes.merge(value: 'other_value'),
variable_attributes.merge(secret_value: 'other_value'),
new_variable_attributes
]
end
......
......@@ -5,6 +5,7 @@ describe 'ci/lints/show' do
describe 'XSS protection' do
let(:config_processor) { Gitlab::Ci::YamlProcessor.new(YAML.dump(content)) }
before do
assign(:status, true)
assign(:builds, config_processor.builds)
......
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