summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/ci_variable_list/ci_variable_list.js4
-rw-r--r--app/assets/javascripts/ci_variable_list/native_form_variable_list.js1
-rw-r--r--app/assets/javascripts/mirrors/mirror_repos.js2
-rw-r--r--app/assets/javascripts/mirrors/ssh_mirror.js114
-rw-r--r--app/controllers/groups/variables_controller.rb2
-rw-r--r--app/controllers/projects/mirrors_controller.rb1
-rw-r--r--app/controllers/projects/pipeline_schedules_controller.rb2
-rw-r--r--app/controllers/projects/pipelines_controller.rb2
-rw-r--r--app/controllers/projects/variables_controller.rb2
-rw-r--r--app/helpers/ci_variables_helper.rb7
-rw-r--r--app/models/concerns/has_variable.rb7
-rw-r--r--app/services/lfs/file_transformer.rb9
-rw-r--r--app/views/ci/variables/_variable_row.html.haml4
-rw-r--r--app/views/projects/mirrors/_authentication_method.html.haml21
-rw-r--r--app/views/projects/mirrors/_mirror_repos_push.html.haml2
-rw-r--r--app/views/projects/mirrors/_ssh_host_keys.html.haml2
-rw-r--r--changelogs/unreleased/46806-typed-ci-variables.yml5
-rw-r--r--changelogs/unreleased/61203-fix-lfs-ui-upload.yml5
-rw-r--r--changelogs/unreleased/ce-its-simple-just-destroy-the-mirrors.yml5
-rw-r--r--db/migrate/20190415030217_add_variable_type_to_ci_variables.rb17
-rw-r--r--db/migrate/20190416213556_add_variable_type_to_ci_group_variables.rb17
-rw-r--r--db/migrate/20190416213615_add_variable_type_to_ci_pipeline_variables.rb17
-rw-r--r--db/migrate/20190416213631_add_variable_type_to_ci_pipeline_schedule_variables.rb17
-rw-r--r--db/schema.rb4
-rw-r--r--doc/api/group_level_variables.md31
-rw-r--r--doc/api/pipeline_schedules.md5
-rw-r--r--doc/api/pipelines.md3
-rw-r--r--doc/api/project_level_variables.md31
-rw-r--r--doc/user/project/labels.md4
-rw-r--r--lib/api/entities.rb2
-rw-r--r--lib/api/group_variables.rb2
-rw-r--r--lib/api/pipeline_schedules.rb2
-rw-r--r--lib/api/variables.rb2
-rw-r--r--locale/gitlab.pot12
-rw-r--r--qa/qa/git/repository.rb2
-rw-r--r--qa/spec/git/repository_spec.rb9
-rw-r--r--spec/controllers/projects/pipeline_schedules_controller_spec.rb3
-rw-r--r--spec/factories/ci/pipeline_schedule_variables.rb1
-rw-r--r--spec/features/projects/settings/repository_settings_spec.rb19
-rw-r--r--spec/fixtures/api/schemas/pipeline_schedule.json2
-rw-r--r--spec/fixtures/api/schemas/pipeline_schedule_variable.json10
-rw-r--r--spec/models/ci/group_variable_spec.rb3
-rw-r--r--spec/models/ci/pipeline_schedule_variable_spec.rb2
-rw-r--r--spec/models/ci/pipeline_variable_spec.rb3
-rw-r--r--spec/models/ci/variable_spec.rb3
-rw-r--r--spec/requests/api/group_variables_spec.rb8
-rw-r--r--spec/requests/api/pipeline_schedules_spec.rb7
-rw-r--r--spec/requests/api/pipelines_spec.rb9
-rw-r--r--spec/requests/api/variables_spec.rb8
-rw-r--r--spec/services/lfs/file_transformer_spec.rb19
-rw-r--r--spec/support/shared_examples/controllers/variables_shared_examples.rb12
-rw-r--r--spec/support/shared_examples/models/ci_variable_shared_examples.rb29
52 files changed, 310 insertions, 202 deletions
diff --git a/app/assets/javascripts/ci_variable_list/ci_variable_list.js b/app/assets/javascripts/ci_variable_list/ci_variable_list.js
index da3100b9386..0390a3bf96a 100644
--- a/app/assets/javascripts/ci_variable_list/ci_variable_list.js
+++ b/app/assets/javascripts/ci_variable_list/ci_variable_list.js
@@ -26,6 +26,10 @@ export default class VariableList {
selector: '.js-ci-variable-input-id',
default: '',
},
+ variable_type: {
+ selector: '.js-ci-variable-input-variable-type',
+ default: 'env_var',
+ },
key: {
selector: '.js-ci-variable-input-key',
default: '',
diff --git a/app/assets/javascripts/ci_variable_list/native_form_variable_list.js b/app/assets/javascripts/ci_variable_list/native_form_variable_list.js
index e7111c666a2..fdbefd8c313 100644
--- a/app/assets/javascripts/ci_variable_list/native_form_variable_list.js
+++ b/app/assets/javascripts/ci_variable_list/native_form_variable_list.js
@@ -19,6 +19,7 @@ export default function setupNativeFormVariableList({ container, formField = 'va
const isTouched = variableList.checkIfRowTouched($lastRow);
if (!isTouched) {
$lastRow.find('input, textarea').attr('name', '');
+ $lastRow.find('select').attr('name', '');
}
});
}
diff --git a/app/assets/javascripts/mirrors/mirror_repos.js b/app/assets/javascripts/mirrors/mirror_repos.js
index 196b84621b6..33e9b1c4e46 100644
--- a/app/assets/javascripts/mirrors/mirror_repos.js
+++ b/app/assets/javascripts/mirrors/mirror_repos.js
@@ -87,7 +87,7 @@ export default class MirrorRepos {
project: {
remote_mirrors_attributes: {
id: $target.data('mirrorId'),
- enabled: 0,
+ _destroy: 1,
},
},
};
diff --git a/app/assets/javascripts/mirrors/ssh_mirror.js b/app/assets/javascripts/mirrors/ssh_mirror.js
index f7e80950803..bb5ae6ce2d1 100644
--- a/app/assets/javascripts/mirrors/ssh_mirror.js
+++ b/app/assets/javascripts/mirrors/ssh_mirror.js
@@ -24,12 +24,6 @@ export default class SSHMirror {
this.$wellAuthTypeChanging = this.$form.find('.js-well-changing-auth');
this.$wellPasswordAuth = this.$form.find('.js-well-password-auth');
- this.$wellSSHAuth = this.$form.find('.js-well-ssh-auth');
- this.$sshPublicKeyWrap = this.$form.find('.js-ssh-public-key-wrap');
- this.$regeneratePublicSshKeyButton = this.$wellSSHAuth.find('.js-btn-regenerate-ssh-key');
- this.$regeneratePublicSshKeyModal = this.$wellSSHAuth.find(
- '.js-regenerate-public-ssh-key-confirm-modal',
- );
}
init() {
@@ -40,15 +34,6 @@ export default class SSHMirror {
this.$dropdownAuthType.on('change', e => this.handleAuthTypeChange(e));
this.$btnDetectHostKeys.on('click', e => this.handleDetectHostKeys(e));
this.$btnSSHHostsShowAdvanced.on('click', e => this.handleSSHHostsAdvanced(e));
- this.$regeneratePublicSshKeyButton.on('click', () =>
- this.$regeneratePublicSshKeyModal.toggle(true),
- );
- $('.js-confirm', this.$regeneratePublicSshKeyModal).on('click', e =>
- this.regeneratePublicSshKey(e),
- );
- $('.js-cancel', this.$regeneratePublicSshKeyModal).on('click', () =>
- this.$regeneratePublicSshKeyModal.toggle(false),
- );
}
/**
@@ -162,54 +147,11 @@ export default class SSHMirror {
* Authentication method dropdown change event listener
*/
handleAuthTypeChange() {
- const projectMirrorAuthTypeEndpoint = `${this.$form.attr('action')}.json`;
- const $sshPublicKey = this.$sshPublicKeyWrap.find('.ssh-public-key');
const selectedAuthType = this.$dropdownAuthType.val();
this.$wellPasswordAuth.collapse('hide');
- this.$wellSSHAuth.collapse('hide');
this.updateHiddenAuthType(selectedAuthType);
-
- // This request should happen only if selected Auth type was SSH
- // and SSH Public key was not present on page load
- if (selectedAuthType === AUTH_METHOD.SSH && !$sshPublicKey.text().trim()) {
- if (!this.$wellSSHAuth.length) return;
-
- // Construct request body
- const authTypeData = {
- project: {
- ...this.$regeneratePublicSshKeyButton.data().projectData,
- },
- };
-
- this.$wellAuthTypeChanging.collapse('show');
- this.$dropdownAuthType.disable();
-
- axios
- .put(projectMirrorAuthTypeEndpoint, JSON.stringify(authTypeData), {
- headers: {
- 'Content-Type': 'application/json; charset=utf-8',
- },
- })
- .then(({ data }) => {
- // Show SSH public key container and fill in public key
- this.toggleAuthWell(selectedAuthType);
- this.toggleSSHAuthWellMessage(true);
- this.setSSHPublicKey(data.import_data_attributes.ssh_public_key);
-
- this.$wellAuthTypeChanging.collapse('hide');
- this.$dropdownAuthType.enable();
- })
- .catch(() => {
- Flash(__('Something went wrong on our end.'));
-
- this.$wellAuthTypeChanging.collapse('hide');
- this.$dropdownAuthType.enable();
- });
- } else {
- this.toggleAuthWell(selectedAuthType);
- this.$wellSSHAuth.find('.js-ssh-public-key-present').collapse('show');
- }
+ this.toggleAuthWell(selectedAuthType);
}
/**
@@ -235,7 +177,6 @@ export default class SSHMirror {
*/
toggleAuthWell(authType) {
this.$wellPasswordAuth.collapse(authType === AUTH_METHOD.PASSWORD ? 'show' : 'hide');
- this.$wellSSHAuth.collapse(authType === AUTH_METHOD.SSH ? 'show' : 'hide');
this.updateHiddenAuthType(authType);
}
@@ -244,64 +185,11 @@ export default class SSHMirror {
this.$hiddenAuthType.prop('disabled', authType === AUTH_METHOD.SSH);
}
- /**
- * Toggle SSH auth information message
- */
- toggleSSHAuthWellMessage(sshKeyPresent) {
- this.$sshPublicKeyWrap.collapse(sshKeyPresent ? 'show' : 'hide');
- this.$wellSSHAuth.find('.js-ssh-public-key-present').collapse(sshKeyPresent ? 'show' : 'hide');
- this.$regeneratePublicSshKeyButton.collapse(sshKeyPresent ? 'show' : 'hide');
- this.$wellSSHAuth.find('.js-ssh-public-key-pending').collapse(sshKeyPresent ? 'hide' : 'show');
- }
-
- /**
- * Sets SSH Public key to Clipboard button and shows it on UI.
- */
- setSSHPublicKey(sshPublicKey) {
- this.$sshPublicKeyWrap.find('.ssh-public-key').text(sshPublicKey);
- this.$sshPublicKeyWrap
- .find('.btn-copy-ssh-public-key')
- .attr('data-clipboard-text', sshPublicKey);
- }
-
- regeneratePublicSshKey(event) {
- event.preventDefault();
-
- this.$regeneratePublicSshKeyModal.toggle(false);
-
- const button = this.$regeneratePublicSshKeyButton;
- const spinner = $('.js-spinner', button);
- const endpoint = button.data('endpoint');
- const authTypeData = {
- project: {
- ...this.$regeneratePublicSshKeyButton.data().projectData,
- },
- };
-
- button.attr('disabled', 'disabled');
- spinner.removeClass('d-none');
-
- axios
- .patch(endpoint, authTypeData)
- .then(({ data }) => {
- button.removeAttr('disabled');
- spinner.addClass('d-none');
-
- this.setSSHPublicKey(data.import_data_attributes.ssh_public_key);
- })
- .catch(() => {
- Flash(__('Unable to regenerate public ssh key.'));
- });
- }
-
destroy() {
this.$repositoryUrl.off('keyup');
this.$form.find('.js-known-hosts').off('keyup');
this.$dropdownAuthType.off('change');
this.$btnDetectHostKeys.off('click');
this.$btnSSHHostsShowAdvanced.off('click');
- this.$regeneratePublicSshKeyButton.off('click');
- $('.js-confirm', this.$regeneratePublicSshKeyModal).off('click');
- $('.js-cancel', this.$regeneratePublicSshKeyModal).off('click');
}
}
diff --git a/app/controllers/groups/variables_controller.rb b/app/controllers/groups/variables_controller.rb
index b44e3b0fff4..11e3cfb01e4 100644
--- a/app/controllers/groups/variables_controller.rb
+++ b/app/controllers/groups/variables_controller.rb
@@ -41,7 +41,7 @@ module Groups
end
def variable_params_attributes
- %i[id key secret_value protected masked _destroy]
+ %i[id variable_type key secret_value protected masked _destroy]
end
def authorize_admin_build!
diff --git a/app/controllers/projects/mirrors_controller.rb b/app/controllers/projects/mirrors_controller.rb
index ef330ae00f4..6c6adc233b7 100644
--- a/app/controllers/projects/mirrors_controller.rb
+++ b/app/controllers/projects/mirrors_controller.rb
@@ -81,6 +81,7 @@ class Projects::MirrorsController < Projects::ApplicationController
password
ssh_known_hosts
regenerate_ssh_private_key
+ _destroy
]
]
end
diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb
index 6b721c8fdf7..72e939a3310 100644
--- a/app/controllers/projects/pipeline_schedules_controller.rb
+++ b/app/controllers/projects/pipeline_schedules_controller.rb
@@ -98,7 +98,7 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController
def schedule_params
params.require(:schedule)
.permit(:description, :cron, :cron_timezone, :ref, :active,
- variables_attributes: [:id, :key, :secret_value, :_destroy] )
+ variables_attributes: [:id, :variable_type, :key, :secret_value, :_destroy] )
end
def authorize_play_pipeline_schedule!
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index 22c4b8eef1f..db3b7c8b177 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -169,7 +169,7 @@ class Projects::PipelinesController < Projects::ApplicationController
end
def create_params
- params.require(:pipeline).permit(:ref, variables_attributes: %i[key secret_value])
+ params.require(:pipeline).permit(:ref, variables_attributes: %i[key variable_type secret_value])
end
# rubocop: disable CodeReuse/ActiveRecord
diff --git a/app/controllers/projects/variables_controller.rb b/app/controllers/projects/variables_controller.rb
index 05a79d59ffd..646728e8167 100644
--- a/app/controllers/projects/variables_controller.rb
+++ b/app/controllers/projects/variables_controller.rb
@@ -38,6 +38,6 @@ class Projects::VariablesController < Projects::ApplicationController
end
def variable_params_attributes
- %i[id key secret_value protected masked _destroy]
+ %i[id variable_type key secret_value protected masked _destroy]
end
end
diff --git a/app/helpers/ci_variables_helper.rb b/app/helpers/ci_variables_helper.rb
index 88ce311a1d4..5bfdeb9e33c 100644
--- a/app/helpers/ci_variables_helper.rb
+++ b/app/helpers/ci_variables_helper.rb
@@ -20,4 +20,11 @@ module CiVariablesHelper
true
end
end
+
+ def ci_variable_type_options
+ [
+ %w(Variable env_var),
+ %w(File file)
+ ]
+ end
end
diff --git a/app/models/concerns/has_variable.rb b/app/models/concerns/has_variable.rb
index 2ec42a1029b..b4e99569071 100644
--- a/app/models/concerns/has_variable.rb
+++ b/app/models/concerns/has_variable.rb
@@ -4,6 +4,11 @@ module HasVariable
extend ActiveSupport::Concern
included do
+ enum variable_type: {
+ env_var: 1,
+ file: 2
+ }
+
validates :key,
presence: true,
length: { maximum: 255 },
@@ -24,6 +29,6 @@ module HasVariable
end
def to_runner_variable
- { key: key, value: value, public: false }
+ { key: key, value: value, public: false, file: file? }
end
end
diff --git a/app/services/lfs/file_transformer.rb b/app/services/lfs/file_transformer.rb
index 6ecf583cb6a..5239fe1b6e3 100644
--- a/app/services/lfs/file_transformer.rb
+++ b/app/services/lfs/file_transformer.rb
@@ -24,7 +24,7 @@ module Lfs
def new_file(file_path, file_content, encoding: nil)
if project.lfs_enabled? && lfs_file?(file_path)
- file_content = Base64.decode64(file_content) if encoding == 'base64'
+ file_content = parse_file_content(file_content, encoding: encoding)
lfs_pointer_file = Gitlab::Git::LfsPointerFile.new(file_content)
lfs_object = create_lfs_object!(lfs_pointer_file, file_content)
@@ -66,5 +66,12 @@ module Lfs
def link_lfs_object!(lfs_object)
project.lfs_objects << lfs_object
end
+
+ def parse_file_content(file_content, encoding: nil)
+ return file_content.read if file_content.respond_to?(:read)
+ return Base64.decode64(file_content) if encoding == 'base64'
+
+ file_content
+ end
end
end
diff --git a/app/views/ci/variables/_variable_row.html.haml b/app/views/ci/variables/_variable_row.html.haml
index 12a8d9930b7..37257b3aa1c 100644
--- a/app/views/ci/variables/_variable_row.html.haml
+++ b/app/views/ci/variables/_variable_row.html.haml
@@ -3,6 +3,7 @@
- only_key_value = local_assigns.fetch(:only_key_value, false)
- id = variable&.id
+- variable_type = variable&.variable_type
- key = variable&.key
- value = variable&.value
- is_protected_default = ci_variable_protected_by_default?
@@ -12,6 +13,7 @@
- id_input_name = "#{form_field}[variables_attributes][][id]"
- destroy_input_name = "#{form_field}[variables_attributes][][_destroy]"
+- variable_type_input_name = "#{form_field}[variables_attributes][][variable_type]"
- key_input_name = "#{form_field}[variables_attributes][][key]"
- value_input_name = "#{form_field}[variables_attributes][][secret_value]"
- protected_input_name = "#{form_field}[variables_attributes][][protected]"
@@ -21,6 +23,8 @@
.ci-variable-row-body
%input.js-ci-variable-input-id{ type: "hidden", name: id_input_name, value: id }
%input.js-ci-variable-input-destroy{ type: "hidden", name: destroy_input_name }
+ %select.js-ci-variable-input-variable-type.ci-variable-body-item.form-control.select-control{ name: variable_type_input_name }
+ = options_for_select(ci_variable_type_options, variable_type)
%input.js-ci-variable-input-key.ci-variable-body-item.qa-ci-variable-input-key.form-control{ type: "text",
name: key_input_name,
value: key,
diff --git a/app/views/projects/mirrors/_authentication_method.html.haml b/app/views/projects/mirrors/_authentication_method.html.haml
index ef6db07a1bb..ee82d68d398 100644
--- a/app/views/projects/mirrors/_authentication_method.html.haml
+++ b/app/views/projects/mirrors/_authentication_method.html.haml
@@ -1,8 +1,5 @@
- mirror = f.object
-- is_push = local_assigns.fetch(:is_push, false)
- auth_options = [[_('Password'), 'password'], [_('SSH public key'), 'ssh_public_key']]
-- regen_data = { auth_method: 'ssh_public_key', regenerate_ssh_private_key: true }
-- ssh_public_key_present = mirror.ssh_public_key.present?
.form-group
= f.label :auth_method, _('Authentication method'), class: 'label-bold'
@@ -17,21 +14,3 @@
.well-password-auth.collapse.js-well-password-auth
= f.label :password, _("Password"), class: "label-bold"
= f.password_field :password, value: mirror.password, class: 'form-control qa-password', autocomplete: 'new-password'
- - unless is_push
- .well-ssh-auth.collapse.js-well-ssh-auth
- %p.js-ssh-public-key-present{ class: ('collapse' unless ssh_public_key_present) }
- = _('Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation.')
- %p.js-ssh-public-key-pending{ class: ('collapse' if ssh_public_key_present) }
- = _('An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation.')
-
- .clearfix.js-ssh-public-key-wrap{ class: ('collapse' unless ssh_public_key_present) }
- %code.prepend-top-10.ssh-public-key
- = mirror.ssh_public_key
- = clipboard_button(text: mirror.ssh_public_key, title: _("Copy SSH public key to clipboard"), class: 'prepend-top-10 btn-copy-ssh-public-key')
-
- = button_tag type: 'button',
- data: { endpoint: project_mirror_path(@project), project_data: { import_data_attributes: regen_data } },
- class: "btn btn-inverted btn-warning prepend-top-10 js-btn-regenerate-ssh-key#{ ' collapse' unless ssh_public_key_present }" do
- = icon('spinner spin', class: 'js-spinner d-none')
- = _('Regenerate key')
- = render 'projects/mirrors/regenerate_public_ssh_key_confirm_modal'
diff --git a/app/views/projects/mirrors/_mirror_repos_push.html.haml b/app/views/projects/mirrors/_mirror_repos_push.html.haml
index 1d9c83653fe..b7c885b4a63 100644
--- a/app/views/projects/mirrors/_mirror_repos_push.html.haml
+++ b/app/views/projects/mirrors/_mirror_repos_push.html.haml
@@ -5,4 +5,4 @@
= rm_f.hidden_field :url, class: 'js-mirror-url-hidden', required: true, pattern: "(#{protocols}):\/\/.+"
= rm_f.hidden_field :only_protected_branches, class: 'js-mirror-protected-hidden'
= render partial: 'projects/mirrors/ssh_host_keys', locals: { f: rm_f }
- = render partial: 'projects/mirrors/authentication_method', locals: { f: rm_f, is_push: true }
+ = render partial: 'projects/mirrors/authentication_method', locals: { f: rm_f }
diff --git a/app/views/projects/mirrors/_ssh_host_keys.html.haml b/app/views/projects/mirrors/_ssh_host_keys.html.haml
index f61aa6ecd11..7762fb4b844 100644
--- a/app/views/projects/mirrors/_ssh_host_keys.html.haml
+++ b/app/views/projects/mirrors/_ssh_host_keys.html.haml
@@ -3,7 +3,7 @@
- verified_at = mirror.ssh_known_hosts_verified_at
.form-group.js-ssh-host-keys-section{ class: ('collapse' unless mirror.ssh_mirror_url?) }
- %button.btn.btn-inverted.btn-success.inline.js-detect-host-keys.append-right-10{ type: 'button' }
+ %button.btn.btn-inverted.btn-secondary.inline.js-detect-host-keys.append-right-10{ type: 'button' }
= icon('spinner spin', class: 'js-spinner d-none')
= _('Detect host keys')
.fingerprint-ssh-info.js-fingerprint-ssh-info.prepend-top-10.append-bottom-10{ class: ('collapse' unless mirror.ssh_mirror_url?) }
diff --git a/changelogs/unreleased/46806-typed-ci-variables.yml b/changelogs/unreleased/46806-typed-ci-variables.yml
new file mode 100644
index 00000000000..aa15c31bca1
--- /dev/null
+++ b/changelogs/unreleased/46806-typed-ci-variables.yml
@@ -0,0 +1,5 @@
+---
+title: CI variables of type file
+merge_request: 27112
+author:
+type: added
diff --git a/changelogs/unreleased/61203-fix-lfs-ui-upload.yml b/changelogs/unreleased/61203-fix-lfs-ui-upload.yml
new file mode 100644
index 00000000000..66afe9f0597
--- /dev/null
+++ b/changelogs/unreleased/61203-fix-lfs-ui-upload.yml
@@ -0,0 +1,5 @@
+---
+title: Fix uploading of LFS tracked file through UI
+merge_request: 28052
+author:
+type: fixed
diff --git a/changelogs/unreleased/ce-its-simple-just-destroy-the-mirrors.yml b/changelogs/unreleased/ce-its-simple-just-destroy-the-mirrors.yml
new file mode 100644
index 00000000000..ac5fc27cf36
--- /dev/null
+++ b/changelogs/unreleased/ce-its-simple-just-destroy-the-mirrors.yml
@@ -0,0 +1,5 @@
+---
+title: Destroy project remote mirrors instead of disabling
+merge_request: 27087
+author:
+type: security
diff --git a/db/migrate/20190415030217_add_variable_type_to_ci_variables.rb b/db/migrate/20190415030217_add_variable_type_to_ci_variables.rb
new file mode 100644
index 00000000000..433f510299a
--- /dev/null
+++ b/db/migrate/20190415030217_add_variable_type_to_ci_variables.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddVariableTypeToCiVariables < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+ ENV_VAR_VARIABLE_TYPE = 1
+
+ def up
+ add_column_with_default(:ci_variables, :variable_type, :smallint, default: ENV_VAR_VARIABLE_TYPE)
+ end
+
+ def down
+ remove_column(:ci_variables, :variable_type)
+ end
+end
diff --git a/db/migrate/20190416213556_add_variable_type_to_ci_group_variables.rb b/db/migrate/20190416213556_add_variable_type_to_ci_group_variables.rb
new file mode 100644
index 00000000000..dce73caeb5e
--- /dev/null
+++ b/db/migrate/20190416213556_add_variable_type_to_ci_group_variables.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddVariableTypeToCiGroupVariables < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+ ENV_VAR_VARIABLE_TYPE = 1
+
+ def up
+ add_column_with_default(:ci_group_variables, :variable_type, :smallint, default: ENV_VAR_VARIABLE_TYPE)
+ end
+
+ def down
+ remove_column(:ci_group_variables, :variable_type)
+ end
+end
diff --git a/db/migrate/20190416213615_add_variable_type_to_ci_pipeline_variables.rb b/db/migrate/20190416213615_add_variable_type_to_ci_pipeline_variables.rb
new file mode 100644
index 00000000000..1010d9bd29e
--- /dev/null
+++ b/db/migrate/20190416213615_add_variable_type_to_ci_pipeline_variables.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddVariableTypeToCiPipelineVariables < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+ ENV_VAR_VARIABLE_TYPE = 1
+
+ def up
+ add_column_with_default(:ci_pipeline_variables, :variable_type, :smallint, default: ENV_VAR_VARIABLE_TYPE)
+ end
+
+ def down
+ remove_column(:ci_pipeline_variables, :variable_type)
+ end
+end
diff --git a/db/migrate/20190416213631_add_variable_type_to_ci_pipeline_schedule_variables.rb b/db/migrate/20190416213631_add_variable_type_to_ci_pipeline_schedule_variables.rb
new file mode 100644
index 00000000000..3079b2afd9c
--- /dev/null
+++ b/db/migrate/20190416213631_add_variable_type_to_ci_pipeline_schedule_variables.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddVariableTypeToCiPipelineScheduleVariables < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+ ENV_VAR_VARIABLE_TYPE = 1
+
+ def up
+ add_column_with_default(:ci_pipeline_schedule_variables, :variable_type, :smallint, default: ENV_VAR_VARIABLE_TYPE)
+ end
+
+ def down
+ remove_column(:ci_pipeline_schedule_variables, :variable_type)
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index ef8cb4abf31..1bcd22dc81c 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -419,6 +419,7 @@ ActiveRecord::Schema.define(version: 20190426180107) do
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.boolean "masked", default: false, null: false
+ t.integer "variable_type", limit: 2, default: 1, null: false
t.index ["group_id", "key"], name: "index_ci_group_variables_on_group_id_and_key", unique: true, using: :btree
end
@@ -458,6 +459,7 @@ ActiveRecord::Schema.define(version: 20190426180107) do
t.integer "pipeline_schedule_id", null: false
t.datetime_with_timezone "created_at"
t.datetime_with_timezone "updated_at"
+ t.integer "variable_type", limit: 2, default: 1, null: false
t.index ["pipeline_schedule_id", "key"], name: "index_ci_pipeline_schedule_variables_on_schedule_id_and_key", unique: true, using: :btree
end
@@ -484,6 +486,7 @@ ActiveRecord::Schema.define(version: 20190426180107) do
t.string "encrypted_value_salt"
t.string "encrypted_value_iv"
t.integer "pipeline_id", null: false
+ t.integer "variable_type", limit: 2, default: 1, null: false
t.index ["pipeline_id", "key"], name: "index_ci_pipeline_variables_on_pipeline_id_and_key", unique: true, using: :btree
end
@@ -618,6 +621,7 @@ ActiveRecord::Schema.define(version: 20190426180107) do
t.boolean "protected", default: false, null: false
t.string "environment_scope", default: "*", null: false
t.boolean "masked", default: false, null: false
+ t.integer "variable_type", limit: 2, default: 1, null: false
t.index ["project_id", "key", "environment_scope"], name: "index_ci_variables_on_project_id_and_key_and_environment_scope", unique: true, using: :btree
end
diff --git a/doc/api/group_level_variables.md b/doc/api/group_level_variables.md
index 3551bfa3f8b..7b00df6d775 100644
--- a/doc/api/group_level_variables.md
+++ b/doc/api/group_level_variables.md
@@ -22,10 +22,12 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
[
{
"key": "TEST_VARIABLE_1",
+ "variable_type": "env_var",
"value": "TEST_1"
},
{
"key": "TEST_VARIABLE_2",
+ "variable_type": "env_var",
"value": "TEST_2"
}
]
@@ -51,6 +53,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
```json
{
"key": "TEST_VARIABLE_1",
+ "variable_type": "env_var",
"value": "TEST_1"
}
```
@@ -63,12 +66,13 @@ Create a new variable.
POST /groups/:id/variables
```
-| Attribute | Type | required | Description |
-|-------------|---------|----------|-----------------------|
-| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `key` | string | yes | The `key` of a variable; must have no more than 255 characters; only `A-Z`, `a-z`, `0-9`, and `_` are allowed |
-| `value` | string | yes | The `value` of a variable |
-| `protected` | boolean | no | Whether the variable is protected |
+| Attribute | Type | required | Description |
+|-----------------|---------|----------|-----------------------|
+| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `key` | string | yes | The `key` of a variable; must have no more than 255 characters; only `A-Z`, `a-z`, `0-9`, and `_` are allowed |
+| `value` | string | yes | The `value` of a variable |
+| `variable_type` | string | no | The type of a variable. Available types are: `env_var` (default) and `file` |
+| `protected` | boolean | no | Whether the variable is protected |
```
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/1/variables" --form "key=NEW_VARIABLE" --form "value=new value"
@@ -78,6 +82,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitla
{
"key": "NEW_VARIABLE",
"value": "new value",
+ "variable_type": "env_var",
"protected": false
}
```
@@ -90,12 +95,13 @@ Update a group's variable.
PUT /groups/:id/variables/:key
```
-| Attribute | Type | required | Description |
-|-------------|---------|----------|-------------------------|
-| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `key` | string | yes | The `key` of a variable |
-| `value` | string | yes | The `value` of a variable |
-| `protected` | boolean | no | Whether the variable is protected |
+| Attribute | Type | required | Description |
+|-----------------|---------|----------|-------------------------|
+| `id` | integer/string | yes | The ID of a group or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `key` | string | yes | The `key` of a variable |
+| `value` | string | yes | The `value` of a variable |
+| `variable_type` | string | no | The type of a variable. Available types are: `env_var` (default) and `file` |
+| `protected` | boolean | no | Whether the variable is protected |
```
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/1/variables/NEW_VARIABLE" --form "value=updated value"
@@ -105,6 +111,7 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab
{
"key": "NEW_VARIABLE",
"value": "updated value",
+ "variable_type": "env_var",
"protected": true
}
```
diff --git a/doc/api/pipeline_schedules.md b/doc/api/pipeline_schedules.md
index 50d9e007ecc..470e55425f8 100644
--- a/doc/api/pipeline_schedules.md
+++ b/doc/api/pipeline_schedules.md
@@ -88,6 +88,7 @@ curl --header "PRIVATE-TOKEN: k5ESFgWY2Qf5xEvDcFxZ" "https://gitlab.example.com/
"variables": [
{
"key": "TEST_VARIABLE_1",
+ "variable_type": "env_var",
"value": "TEST_1"
}
]
@@ -296,6 +297,7 @@ POST /projects/:id/pipeline_schedules/:pipeline_schedule_id/variables
| `pipeline_schedule_id` | integer | yes | The pipeline schedule id |
| `key` | string | yes | The `key` of a variable; must have no more than 255 characters; only `A-Z`, `a-z`, `0-9`, and `_` are allowed |
| `value` | string | yes | The `value` of a variable |
+| `variable_type` | string | no | The type of a variable. Available types are: `env_var` (default) and `file` |
```sh
curl --request POST --header "PRIVATE-TOKEN: k5ESFgWY2Qf5xEvDcFxZ" --form "key=NEW_VARIABLE" --form "value=new value" "https://gitlab.example.com/api/v4/projects/29/pipeline_schedules/13/variables"
@@ -304,6 +306,7 @@ curl --request POST --header "PRIVATE-TOKEN: k5ESFgWY2Qf5xEvDcFxZ" --form "key=N
```json
{
"key": "NEW_VARIABLE",
+ "variable_type": "env_var",
"value": "new value"
}
```
@@ -322,6 +325,7 @@ PUT /projects/:id/pipeline_schedules/:pipeline_schedule_id/variables/:key
| `pipeline_schedule_id` | integer | yes | The pipeline schedule id |
| `key` | string | yes | The `key` of a variable |
| `value` | string | yes | The `value` of a variable |
+| `variable_type` | string | no | The type of a variable. Available types are: `env_var` (default) and `file` |
```sh
curl --request PUT --header "PRIVATE-TOKEN: k5ESFgWY2Qf5xEvDcFxZ" --form "value=updated value" "https://gitlab.example.com/api/v4/projects/29/pipeline_schedules/13/variables/NEW_VARIABLE"
@@ -331,6 +335,7 @@ curl --request PUT --header "PRIVATE-TOKEN: k5ESFgWY2Qf5xEvDcFxZ" --form "value=
{
"key": "NEW_VARIABLE",
"value": "updated value"
+ "variable_type": "env_var",
}
```
diff --git a/doc/api/pipelines.md b/doc/api/pipelines.md
index 1a4310ef328..753faec3cc8 100644
--- a/doc/api/pipelines.md
+++ b/doc/api/pipelines.md
@@ -114,6 +114,7 @@ Example of response
[
{
"key": "RUN_NIGHTLY_BUILD",
+ "variable_type": "env_var",
"value": "true"
},
{
@@ -135,7 +136,7 @@ POST /projects/:id/pipeline
|------------|---------|----------|---------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `ref` | string | yes | Reference to commit |
-| `variables` | array | no | An array containing the variables available in the pipeline, matching the structure [{ 'key' => 'UPLOAD_TO_S3', 'value' => 'true' }] |
+| `variables` | array | no | An array containing the variables available in the pipeline, matching the structure [{ 'key' => 'UPLOAD_TO_S3', 'variable_type' => 'file', 'value' => 'true' }] |
```
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/pipeline?ref=master"
diff --git a/doc/api/project_level_variables.md b/doc/api/project_level_variables.md
index 438bebe62f5..4a6f5624394 100644
--- a/doc/api/project_level_variables.md
+++ b/doc/api/project_level_variables.md
@@ -20,10 +20,12 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
[
{
"key": "TEST_VARIABLE_1",
+ "variable_type": "env_var",
"value": "TEST_1"
},
{
"key": "TEST_VARIABLE_2",
+ "variable_type": "env_var",
"value": "TEST_2"
}
]
@@ -49,6 +51,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
```json
{
"key": "TEST_VARIABLE_1",
+ "variable_type": "env_var",
"value": "TEST_1"
}
```
@@ -61,12 +64,13 @@ Create a new variable.
POST /projects/:id/variables
```
-| Attribute | Type | required | Description |
-|-------------|---------|----------|-----------------------|
-| `id` | integer/string | yes | The ID of a project or [urlencoded NAMESPACE/PROJECT_NAME of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `key` | string | yes | The `key` of a variable; must have no more than 255 characters; only `A-Z`, `a-z`, `0-9`, and `_` are allowed |
-| `value` | string | yes | The `value` of a variable |
-| `protected` | boolean | no | Whether the variable is protected |
+| Attribute | Type | required | Description |
+|-----------------|---------|----------|-----------------------|
+| `id` | integer/string | yes | The ID of a project or [urlencoded NAMESPACE/PROJECT_NAME of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `key` | string | yes | The `key` of a variable; must have no more than 255 characters; only `A-Z`, `a-z`, `0-9`, and `_` are allowed |
+| `value` | string | yes | The `value` of a variable |
+| `variable_type` | string | no | The type of a variable. Available types are: `env_var` (default) and `file` |
+| `protected` | boolean | no | Whether the variable is protected |
```
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/variables" --form "key=NEW_VARIABLE" --form "value=new value"
@@ -76,6 +80,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitla
{
"key": "NEW_VARIABLE",
"value": "new value",
+ "variable_type": "env_var",
"protected": false
}
```
@@ -88,12 +93,13 @@ Update a project's variable.
PUT /projects/:id/variables/:key
```
-| Attribute | Type | required | Description |
-|-------------|---------|----------|-------------------------|
-| `id` | integer/string | yes | The ID of a project or [urlencoded NAMESPACE/PROJECT_NAME of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `key` | string | yes | The `key` of a variable |
-| `value` | string | yes | The `value` of a variable |
-| `protected` | boolean | no | Whether the variable is protected |
+| Attribute | Type | required | Description |
+|-----------------|---------|----------|-------------------------|
+| `id` | integer/string | yes | The ID of a project or [urlencoded NAMESPACE/PROJECT_NAME of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `key` | string | yes | The `key` of a variable |
+| `value` | string | yes | The `value` of a variable |
+| `variable_type` | string | no | The type of a variable. Available types are: `env_var` (default) and `file` |
+| `protected` | boolean | no | Whether the variable is protected |
```
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/variables/NEW_VARIABLE" --form "value=updated value"
@@ -103,6 +109,7 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab
{
"key": "NEW_VARIABLE",
"value": "updated value",
+ "variable_type": "env_var",
"protected": true
}
```
diff --git a/doc/user/project/labels.md b/doc/user/project/labels.md
index 9003018a521..ac91cd4ea98 100644
--- a/doc/user/project/labels.md
+++ b/doc/user/project/labels.md
@@ -53,7 +53,7 @@ be able to advance workflow states consistently in issues themselves.
## Creating labels
>**Note:**
-A permission level of `Developer` or higher is required to create labels.
+A permission level of Reporter or higher is required to create labels.
### New project label
@@ -91,7 +91,7 @@ From the sidebar of an issue or a merge request, you can create a new **project
## Editing labels
NOTE: **Note:**
-A permission level of `Developer` or higher is required to edit labels.
+A permission level of Reporter or higher is required to edit labels.
You can update a label by navigating to **Issues > Labels** in the project or group and clicking the pencil icon.
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index a228614f684..90ed24a2ded 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -1288,7 +1288,7 @@ module API
end
class Variable < Grape::Entity
- expose :key, :value
+ expose :variable_type, :key, :value
expose :protected?, as: :protected, if: -> (entity, _) { entity.respond_to?(:protected?) }
end
diff --git a/lib/api/group_variables.rb b/lib/api/group_variables.rb
index 3f048e0dc56..47fcbabb4d4 100644
--- a/lib/api/group_variables.rb
+++ b/lib/api/group_variables.rb
@@ -47,6 +47,7 @@ module API
requires :key, type: String, desc: 'The key of the variable'
requires :value, type: String, desc: 'The value of the variable'
optional :protected, type: String, desc: 'Whether the variable is protected'
+ optional :variable_type, type: String, values: Ci::GroupVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var'
end
post ':id/variables' do
variable_params = declared_params(include_missing: false)
@@ -67,6 +68,7 @@ module API
optional :key, type: String, desc: 'The key of the variable'
optional :value, type: String, desc: 'The value of the variable'
optional :protected, type: String, desc: 'Whether the variable is protected'
+ optional :variable_type, type: String, values: Ci::GroupVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file'
end
# rubocop: disable CodeReuse/ActiveRecord
put ':id/variables/:key' do
diff --git a/lib/api/pipeline_schedules.rb b/lib/api/pipeline_schedules.rb
index c86b50d3736..1d1ef1afc6b 100644
--- a/lib/api/pipeline_schedules.rb
+++ b/lib/api/pipeline_schedules.rb
@@ -118,6 +118,7 @@ module API
requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
requires :key, type: String, desc: 'The key of the variable'
requires :value, type: String, desc: 'The value of the variable'
+ optional :variable_type, type: String, values: Ci::PipelineScheduleVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var'
end
post ':id/pipeline_schedules/:pipeline_schedule_id/variables' do
authorize! :update_pipeline_schedule, pipeline_schedule
@@ -138,6 +139,7 @@ module API
requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
requires :key, type: String, desc: 'The key of the variable'
optional :value, type: String, desc: 'The value of the variable'
+ optional :variable_type, type: String, values: Ci::PipelineScheduleVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file'
end
put ':id/pipeline_schedules/:pipeline_schedule_id/variables/:key' do
authorize! :update_pipeline_schedule, pipeline_schedule
diff --git a/lib/api/variables.rb b/lib/api/variables.rb
index 3489ba827e4..a1bb21b3a06 100644
--- a/lib/api/variables.rb
+++ b/lib/api/variables.rb
@@ -55,6 +55,7 @@ module API
requires :key, type: String, desc: 'The key of the variable'
requires :value, type: String, desc: 'The value of the variable'
optional :protected, type: String, desc: 'Whether the variable is protected'
+ optional :variable_type, type: String, values: Ci::Variable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var'
if Gitlab.ee?
optional :environment_scope, type: String, desc: 'The environment_scope of the variable'
@@ -80,6 +81,7 @@ module API
optional :key, type: String, desc: 'The key of the variable'
optional :value, type: String, desc: 'The value of the variable'
optional :protected, type: String, desc: 'Whether the variable is protected'
+ optional :variable_type, type: String, values: Ci::Variable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file'
if Gitlab.ee?
optional :environment_scope, type: String, desc: 'The environment_scope of the variable'
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 122f31fec44..445984c7847 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -811,9 +811,6 @@ msgstr ""
msgid "Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication"
msgstr ""
-msgid "An SSH key will be automatically generated when the form is submitted. For more information, please refer to the documentation."
-msgstr ""
-
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr ""
@@ -2811,9 +2808,6 @@ msgstr ""
msgid "Copy SSH public key"
msgstr ""
-msgid "Copy SSH public key to clipboard"
-msgstr ""
-
msgid "Copy URL to clipboard"
msgstr ""
@@ -4762,9 +4756,6 @@ msgstr ""
msgid "Help page text and support page url."
msgstr ""
-msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
-msgstr ""
-
msgid "Hide file browser"
msgstr ""
@@ -10154,9 +10145,6 @@ msgstr ""
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
-msgid "Unable to regenerate public ssh key."
-msgstr ""
-
msgid "Unable to resolve"
msgstr ""
diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb
index b3bad40a90f..84353e3d0c7 100644
--- a/qa/qa/git/repository.rb
+++ b/qa/qa/git/repository.rb
@@ -251,7 +251,7 @@ module QA
end
def netrc_already_contains_content?
- read_netrc_content.grep(/^#{netrc_content}$/).any?
+ read_netrc_content.grep(/^#{Regexp.escape(netrc_content)}$/).any?
end
end
end
diff --git a/qa/spec/git/repository_spec.rb b/qa/spec/git/repository_spec.rb
index 776dce31d4f..5198eb6f58b 100644
--- a/qa/spec/git/repository_spec.rb
+++ b/qa/spec/git/repository_spec.rb
@@ -116,6 +116,15 @@ describe QA::Git::Repository do
expect(File.read(File.join(tmp_netrc_dir, '.netrc')))
.to eq("machine foo login user password foo\n")
end
+
+ it 'adds credentials with special characters' do
+ password = %q[!"#$%&')(*+,-./:;<=>?]
+ repository.username = 'user'
+ repository.password = password
+
+ expect(File.read(File.join(tmp_netrc_dir, '.netrc')))
+ .to eq("machine foo login user password #{password}\n")
+ end
end
end
end
diff --git a/spec/controllers/projects/pipeline_schedules_controller_spec.rb b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
index eb8983a7633..850ef9c92fb 100644
--- a/spec/controllers/projects/pipeline_schedules_controller_spec.rb
+++ b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
@@ -91,7 +91,7 @@ describe Projects::PipelineSchedulesController do
context 'when variables_attributes has one variable' do
let(:schedule) do
basic_param.merge({
- variables_attributes: [{ key: 'AAA', secret_value: 'AAA123' }]
+ variables_attributes: [{ key: 'AAA', secret_value: 'AAA123', variable_type: 'file' }]
})
end
@@ -105,6 +105,7 @@ describe Projects::PipelineSchedulesController do
Ci::PipelineScheduleVariable.last.tap do |v|
expect(v.key).to eq("AAA")
expect(v.value).to eq("AAA123")
+ expect(v.variable_type).to eq("file")
end
end
end
diff --git a/spec/factories/ci/pipeline_schedule_variables.rb b/spec/factories/ci/pipeline_schedule_variables.rb
index 8d29118e310..c85b97fbfc7 100644
--- a/spec/factories/ci/pipeline_schedule_variables.rb
+++ b/spec/factories/ci/pipeline_schedule_variables.rb
@@ -2,6 +2,7 @@ FactoryBot.define do
factory :ci_pipeline_schedule_variable, class: Ci::PipelineScheduleVariable do
sequence(:key) { |n| "VARIABLE_#{n}" }
value 'VARIABLE_VALUE'
+ variable_type 'env_var'
pipeline_schedule factory: :ci_pipeline_schedule
end
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index 1259ad45791..f7de769cee9 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -217,5 +217,24 @@ describe 'Projects > Settings > Repository settings' do
expect(RepositoryCleanupWorker.jobs.count).to eq(1)
end
end
+
+ context 'with an existing mirror', :js do
+ let(:mirrored_project) { create(:project, :repository, :remote_mirror) }
+
+ before do
+ mirrored_project.add_maintainer(user)
+
+ visit project_settings_repository_path(mirrored_project)
+ end
+
+ it 'delete remote mirrors' do
+ expect(mirrored_project.remote_mirrors.count).to eq(1)
+
+ find('.js-delete-mirror').click
+ wait_for_requests
+
+ expect(mirrored_project.remote_mirrors.count).to eq(0)
+ end
+ end
end
end
diff --git a/spec/fixtures/api/schemas/pipeline_schedule.json b/spec/fixtures/api/schemas/pipeline_schedule.json
index c76c6945117..690c4a7d4e8 100644
--- a/spec/fixtures/api/schemas/pipeline_schedule.json
+++ b/spec/fixtures/api/schemas/pipeline_schedule.json
@@ -33,7 +33,7 @@
"additionalProperties": false
},
"variables": {
- "type": ["array", "null"],
+ "type": "array",
"items": { "$ref": "pipeline_schedule_variable.json" }
}
},
diff --git a/spec/fixtures/api/schemas/pipeline_schedule_variable.json b/spec/fixtures/api/schemas/pipeline_schedule_variable.json
index f7ccb2d44a0..022d36cb88c 100644
--- a/spec/fixtures/api/schemas/pipeline_schedule_variable.json
+++ b/spec/fixtures/api/schemas/pipeline_schedule_variable.json
@@ -1,8 +1,14 @@
{
- "type": ["object", "null"],
+ "type": "object",
+ "required": [
+ "key",
+ "value",
+ "variable_type"
+ ],
"properties": {
"key": { "type": "string" },
- "value": { "type": "string" }
+ "value": { "type": "string" },
+ "variable_type": { "type": "string" }
},
"additionalProperties": false
}
diff --git a/spec/models/ci/group_variable_spec.rb b/spec/models/ci/group_variable_spec.rb
index b3999765e5f..406a69f3bbc 100644
--- a/spec/models/ci/group_variable_spec.rb
+++ b/spec/models/ci/group_variable_spec.rb
@@ -5,7 +5,8 @@ require 'spec_helper'
describe Ci::GroupVariable do
subject { build(:ci_group_variable) }
- it { is_expected.to include_module(HasVariable) }
+ it_behaves_like "CI variable"
+
it { is_expected.to include_module(Presentable) }
it { is_expected.to include_module(Maskable) }
it { is_expected.to validate_uniqueness_of(:key).scoped_to(:group_id).with_message(/\(\w+\) has already been taken/) }
diff --git a/spec/models/ci/pipeline_schedule_variable_spec.rb b/spec/models/ci/pipeline_schedule_variable_spec.rb
index 3c9379ecb0d..c96a24d5042 100644
--- a/spec/models/ci/pipeline_schedule_variable_spec.rb
+++ b/spec/models/ci/pipeline_schedule_variable_spec.rb
@@ -5,5 +5,5 @@ require 'spec_helper'
describe Ci::PipelineScheduleVariable do
subject { build(:ci_pipeline_schedule_variable) }
- it { is_expected.to include_module(HasVariable) }
+ it_behaves_like "CI variable"
end
diff --git a/spec/models/ci/pipeline_variable_spec.rb b/spec/models/ci/pipeline_variable_spec.rb
index 2ecb688299a..e8c7ce088e2 100644
--- a/spec/models/ci/pipeline_variable_spec.rb
+++ b/spec/models/ci/pipeline_variable_spec.rb
@@ -5,7 +5,8 @@ require 'spec_helper'
describe Ci::PipelineVariable do
subject { build(:ci_pipeline_variable) }
- it { is_expected.to include_module(HasVariable) }
+ it_behaves_like "CI variable"
+
it { is_expected.to validate_uniqueness_of(:key).scoped_to(:pipeline_id) }
describe '#hook_attrs' do
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
index d2df6b3344e..a231c7eaed8 100644
--- a/spec/models/ci/variable_spec.rb
+++ b/spec/models/ci/variable_spec.rb
@@ -5,8 +5,9 @@ require 'spec_helper'
describe Ci::Variable do
subject { build(:ci_variable) }
+ it_behaves_like "CI variable"
+
describe 'validations' do
- it { is_expected.to include_module(HasVariable) }
it { is_expected.to include_module(Presentable) }
it { is_expected.to include_module(Maskable) }
it { is_expected.to validate_uniqueness_of(:key).scoped_to(:project_id, :environment_scope).with_message(/\(\w+\) has already been taken/) }
diff --git a/spec/requests/api/group_variables_spec.rb b/spec/requests/api/group_variables_spec.rb
index 66b9aae4b58..d50bae3dc47 100644
--- a/spec/requests/api/group_variables_spec.rb
+++ b/spec/requests/api/group_variables_spec.rb
@@ -51,6 +51,7 @@ describe API::GroupVariables do
expect(response).to have_gitlab_http_status(200)
expect(json_response['value']).to eq(variable.value)
expect(json_response['protected']).to eq(variable.protected?)
+ expect(json_response['variable_type']).to eq(variable.variable_type)
end
it 'responds with 404 Not Found if requesting non-existing variable' do
@@ -94,17 +95,19 @@ describe API::GroupVariables do
expect(json_response['key']).to eq('TEST_VARIABLE_2')
expect(json_response['value']).to eq('PROTECTED_VALUE_2')
expect(json_response['protected']).to be_truthy
+ expect(json_response['variable_type']).to eq('env_var')
end
it 'creates variable with optional attributes' do
expect do
- post api("/groups/#{group.id}/variables", user), params: { key: 'TEST_VARIABLE_2', value: 'VALUE_2' }
+ post api("/groups/#{group.id}/variables", user), params: { variable_type: 'file', key: 'TEST_VARIABLE_2', value: 'VALUE_2' }
end.to change {group.variables.count}.by(1)
expect(response).to have_gitlab_http_status(201)
expect(json_response['key']).to eq('TEST_VARIABLE_2')
expect(json_response['value']).to eq('VALUE_2')
expect(json_response['protected']).to be_falsey
+ expect(json_response['variable_type']).to eq('file')
end
it 'does not allow to duplicate variable key' do
@@ -145,7 +148,7 @@ describe API::GroupVariables do
initial_variable = group.variables.reload.first
value_before = initial_variable.value
- put api("/groups/#{group.id}/variables/#{variable.key}", user), params: { value: 'VALUE_1_UP', protected: true }
+ put api("/groups/#{group.id}/variables/#{variable.key}", user), params: { variable_type: 'file', value: 'VALUE_1_UP', protected: true }
updated_variable = group.variables.reload.first
@@ -153,6 +156,7 @@ describe API::GroupVariables do
expect(value_before).to eq(variable.value)
expect(updated_variable.value).to eq('VALUE_1_UP')
expect(updated_variable).to be_protected
+ expect(json_response['variable_type']).to eq('file')
end
it 'responds with 404 Not Found if requesting non-existing variable' do
diff --git a/spec/requests/api/pipeline_schedules_spec.rb b/spec/requests/api/pipeline_schedules_spec.rb
index 870ef34437f..072bd02f2ac 100644
--- a/spec/requests/api/pipeline_schedules_spec.rb
+++ b/spec/requests/api/pipeline_schedules_spec.rb
@@ -91,6 +91,7 @@ describe API::PipelineSchedules do
let(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: developer) }
before do
+ pipeline_schedule.variables << build(:ci_pipeline_schedule_variable)
pipeline_schedule.pipelines << build(:ci_pipeline, project: project)
end
@@ -331,13 +332,14 @@ describe API::PipelineSchedules do
it 'creates pipeline_schedule_variable' do
expect do
post api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/variables", developer),
- params: params
+ params: params.merge(variable_type: 'file')
end.to change { pipeline_schedule.variables.count }.by(1)
expect(response).to have_gitlab_http_status(:created)
expect(response).to match_response_schema('pipeline_schedule_variable')
expect(json_response['key']).to eq(params[:key])
expect(json_response['value']).to eq(params[:value])
+ expect(json_response['variable_type']).to eq('file')
end
end
@@ -389,11 +391,12 @@ describe API::PipelineSchedules do
context 'authenticated user with valid permissions' do
it 'updates pipeline_schedule_variable' do
put api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/variables/#{pipeline_schedule_variable.key}", developer),
- params: { value: 'updated_value' }
+ params: { value: 'updated_value', variable_type: 'file' }
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('pipeline_schedule_variable')
expect(json_response['value']).to eq('updated_value')
+ expect(json_response['variable_type']).to eq('file')
end
end
diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb
index 26158231444..35b3dd219f7 100644
--- a/spec/requests/api/pipelines_spec.rb
+++ b/spec/requests/api/pipelines_spec.rb
@@ -294,6 +294,7 @@ describe API::Pipelines do
expect(variable.key).to eq(expected_variable['key'])
expect(variable.value).to eq(expected_variable['value'])
+ expect(variable.variable_type).to eq(expected_variable['variable_type'])
end
end
@@ -314,7 +315,7 @@ describe API::Pipelines do
end
context 'variables given' do
- let(:variables) { [{ 'key' => 'UPLOAD_TO_S3', 'value' => 'true' }] }
+ let(:variables) { [{ 'variable_type' => 'file', 'key' => 'UPLOAD_TO_S3', 'value' => 'true' }] }
it 'creates and returns a new pipeline using the given variables' do
expect do
@@ -330,7 +331,7 @@ describe API::Pipelines do
end
describe 'using variables conditions' do
- let(:variables) { [{ 'key' => 'STAGING', 'value' => 'true' }] }
+ let(:variables) { [{ 'variable_type' => 'env_var', 'key' => 'STAGING', 'value' => 'true' }] }
before do
config = YAML.dump(test: { script: 'test', only: { variables: ['$STAGING'] } })
@@ -467,7 +468,7 @@ describe API::Pipelines do
subject
expect(response).to have_gitlab_http_status(200)
- expect(json_response).to contain_exactly({ "key" => "foo", "value" => "bar" })
+ expect(json_response).to contain_exactly({ "variable_type" => "env_var", "key" => "foo", "value" => "bar" })
end
end
end
@@ -488,7 +489,7 @@ describe API::Pipelines do
subject
expect(response).to have_gitlab_http_status(200)
- expect(json_response).to contain_exactly({ "key" => "foo", "value" => "bar" })
+ expect(json_response).to contain_exactly({ "variable_type" => "env_var", "key" => "foo", "value" => "bar" })
end
end
diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb
index 5df6baf0ddf..cc07869a744 100644
--- a/spec/requests/api/variables_spec.rb
+++ b/spec/requests/api/variables_spec.rb
@@ -43,6 +43,7 @@ describe API::Variables do
expect(response).to have_gitlab_http_status(200)
expect(json_response['value']).to eq(variable.value)
expect(json_response['protected']).to eq(variable.protected?)
+ expect(json_response['variable_type']).to eq('env_var')
end
it 'responds with 404 Not Found if requesting non-existing variable' do
@@ -80,17 +81,19 @@ describe API::Variables do
expect(json_response['key']).to eq('TEST_VARIABLE_2')
expect(json_response['value']).to eq('PROTECTED_VALUE_2')
expect(json_response['protected']).to be_truthy
+ expect(json_response['variable_type']).to eq('env_var')
end
it 'creates variable with optional attributes' do
expect do
- post api("/projects/#{project.id}/variables", user), params: { key: 'TEST_VARIABLE_2', value: 'VALUE_2' }
+ post api("/projects/#{project.id}/variables", user), params: { variable_type: 'file', key: 'TEST_VARIABLE_2', value: 'VALUE_2' }
end.to change {project.variables.count}.by(1)
expect(response).to have_gitlab_http_status(201)
expect(json_response['key']).to eq('TEST_VARIABLE_2')
expect(json_response['value']).to eq('VALUE_2')
expect(json_response['protected']).to be_falsey
+ expect(json_response['variable_type']).to eq('file')
end
it 'does not allow to duplicate variable key' do
@@ -125,7 +128,7 @@ describe API::Variables do
initial_variable = project.variables.reload.first
value_before = initial_variable.value
- put api("/projects/#{project.id}/variables/#{variable.key}", user), params: { value: 'VALUE_1_UP', protected: true }
+ put api("/projects/#{project.id}/variables/#{variable.key}", user), params: { variable_type: 'file', value: 'VALUE_1_UP', protected: true }
updated_variable = project.variables.reload.first
@@ -133,6 +136,7 @@ describe API::Variables do
expect(value_before).to eq(variable.value)
expect(updated_variable.value).to eq('VALUE_1_UP')
expect(updated_variable).to be_protected
+ expect(updated_variable.variable_type).to eq('file')
end
it 'responds with 404 Not Found if requesting non-existing variable' do
diff --git a/spec/services/lfs/file_transformer_spec.rb b/spec/services/lfs/file_transformer_spec.rb
index 2c6e86069f3..888eea6e91e 100644
--- a/spec/services/lfs/file_transformer_spec.rb
+++ b/spec/services/lfs/file_transformer_spec.rb
@@ -64,6 +64,25 @@ describe Lfs::FileTransformer do
expect(result.encoding).to eq('text')
end
+ context 'when an actual file is passed' do
+ let(:file) { Tempfile.new(file_path) }
+
+ before do
+ file.write(file_content)
+ file.rewind
+ end
+
+ after do
+ file.unlink
+ end
+
+ it "creates an LfsObject with the file's content" do
+ subject.new_file(file_path, file)
+
+ expect(LfsObject.last.file.read).to eq file_content
+ end
+ end
+
context "when doesn't use LFS" do
let(:file_path) { 'other.filetype' }
diff --git a/spec/support/shared_examples/controllers/variables_shared_examples.rb b/spec/support/shared_examples/controllers/variables_shared_examples.rb
index b615a8f54cf..e80722857ec 100644
--- a/spec/support/shared_examples/controllers/variables_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/variables_shared_examples.rb
@@ -120,4 +120,16 @@ shared_examples 'PATCH #update updates variables' do
expect(response).to match_response_schema('variables')
end
end
+
+ context 'for variables of type file' do
+ let(:variables_attributes) do
+ [
+ new_variable_attributes.merge(variable_type: 'file')
+ ]
+ end
+
+ it 'creates new variable of type file' do
+ expect { subject }.to change { owner.variables.file.count }.by(1)
+ end
+ end
end
diff --git a/spec/support/shared_examples/models/ci_variable_shared_examples.rb b/spec/support/shared_examples/models/ci_variable_shared_examples.rb
new file mode 100644
index 00000000000..f93de8b6ff1
--- /dev/null
+++ b/spec/support/shared_examples/models/ci_variable_shared_examples.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+shared_examples_for 'CI variable' do
+ it { is_expected.to include_module(HasVariable) }
+
+ describe "variable type" do
+ it 'defines variable types' do
+ expect(described_class.variable_types).to eq({ "env_var" => 1, "file" => 2 })
+ end
+
+ it "defaults variable type to env_var" do
+ expect(subject.variable_type).to eq("env_var")
+ end
+
+ it "supports variable type file" do
+ variable = described_class.new(variable_type: :file)
+ expect(variable).to be_file
+ end
+ end
+
+ it 'strips whitespaces when assigning key' do
+ subject.key = " SECRET "
+ expect(subject.key).to eq("SECRET")
+ end
+
+ it 'can convert to runner variable' do
+ expect(subject.to_runner_variable.keys).to include(:key, :value, :public, :file)
+ end
+end