Commit f5abc9f6 authored by Valery Sizov's avatar Valery Sizov

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into ce_upstream

parents b99e3ca2 b5b4054d
<<<<<<< HEAD
5.1.0 5.1.0
=======
5.1.1
>>>>>>> b5b4054d5882782892d0a860c7e95db9a22bfdec
import autosize from 'vendor/autosize'; import autosize from 'vendor/autosize';
$(() => { document.addEventListener('DOMContentLoaded', () => {
const $fields = $('.js-autosize'); const autosizeEls = document.querySelectorAll('.js-autosize');
$fields.on('autosize:resized', function resized() { autosize(autosizeEls);
const $field = $(this); autosize.update(autosizeEls);
$field.data('height', $field.outerHeight());
});
$fields.on('resize.autosize', function resize() {
const $field = $(this);
if ($field.data('height') !== $field.outerHeight()) {
$field.data('height', $field.outerHeight());
autosize.destroy($field);
$field.css('max-height', window.outerHeight);
}
});
autosize($fields);
autosize.update($fields);
$fields.css('resize', 'vertical');
}); });
...@@ -33,3 +33,20 @@ ...@@ -33,3 +33,20 @@
font-weight: normal; font-weight: normal;
} }
} }
.admin-runner-btn-group-cell {
min-width: 150px;
.btn-sm {
padding: 4px 9px;
}
.btn-default {
color: $gl-text-color-secondary;
}
.fa-pause,
.fa-play {
font-size: 11px;
}
}
...@@ -5,7 +5,7 @@ module Ci ...@@ -5,7 +5,7 @@ module Ci
belongs_to :project belongs_to :project
validates :key, uniqueness: { scope: :project_id } validates :key, uniqueness: { scope: [:project_id, :environment_scope] }
scope :unprotected, -> { where(protected: false) } scope :unprotected, -> { where(protected: false) }
end end
......
...@@ -103,8 +103,12 @@ module Routable ...@@ -103,8 +103,12 @@ module Routable
def full_path def full_path
return uncached_full_path unless RequestStore.active? return uncached_full_path unless RequestStore.active?
key = "routable/full_path/#{self.class.name}/#{self.id}" RequestStore[full_path_key] ||= uncached_full_path
RequestStore[key] ||= uncached_full_path end
def expires_full_path_cache
RequestStore.delete(full_path_key) if RequestStore.active?
@full_path = nil
end end
def build_full_path def build_full_path
...@@ -135,6 +139,10 @@ module Routable ...@@ -135,6 +139,10 @@ module Routable
path_changed? || parent_changed? path_changed? || parent_changed?
end end
def full_path_key
@full_path_key ||= "routable/full_path/#{self.class.name}/#{self.id}"
end
def build_full_name def build_full_name
if parent && name if parent && name
parent.human_name + ' / ' + name parent.human_name + ' / ' + name
......
...@@ -964,6 +964,7 @@ class Project < ActiveRecord::Base ...@@ -964,6 +964,7 @@ class Project < ActiveRecord::Base
begin begin
gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki") gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
send_move_instructions(old_path_with_namespace) send_move_instructions(old_path_with_namespace)
expires_full_path_cache
@old_path_with_namespace = old_path_with_namespace @old_path_with_namespace = old_path_with_namespace
...@@ -1074,21 +1075,21 @@ class Project < ActiveRecord::Base ...@@ -1074,21 +1075,21 @@ class Project < ActiveRecord::Base
merge_requests.where(source_project_id: self.id) merge_requests.where(source_project_id: self.id)
end end
def create_repository def create_repository(force: false)
# Forked import is handled asynchronously # Forked import is handled asynchronously
unless forked? return if forked? && !force
if gitlab_shell.add_repository(repository_storage_path, path_with_namespace)
repository.after_create if gitlab_shell.add_repository(repository_storage_path, path_with_namespace)
true repository.after_create
else true
errors.add(:base, 'Failed to create repository via gitlab-shell') else
false errors.add(:base, 'Failed to create repository via gitlab-shell')
end false
end end
end end
def ensure_repository def ensure_repository
create_repository unless repository_exists? create_repository(force: true) unless repository_exists?
end end
def repository_exists? def repository_exists?
......
...@@ -78,6 +78,7 @@ module Projects ...@@ -78,6 +78,7 @@ module Projects
Gitlab::PagesTransfer.new.move_project(project.path, @old_namespace.full_path, @new_namespace.full_path) Gitlab::PagesTransfer.new.move_project(project.path, @old_namespace.full_path, @new_namespace.full_path)
project.old_path_with_namespace = @old_path project.old_path_with_namespace = @old_path
project.expires_full_path_cache
execute_system_hooks execute_system_hooks
end end
......
...@@ -32,13 +32,16 @@ ...@@ -32,13 +32,16 @@
#{time_ago_in_words(runner.contacted_at)} ago #{time_ago_in_words(runner.contacted_at)} ago
- else - else
Never Never
%td %td.admin-runner-btn-group-cell
.pull-right .pull-right.btn-group
= link_to 'Edit', admin_runner_path(runner), class: 'btn btn-sm' = link_to admin_runner_path(runner), class: 'btn btn-sm btn-default has-tooltip', title: 'Edit', ref: 'tooltip', aria: { label: 'Edit' }, data: { placement: 'top', container: 'body'} do
= icon('pencil')
&nbsp; &nbsp;
- if runner.active? - if runner.active?
= link_to 'Pause', [:pause, :admin, runner], data: { confirm: "Are you sure?" }, method: :get, class: 'btn btn-danger btn-sm' = link_to [:pause, :admin, runner], method: :get, class: 'btn btn-sm btn-default has-tooltip', title: 'Pause', ref: 'tooltip', aria: { label: 'Pause' }, data: { placement: 'top', container: 'body', confirm: "Are you sure?" } do
= icon('pause')
- else - else
= link_to 'Resume', [:resume, :admin, runner], method: :get, class: 'btn btn-success btn-sm' = link_to [:resume, :admin, runner], method: :get, class: 'btn btn-default btn-sm has-tooltip', title: 'Resume', ref: 'tooltip', aria: { label: 'Resume' }, data: { placement: 'top', container: 'body'} do
= link_to 'Remove', [:admin, runner], data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' = icon('play')
= link_to [:admin, runner], method: :delete, class: 'btn btn-danger btn-sm has-tooltip', title: 'Remove', ref: 'tooltip', aria: { label: 'Remove' }, data: { placement: 'top', container: 'body', confirm: "Are you sure?" } do
= icon('remove')
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
%div %div
- if Gitlab::Recaptcha.enabled? - if Gitlab::Recaptcha.enabled?
= recaptcha_tags = recaptcha_tags
%div .submit-container
= f.submit "Register", class: "btn-register btn" = f.submit "Register", class: "btn-register btn"
.clearfix.submit-container .clearfix.submit-container
%p %p
......
...@@ -7,10 +7,9 @@ ...@@ -7,10 +7,9 @@
.form-group .form-group
.col-sm-10.col-sm-offset-2 .col-sm-10.col-sm-offset-2
.checkbox .checkbox
- initial_checkbox_value = issuable.merge_params.key?('force_remove_source_branch') ? issuable.force_remove_source_branch? : true
= label_tag 'merge_request[force_remove_source_branch]' do = label_tag 'merge_request[force_remove_source_branch]' do
= hidden_field_tag 'merge_request[force_remove_source_branch]', '0', id: nil = hidden_field_tag 'merge_request[force_remove_source_branch]', '0', id: nil
= check_box_tag 'merge_request[force_remove_source_branch]', '1', initial_checkbox_value = check_box_tag 'merge_request[force_remove_source_branch]', '1', issuable.force_remove_source_branch?
Remove source branch when merge request is accepted. Remove source branch when merge request is accepted.
= render 'shared/issuable/form/ee/squash_merge_param', issuable: issuable = render 'shared/issuable/form/ee/squash_merge_param', issuable: issuable
---
title: Fix spacing on runner buttons.
merge_request: !12535
author:
---
title: Set default for Remove source branch to false.
merge_request: !12576
author:
---
title: Rename duplicated variables with the same key for projects. Add environment_scope
column to variables and add unique constraint to make sure that no variables could
be created with the same key within a project
merge_request: 12363
author:
---
title: Expires full_path cache after a repository is renamed/transferred
merge_request:
author:
---
title: Make Project#ensure_repository force create a repo
merge_request:
author:
class RenameDuplicatedVariableKey < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
execute(<<~SQL)
UPDATE ci_variables
SET #{key} = CONCAT(#{key}, #{underscore}, id)
WHERE id IN (
SELECT *
FROM ( -- MySQL requires an extra layer
SELECT dup.id
FROM ci_variables dup
INNER JOIN (SELECT max(id) AS id, #{key}, project_id
FROM ci_variables tmp
GROUP BY #{key}, project_id) var
USING (#{key}, project_id) where dup.id <> var.id
) dummy
)
SQL
end
def down
# noop
end
def key
# key needs to be quoted in MySQL
quote_column_name('key')
end
def underscore
quote('_')
end
end
class AddEnvironmentScopeToCiVariables < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default(:ci_variables, :environment_scope, :string, default: '*')
end
def down
remove_column(:ci_variables, :environment_scope)
end
end
class AddUniqueConstraintToCiVariables < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
unless this_index_exists?
add_concurrent_index(:ci_variables, columns, name: index_name, unique: true)
end
end
def down
if this_index_exists?
if Gitlab::Database.mysql? && !index_exists?(:ci_variables, :project_id)
# Need to add this index for MySQL project_id foreign key constraint
add_concurrent_index(:ci_variables, :project_id)
end
remove_concurrent_index(:ci_variables, columns, name: index_name)
end
end
private
def this_index_exists?
index_exists?(:ci_variables, columns, name: index_name)
end
def columns
@columns ||= [:project_id, :key, :environment_scope]
end
def index_name
'index_ci_variables_on_project_id_and_key_and_environment_scope'
end
end
class RemoveCiVariablesProjectIdIndex < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
if index_exists?(:ci_variables, :project_id)
remove_concurrent_index(:ci_variables, :project_id)
end
end
def down
unless index_exists?(:ci_variables, :project_id)
add_concurrent_index(:ci_variables, :project_id)
end
end
end
...@@ -11,7 +11,11 @@ ...@@ -11,7 +11,11 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
<<<<<<< HEAD
ActiveRecord::Schema.define(version: 20170627211700) do ActiveRecord::Schema.define(version: 20170627211700) do
=======
ActiveRecord::Schema.define(version: 20170623080805) do
>>>>>>> b5b4054d5882782892d0a860c7e95db9a22bfdec
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -442,9 +446,10 @@ ActiveRecord::Schema.define(version: 20170627211700) do ...@@ -442,9 +446,10 @@ ActiveRecord::Schema.define(version: 20170627211700) do
t.string "encrypted_value_iv" t.string "encrypted_value_iv"
t.integer "project_id", null: false t.integer "project_id", null: false
t.boolean "protected", default: false, null: false t.boolean "protected", default: false, null: false
t.string "environment_scope", default: "*", null: false
end end
add_index "ci_variables", ["project_id"], name: "index_ci_variables_on_project_id", using: :btree add_index "ci_variables", ["project_id", "key", "environment_scope"], name: "index_ci_variables_on_project_id_and_key_and_environment_scope", unique: true, using: :btree
create_table "container_repositories", force: :cascade do |t| create_table "container_repositories", force: :cascade do |t|
t.integer "project_id", null: false t.integer "project_id", null: false
......
...@@ -31,7 +31,11 @@ You create issues, host code, perform reviews, build, test, ...@@ -31,7 +31,11 @@ You create issues, host code, perform reviews, build, test,
and deploy from one single platform. Issue Boards help you to visualize and deploy from one single platform. Issue Boards help you to visualize
and manage the entire process _in_ GitLab. and manage the entire process _in_ GitLab.
<<<<<<< HEAD
With [Multiple Issue Boards](#multiple-issue-boards), available With [Multiple Issue Boards](#multiple-issue-boards), available
=======
With [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards), available
>>>>>>> b5b4054d5882782892d0a860c7e95db9a22bfdec
only in [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/), only in [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/),
you go even further, as you can not only keep yourself and your project you go even further, as you can not only keep yourself and your project
organized from a broader perspective with one Issue Board per project, organized from a broader perspective with one Issue Board per project,
...@@ -40,10 +44,13 @@ multiple Issue Boards within the same project. ...@@ -40,10 +44,13 @@ multiple Issue Boards within the same project.
## Use cases ## Use cases
<<<<<<< HEAD
You can see below a few different use cases for GitLab's Issue Boards. You can see below a few different use cases for GitLab's Issue Boards.
### Use cases for a single Issue Board ### Use cases for a single Issue Board
=======
>>>>>>> b5b4054d5882782892d0a860c7e95db9a22bfdec
GitLab Workflow allows you to discuss proposals in issues, categorize them GitLab Workflow allows you to discuss proposals in issues, categorize them
with labels, and from there organize and prioritize them with Issue Boards. with labels, and from there organize and prioritize them with Issue Boards.
...@@ -67,6 +74,7 @@ beginning of the development lifecycle until deployed to production ...@@ -67,6 +74,7 @@ beginning of the development lifecycle until deployed to production
![issue card moving](img/issue_board_move_issue_card_list.png) ![issue card moving](img/issue_board_move_issue_card_list.png)
<<<<<<< HEAD
### Use cases for Multiple Issue Boards ### Use cases for Multiple Issue Boards
With [Multiple Issue Boards](#multiple-issue-boards), available only in With [Multiple Issue Boards](#multiple-issue-boards), available only in
...@@ -101,6 +109,8 @@ When done with something, they move the card to **Frontend**. The Frontend team' ...@@ -101,6 +109,8 @@ When done with something, they move the card to **Frontend**. The Frontend team'
Cards finished by the UX team will automatically appear in the **Frontend** column when they're ready for them. Cards finished by the UX team will automatically appear in the **Frontend** column when they're ready for them.
=======
>>>>>>> b5b4054d5882782892d0a860c7e95db9a22bfdec
> **Notes:** > **Notes:**
> >
>- For a broader use case, please check the blog post >- For a broader use case, please check the blog post
...@@ -108,8 +118,12 @@ Cards finished by the UX team will automatically appear in the **Frontend** colu ...@@ -108,8 +118,12 @@ Cards finished by the UX team will automatically appear in the **Frontend** colu
> >
>- For a real use case, please check why >- For a real use case, please check why
[Codepen decided to adopt Issue Boards](https://about.gitlab.com/2017/01/27/codepen-welcome-to-gitlab/#project-management-everything-in-one-place) [Codepen decided to adopt Issue Boards](https://about.gitlab.com/2017/01/27/codepen-welcome-to-gitlab/#project-management-everything-in-one-place)
<<<<<<< HEAD
to improve their workflow with to improve their workflow with
multiple boards. multiple boards.
=======
to improve their workflow with [multiple boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards).
>>>>>>> b5b4054d5882782892d0a860c7e95db9a22bfdec
## Issue Board terminology ## Issue Board terminology
......
...@@ -2,6 +2,8 @@ require 'securerandom' ...@@ -2,6 +2,8 @@ require 'securerandom'
module Gitlab module Gitlab
class Shell class Shell
GITLAB_SHELL_ENV_VARS = %w(GIT_TERMINAL_PROMPT).freeze
Error = Class.new(StandardError) Error = Class.new(StandardError)
KeyAdder = Struct.new(:io) do KeyAdder = Struct.new(:io) do
...@@ -67,8 +69,8 @@ module Gitlab ...@@ -67,8 +69,8 @@ module Gitlab
# add_repository("/path/to/storage", "gitlab/gitlab-ci") # add_repository("/path/to/storage", "gitlab/gitlab-ci")
# #
def add_repository(storage, name) def add_repository(storage, name)
Gitlab::Utils.system_silent([gitlab_shell_projects_path, gitlab_shell_fast_execute([gitlab_shell_projects_path,
'add-project', storage, "#{name}.git"]) 'add-project', storage, "#{name}.git"])
end end
# Import repository # Import repository
...@@ -82,10 +84,9 @@ module Gitlab ...@@ -82,10 +84,9 @@ module Gitlab
def import_repository(storage, name, url) def import_repository(storage, name, url)
# Timeout should be less than 900 ideally, to prevent the memory killer # Timeout should be less than 900 ideally, to prevent the memory killer
# to silently kill the process without knowing we are timing out here. # to silently kill the process without knowing we are timing out here.
output, status = Popen.popen([gitlab_shell_projects_path, 'import-project', cmd = [gitlab_shell_projects_path, 'import-project',
storage, "#{name}.git", url, "#{Gitlab.config.gitlab_shell.git_timeout}"]) storage, "#{name}.git", url, "#{Gitlab.config.gitlab_shell.git_timeout}"]
raise Error, output unless status.zero? gitlab_shell_fast_execute_raise_error(cmd)
true
end end
def list_remote_tags(storage, name, remote) def list_remote_tags(storage, name, remote)
...@@ -131,9 +132,7 @@ module Gitlab ...@@ -131,9 +132,7 @@ module Gitlab
args << '--force' if forced args << '--force' if forced
args << '--no-tags' if no_tags args << '--no-tags' if no_tags
output, status = Popen.popen(args) gitlab_shell_fast_execute_raise_error(args)
raise Error, output unless status.zero?
true
end end
# Move repository # Move repository
...@@ -145,8 +144,8 @@ module Gitlab ...@@ -145,8 +144,8 @@ module Gitlab
# mv_repository("/path/to/storage", "gitlab/gitlab-ci", "randx/gitlab-ci-new") # mv_repository("/path/to/storage", "gitlab/gitlab-ci", "randx/gitlab-ci-new")
# #
def mv_repository(storage, path, new_path) def mv_repository(storage, path, new_path)
Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'mv-project', gitlab_shell_fast_execute([gitlab_shell_projects_path, 'mv-project',
storage, "#{path}.git", "#{new_path}.git"]) storage, "#{path}.git", "#{new_path}.git"])
end end
# Move repository storage # Move repository storage
...@@ -173,9 +172,9 @@ module Gitlab ...@@ -173,9 +172,9 @@ module Gitlab
# fork_repository("/path/to/forked_from/storage", "gitlab/gitlab-ci", "/path/to/forked_to/storage", "randx") # fork_repository("/path/to/forked_from/storage", "gitlab/gitlab-ci", "/path/to/forked_to/storage", "randx")
# #
def fork_repository(forked_from_storage, path, forked_to_storage, fork_namespace) def fork_repository(forked_from_storage, path, forked_to_storage, fork_namespace)
Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'fork-project', gitlab_shell_fast_execute([gitlab_shell_projects_path, 'fork-project',
forked_from_storage, "#{path}.git", forked_to_storage, forked_from_storage, "#{path}.git", forked_to_storage,
fork_namespace]) fork_namespace])
end end
# Remove repository from file system # Remove repository from file system
...@@ -187,8 +186,8 @@ module Gitlab ...@@ -187,8 +186,8 @@ module Gitlab
# remove_repository("/path/to/storage", "gitlab/gitlab-ci") # remove_repository("/path/to/storage", "gitlab/gitlab-ci")
# #
def remove_repository(storage, name) def remove_repository(storage, name)
Gitlab::Utils.system_silent([gitlab_shell_projects_path, gitlab_shell_fast_execute([gitlab_shell_projects_path,
'rm-project', storage, "#{name}.git"]) 'rm-project', storage, "#{name}.git"])
end end
# Add new key to gitlab-shell # Add new key to gitlab-shell
...@@ -197,10 +196,15 @@ module Gitlab ...@@ -197,10 +196,15 @@ module Gitlab
# add_key("key-42", "sha-rsa ...") # add_key("key-42", "sha-rsa ...")
# #
def add_key(key_id, key_content) def add_key(key_id, key_content)
<<<<<<< HEAD
return unless self.authorized_keys_enabled? return unless self.authorized_keys_enabled?
Gitlab::Utils.system_silent([gitlab_shell_keys_path, Gitlab::Utils.system_silent([gitlab_shell_keys_path,
'add-key', key_id, self.class.strip_key(key_content)]) 'add-key', key_id, self.class.strip_key(key_content)])
=======
gitlab_shell_fast_execute([gitlab_shell_keys_path,
'add-key', key_id, self.class.strip_key(key_content)])
>>>>>>> b5b4054d5882782892d0a860c7e95db9a22bfdec
end end
# Batch-add keys to authorized_keys # Batch-add keys to authorized_keys
...@@ -220,12 +224,20 @@ module Gitlab ...@@ -220,12 +224,20 @@ module Gitlab
# Ex. # Ex.
# remove_key("key-342", "sha-rsa ...") # remove_key("key-342", "sha-rsa ...")
# #
<<<<<<< HEAD
def remove_key(key_id, key_content = nil) def remove_key(key_id, key_content = nil)
return unless self.authorized_keys_enabled? return unless self.authorized_keys_enabled?
args = [gitlab_shell_keys_path, 'rm-key', key_id] args = [gitlab_shell_keys_path, 'rm-key', key_id]
args << key_content if key_content args << key_content if key_content
Gitlab::Utils.system_silent(args) Gitlab::Utils.system_silent(args)
=======
def remove_key(key_id, key_content)
args = [gitlab_shell_keys_path, 'rm-key', key_id]
args << key_content if key_content
gitlab_shell_fast_execute(args)
>>>>>>> b5b4054d5882782892d0a860c7e95db9a22bfdec
end end
# Remove all ssh keys from gitlab shell # Remove all ssh keys from gitlab shell
...@@ -234,9 +246,13 @@ module Gitlab ...@@ -234,9 +246,13 @@ module Gitlab
# remove_all_keys # remove_all_keys
# #
def remove_all_keys def remove_all_keys
<<<<<<< HEAD
return unless self.authorized_keys_enabled? return unless self.authorized_keys_enabled?
Gitlab::Utils.system_silent([gitlab_shell_keys_path, 'clear']) Gitlab::Utils.system_silent([gitlab_shell_keys_path, 'clear'])
=======
gitlab_shell_fast_execute([gitlab_shell_keys_path, 'clear'])
>>>>>>> b5b4054d5882782892d0a860c7e95db9a22bfdec
end end
# Remove ssh keys from gitlab shell that are not in the DB # Remove ssh keys from gitlab shell that are not in the DB
...@@ -417,12 +433,39 @@ module Gitlab ...@@ -417,12 +433,39 @@ module Gitlab
File.join(gitlab_shell_path, 'bin', 'gitlab-keys') File.join(gitlab_shell_path, 'bin', 'gitlab-keys')
end end
<<<<<<< HEAD
def authorized_keys_enabled? def authorized_keys_enabled?
# Return true if nil to ensure the authorized_keys methods work while # Return true if nil to ensure the authorized_keys methods work while
# fixing the authorized_keys file during migration. # fixing the authorized_keys file during migration.
return true if current_application_settings.authorized_keys_enabled.nil? return true if current_application_settings.authorized_keys_enabled.nil?
current_application_settings.authorized_keys_enabled current_application_settings.authorized_keys_enabled
=======
private
def gitlab_shell_fast_execute(cmd)
output, status = gitlab_shell_fast_execute_helper(cmd)
return true if status.zero?
Rails.logger.error("gitlab-shell failed with error #{status}: #{output}")
false
end
def gitlab_shell_fast_execute_raise_error(cmd)
output, status = gitlab_shell_fast_execute_helper(cmd)
raise Error, output unless status.zero?
true
end
def gitlab_shell_fast_execute_helper(cmd)
vars = ENV.to_h.slice(*GITLAB_SHELL_ENV_VARS)
# Don't pass along the entire parent environment to prevent gitlab-shell
# from wasting I/O by searching through GEM_PATH
Bundler.with_original_env { Popen.popen(cmd, nil, vars) }
>>>>>>> b5b4054d5882782892d0a860c7e95db9a22bfdec
end end
end end
end end
...@@ -32,6 +32,17 @@ describe 'Gitlab::Popen', lib: true, no_db: true do ...@@ -32,6 +32,17 @@ describe 'Gitlab::Popen', lib: true, no_db: true do
end end
end end
context 'with custom options' do
let(:vars) { { 'foobar' => 123, 'PWD' => path } }
let(:options) { { chdir: path } }
it 'calls popen3 with the provided environment variables' do
expect(Open3).to receive(:popen3).with(vars, 'ls', options)
@output, @status = @klass.new.popen(%w(ls), path, { 'foobar' => 123 })
end
end
context 'without a directory argument' do context 'without a directory argument' do
before do before do
@output, @status = @klass.new.popen(%w(ls)) @output, @status = @klass.new.popen(%w(ls))
...@@ -45,7 +56,7 @@ describe 'Gitlab::Popen', lib: true, no_db: true do ...@@ -45,7 +56,7 @@ describe 'Gitlab::Popen', lib: true, no_db: true do
before do before do
@output, @status = @klass.new.popen(%w[cat]) { |stdin| stdin.write 'hello' } @output, @status = @klass.new.popen(%w[cat]) { |stdin| stdin.write 'hello' }
end end
it { expect(@status).to be_zero } it { expect(@status).to be_zero }
it { expect(@output).to eq('hello') } it { expect(@output).to eq('hello') }
end end
......
...@@ -4,6 +4,7 @@ require 'stringio' ...@@ -4,6 +4,7 @@ require 'stringio'
describe Gitlab::Shell, lib: true do describe Gitlab::Shell, lib: true do
let(:project) { double('Project', id: 7, path: 'diaspora') } let(:project) { double('Project', id: 7, path: 'diaspora') }
let(:gitlab_shell) { Gitlab::Shell.new } let(:gitlab_shell) { Gitlab::Shell.new }
let(:popen_vars) { { 'GIT_TERMINAL_PROMPT' => ENV['GIT_TERMINAL_PROMPT'] } }
before do before do
allow(Project).to receive(:find).and_return(project) allow(Project).to receive(:find).and_return(project)
...@@ -104,6 +105,7 @@ describe Gitlab::Shell, lib: true do ...@@ -104,6 +105,7 @@ describe Gitlab::Shell, lib: true do
end end
describe '#add_key' do describe '#add_key' do
<<<<<<< HEAD
context 'when authorized_keys_enabled is true' do context 'when authorized_keys_enabled is true' do
it 'removes trailing garbage' do it 'removes trailing garbage' do
allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path) allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path)
...@@ -167,6 +169,13 @@ describe Gitlab::Shell, lib: true do ...@@ -167,6 +169,13 @@ describe Gitlab::Shell, lib: true do
end end
end end
end end
=======
it 'removes trailing garbage' do
allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path)
expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with(
[:gitlab_shell_keys_path, 'add-key', 'key-123', 'ssh-rsa foobar']
)
>>>>>>> b5b4054d5882782892d0a860c7e95db9a22bfdec
context 'when authorized_keys_enabled is nil' do context 'when authorized_keys_enabled is nil' do
before do before do
...@@ -448,17 +457,91 @@ describe Gitlab::Shell, lib: true do ...@@ -448,17 +457,91 @@ describe Gitlab::Shell, lib: true do
allow(Gitlab.config.gitlab_shell).to receive(:git_timeout).and_return(800) allow(Gitlab.config.gitlab_shell).to receive(:git_timeout).and_return(800)
end end
describe '#add_repository' do
it 'returns true when the command succeeds' do
expect(Gitlab::Popen).to receive(:popen)
.with([projects_path, 'add-project', 'current/storage', 'project/path.git'],
nil, popen_vars).and_return([nil, 0])
expect(gitlab_shell.add_repository('current/storage', 'project/path')).to be true
end
it 'returns false when the command fails' do
expect(Gitlab::Popen).to receive(:popen)
.with([projects_path, 'add-project', 'current/storage', 'project/path.git'],
nil, popen_vars).and_return(["error", 1])
expect(gitlab_shell.add_repository('current/storage', 'project/path')).to be false
end
end
describe '#remove_repository' do
it 'returns true when the command succeeds' do
expect(Gitlab::Popen).to receive(:popen)
.with([projects_path, 'rm-project', 'current/storage', 'project/path.git'],
nil, popen_vars).and_return([nil, 0])
expect(gitlab_shell.remove_repository('current/storage', 'project/path')).to be true
end
it 'returns false when the command fails' do
expect(Gitlab::Popen).to receive(:popen)
.with([projects_path, 'rm-project', 'current/storage', 'project/path.git'],
nil, popen_vars).and_return(["error", 1])
expect(gitlab_shell.remove_repository('current/storage', 'project/path')).to be false
end
end
describe '#mv_repository' do
it 'returns true when the command succeeds' do
expect(Gitlab::Popen).to receive(:popen)
.with([projects_path, 'mv-project', 'current/storage', 'project/path.git', 'project/newpath.git'],
nil, popen_vars).and_return([nil, 0])
expect(gitlab_shell.mv_repository('current/storage', 'project/path', 'project/newpath')).to be true
end
it 'returns false when the command fails' do
expect(Gitlab::Popen).to receive(:popen)
.with([projects_path, 'mv-project', 'current/storage', 'project/path.git', 'project/newpath.git'],
nil, popen_vars).and_return(["error", 1])
expect(gitlab_shell.mv_repository('current/storage', 'project/path', 'project/newpath')).to be false
end
end
describe '#fork_repository' do
it 'returns true when the command succeeds' do
expect(Gitlab::Popen).to receive(:popen)
.with([projects_path, 'fork-project', 'current/storage', 'project/path.git', 'new/storage', 'new-namespace'],
nil, popen_vars).and_return([nil, 0])
expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'new-namespace')).to be true
end
it 'return false when the command fails' do
expect(Gitlab::Popen).to receive(:popen)
.with([projects_path, 'fork-project', 'current/storage', 'project/path.git', 'new/storage', 'new-namespace'],
nil, popen_vars).and_return(["error", 1])
expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'new-namespace')).to be false
end
end
describe '#fetch_remote' do describe '#fetch_remote' do
it 'returns true when the command succeeds' do it 'returns true when the command succeeds' do
expect(Gitlab::Popen).to receive(:popen) expect(Gitlab::Popen).to receive(:popen)
.with([projects_path, 'fetch-remote', 'current/storage', 'project/path.git', 'new/storage', '800']).and_return([nil, 0]) .with([projects_path, 'fetch-remote', 'current/storage', 'project/path.git', 'new/storage', '800'],
nil, popen_vars).and_return([nil, 0])
expect(gitlab_shell.fetch_remote('current/storage', 'project/path', 'new/storage')).to be true expect(gitlab_shell.fetch_remote('current/storage', 'project/path', 'new/storage')).to be true
end end
it 'raises an exception when the command fails' do it 'raises an exception when the command fails' do
expect(Gitlab::Popen).to receive(:popen) expect(Gitlab::Popen).to receive(:popen)
.with([projects_path, 'fetch-remote', 'current/storage', 'project/path.git', 'new/storage', '800']).and_return(["error", 1]) .with([projects_path, 'fetch-remote', 'current/storage', 'project/path.git', 'new/storage', '800'],
nil, popen_vars).and_return(["error", 1])
expect { gitlab_shell.fetch_remote('current/storage', 'project/path', 'new/storage') }.to raise_error(Gitlab::Shell::Error, "error") expect { gitlab_shell.fetch_remote('current/storage', 'project/path', 'new/storage') }.to raise_error(Gitlab::Shell::Error, "error")
end end
...@@ -467,14 +550,16 @@ describe Gitlab::Shell, lib: true do ...@@ -467,14 +550,16 @@ describe Gitlab::Shell, lib: true do
describe '#import_repository' do describe '#import_repository' do
it 'returns true when the command succeeds' do it 'returns true when the command succeeds' do
expect(Gitlab::Popen).to receive(:popen) expect(Gitlab::Popen).to receive(:popen)
.with([projects_path, 'import-project', 'current/storage', 'project/path.git', 'https://gitlab.com/gitlab-org/gitlab-ce.git', "800"]).and_return([nil, 0]) .with([projects_path, 'import-project', 'current/storage', 'project/path.git', 'https://gitlab.com/gitlab-org/gitlab-ce.git', "800"],
nil, popen_vars).and_return([nil, 0])
expect(gitlab_shell.import_repository('current/storage', 'project/path', 'https://gitlab.com/gitlab-org/gitlab-ce.git')).to be true expect(gitlab_shell.import_repository('current/storage', 'project/path', 'https://gitlab.com/gitlab-org/gitlab-ce.git')).to be true
end end
it 'raises an exception when the command fails' do it 'raises an exception when the command fails' do
expect(Gitlab::Popen).to receive(:popen) expect(Gitlab::Popen).to receive(:popen)
.with([projects_path, 'import-project', 'current/storage', 'project/path.git', 'https://gitlab.com/gitlab-org/gitlab-ce.git', "800"]).and_return(["error", 1]) .with([projects_path, 'import-project', 'current/storage', 'project/path.git', 'https://gitlab.com/gitlab-org/gitlab-ce.git', "800"],
nil, popen_vars).and_return(["error", 1])
expect { gitlab_shell.import_repository('current/storage', 'project/path', 'https://gitlab.com/gitlab-org/gitlab-ce.git') }.to raise_error(Gitlab::Shell::Error, "error") expect { gitlab_shell.import_repository('current/storage', 'project/path', 'https://gitlab.com/gitlab-org/gitlab-ce.git') }.to raise_error(Gitlab::Shell::Error, "error")
end end
......
require 'spec_helper'
require Rails.root.join('db', 'migrate', '20170622135451_rename_duplicated_variable_key.rb')
describe RenameDuplicatedVariableKey, :migration do
let(:variables) { table(:ci_variables) }
let(:projects) { table(:projects) }
before do
projects.create!(id: 1)
variables.create!(id: 1, key: 'key1', project_id: 1)
variables.create!(id: 2, key: 'key2', project_id: 1)
variables.create!(id: 3, key: 'keyX', project_id: 1)
variables.create!(id: 4, key: 'keyX', project_id: 1)
variables.create!(id: 5, key: 'keyY', project_id: 1)
variables.create!(id: 6, key: 'keyX', project_id: 1)
variables.create!(id: 7, key: 'key7', project_id: 1)
variables.create!(id: 8, key: 'keyY', project_id: 1)
end
it 'correctly remove duplicated records with smaller id' do
migrate!
expect(variables.pluck(:id, :key)).to contain_exactly(
[1, 'key1'],
[2, 'key2'],
[3, 'keyX_3'],
[4, 'keyX_4'],
[5, 'keyY_5'],
[6, 'keyX'],
[7, 'key7'],
[8, 'keyY']
)
end
end
...@@ -3,8 +3,16 @@ require 'spec_helper' ...@@ -3,8 +3,16 @@ require 'spec_helper'
describe Ci::Variable, models: true do describe Ci::Variable, models: true do
subject { build(:ci_variable) } subject { build(:ci_variable) }
it { is_expected.to include_module(HasVariable) } let(:secret_value) { 'secret' }
it { is_expected.to validate_uniqueness_of(:key).scoped_to(:project_id) }
describe 'validations' do
it { is_expected.to include_module(HasVariable) }
it { is_expected.to validate_uniqueness_of(:key).scoped_to(:project_id, :environment_scope) }
it { is_expected.to validate_length_of(:key).is_at_most(255) }
it { is_expected.to allow_value('foo').for(:key) }
it { is_expected.not_to allow_value('foo bar').for(:key) }
it { is_expected.not_to allow_value('foo/bar').for(:key) }
end
describe '.unprotected' do describe '.unprotected' do
subject { described_class.unprotected } subject { described_class.unprotected }
......
...@@ -142,6 +142,19 @@ describe Group, 'Routable' do ...@@ -142,6 +142,19 @@ describe Group, 'Routable' do
end end
end end
describe '#expires_full_path_cache' do
context 'with RequestStore active', :request_store do
it 'expires the full_path cache' do
expect(group.full_path).to eq('foo')
group.route.update(path: 'bar', name: 'bar')
group.expires_full_path_cache
expect(group.full_path).to eq('bar')
end
end
end
describe '#full_name' do describe '#full_name' do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:nested_group) { create(:group, parent: group) } let(:nested_group) { create(:group, parent: group) }
......
...@@ -1458,6 +1458,8 @@ describe Project, models: true do ...@@ -1458,6 +1458,8 @@ describe Project, models: true do
expect(project).to receive(:expire_caches_before_rename) expect(project).to receive(:expire_caches_before_rename)
expect(project).to receive(:expires_full_path_cache)
project.rename_repo project.rename_repo
end end
...@@ -1586,7 +1588,7 @@ describe Project, models: true do ...@@ -1586,7 +1588,7 @@ describe Project, models: true do
.with(project.repository_storage_path, project.path_with_namespace) .with(project.repository_storage_path, project.path_with_namespace)
.and_return(true) .and_return(true)
expect(project).to receive(:create_repository) expect(project).to receive(:create_repository).with(force: true)
project.ensure_repository project.ensure_repository
end end
...@@ -1599,6 +1601,19 @@ describe Project, models: true do ...@@ -1599,6 +1601,19 @@ describe Project, models: true do
project.ensure_repository project.ensure_repository
end end
it 'creates the repository if it is a fork' do
expect(project).to receive(:forked?).and_return(true)
allow(project).to receive(:repository_exists?)
.and_return(false)
expect(shell).to receive(:add_repository)
.with(project.repository_storage_path, project.path_with_namespace)
.and_return(true)
project.ensure_repository
end
end end
describe 'handling import URL' do describe 'handling import URL' do
......
...@@ -30,6 +30,12 @@ describe Projects::TransferService, services: true do ...@@ -30,6 +30,12 @@ describe Projects::TransferService, services: true do
transfer_project(project, user, group) transfer_project(project, user, group)
end end
it 'expires full_path cache' do
expect(project).to receive(:expires_full_path_cache)
transfer_project(project, user, group)
end
it 'executes system hooks' do it 'executes system hooks' do
expect_any_instance_of(Projects::TransferService).to receive(:execute_system_hooks) expect_any_instance_of(Projects::TransferService).to receive(:execute_system_hooks)
......
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