Commit 8b0ec861 authored by Valery Sizov's avatar Valery Sizov

Merge branch 'ce_upstream'

parents 691b0e8d 430f7da3
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;
}
}
...@@ -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
......
...@@ -977,6 +977,7 @@ class Project < ActiveRecord::Base ...@@ -977,6 +977,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
...@@ -1087,9 +1088,10 @@ class Project < ActiveRecord::Base ...@@ -1087,9 +1088,10 @@ 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) if gitlab_shell.add_repository(repository_storage_path, path_with_namespace)
repository.after_create repository.after_create
true true
...@@ -1098,10 +1100,9 @@ class Project < ActiveRecord::Base ...@@ -1098,10 +1100,9 @@ class Project < ActiveRecord::Base
false false
end 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:
...@@ -108,8 +108,7 @@ Cards finished by the UX team will automatically appear in the **Frontend** colu ...@@ -108,8 +108,7 @@ 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)
to improve their workflow with to improve their workflow with multiple boards.
multiple boards.
## 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,7 +69,7 @@ module Gitlab ...@@ -67,7 +69,7 @@ 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
...@@ -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,7 +144,7 @@ module Gitlab ...@@ -145,7 +144,7 @@ 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
...@@ -173,7 +172,7 @@ module Gitlab ...@@ -173,7 +172,7 @@ 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
...@@ -187,7 +186,7 @@ module Gitlab ...@@ -187,7 +186,7 @@ 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
...@@ -199,7 +198,7 @@ module Gitlab ...@@ -199,7 +198,7 @@ module Gitlab
def add_key(key_id, key_content) def add_key(key_id, key_content)
return unless self.authorized_keys_enabled? return unless self.authorized_keys_enabled?
Gitlab::Utils.system_silent([gitlab_shell_keys_path, gitlab_shell_fast_execute([gitlab_shell_keys_path,
'add-key', key_id, self.class.strip_key(key_content)]) 'add-key', key_id, self.class.strip_key(key_content)])
end end
...@@ -225,7 +224,7 @@ module Gitlab ...@@ -225,7 +224,7 @@ module Gitlab
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_shell_fast_execute(args)
end end
# Remove all ssh keys from gitlab shell # Remove all ssh keys from gitlab shell
...@@ -236,7 +235,7 @@ module Gitlab ...@@ -236,7 +235,7 @@ module Gitlab
def remove_all_keys def remove_all_keys
return unless self.authorized_keys_enabled? return unless self.authorized_keys_enabled?
Gitlab::Utils.system_silent([gitlab_shell_keys_path, 'clear']) gitlab_shell_fast_execute([gitlab_shell_keys_path, 'clear'])
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
...@@ -424,5 +423,31 @@ module Gitlab ...@@ -424,5 +423,31 @@ module Gitlab
current_application_settings.authorized_keys_enabled current_application_settings.authorized_keys_enabled
end end
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) }
end
end end
end end
require 'spec_helper' require 'spec_helper'
feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do feature 'Projects > Wiki > User creates wiki page', :js do
let(:user) { create(:user) } let(:user) { create(:user) }
background do background do
project.team << [user, :master] project.team << [user, :master]
gitlab_sign_in(user) sign_in(user)
visit project_path(project) visit project_path(project)
find('.shortcuts-wiki').trigger('click')
end end
context 'in the user namespace' do context 'in the user namespace' do
let(:project) { create(:project, namespace: user.namespace) } let(:project) { create(:project, namespace: user.namespace) }
context 'when wiki is empty' do context 'when wiki is empty' do
before do
find('.shortcuts-wiki').trigger('click')
end
scenario 'commit message field has value "Create home"' do scenario 'commit message field has value "Create home"' do
expect(page).to have_field('wiki[message]', with: 'Create home') expect(page).to have_field('wiki[message]', with: 'Create home')
end end
...@@ -67,10 +70,11 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do ...@@ -67,10 +70,11 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do
context 'when wiki is not empty' do context 'when wiki is not empty' do
before do before do
WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
find('.shortcuts-wiki').trigger('click')
end end
context 'via the "new wiki page" page' do context 'via the "new wiki page" page' do
scenario 'when the wiki page has a single word name', js: true do scenario 'when the wiki page has a single word name' do
click_link 'New page' click_link 'New page'
page.within '#modal-new-wiki' do page.within '#modal-new-wiki' do
...@@ -91,7 +95,7 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do ...@@ -91,7 +95,7 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do
expect(page).to have_content('My awesome wiki!') expect(page).to have_content('My awesome wiki!')
end end
scenario 'when the wiki page has spaces in the name', js: true do scenario 'when the wiki page has spaces in the name' do
click_link 'New page' click_link 'New page'
page.within '#modal-new-wiki' do page.within '#modal-new-wiki' do
...@@ -112,7 +116,7 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do ...@@ -112,7 +116,7 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do
expect(page).to have_content('My awesome wiki!') expect(page).to have_content('My awesome wiki!')
end end
scenario 'when the wiki page has hyphens in the name', js: true do scenario 'when the wiki page has hyphens in the name' do
click_link 'New page' click_link 'New page'
page.within '#modal-new-wiki' do page.within '#modal-new-wiki' do
...@@ -134,7 +138,7 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do ...@@ -134,7 +138,7 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do
end end
end end
scenario 'content has autocomplete', :js do scenario 'content has autocomplete' do
click_link 'New page' click_link 'New page'
page.within '#modal-new-wiki' do page.within '#modal-new-wiki' do
...@@ -156,6 +160,10 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do ...@@ -156,6 +160,10 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do
let(:project) { create(:project, namespace: create(:group, :public)) } let(:project) { create(:project, namespace: create(:group, :public)) }
context 'when wiki is empty' do context 'when wiki is empty' do
before do
find('.shortcuts-wiki').trigger('click')
end
scenario 'commit message field has value "Create home"' do scenario 'commit message field has value "Create home"' do
expect(page).to have_field('wiki[message]', with: 'Create home') expect(page).to have_field('wiki[message]', with: 'Create home')
end end
...@@ -175,9 +183,10 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do ...@@ -175,9 +183,10 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do
context 'when wiki is not empty' do context 'when wiki is not empty' do
before do before do
WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
find('.shortcuts-wiki').trigger('click')
end end
scenario 'via the "new wiki page" page', js: true do scenario 'via the "new wiki page" page' do
click_link 'New page' click_link 'New page'
page.within '#modal-new-wiki' do page.within '#modal-new-wiki' do
......
...@@ -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))
......
This diff is collapsed.
...@@ -3,14 +3,13 @@ require 'spec_helper' ...@@ -3,14 +3,13 @@ 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) }
describe 'validations' do describe 'validations' do
# EE # EE
before do before do
stub_licensed_features(variable_environment_scope: true) stub_licensed_features(variable_environment_scope: true)
end end
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_uniqueness_of(:key).scoped_to(:project_id, :environment_scope) }
end end
......
...@@ -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