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

Merge branch 'ce_upstream'

parents 691b0e8d 430f7da3
import autosize from 'vendor/autosize';
$(() => {
const $fields = $('.js-autosize');
document.addEventListener('DOMContentLoaded', () => {
const autosizeEls = document.querySelectorAll('.js-autosize');
$fields.on('autosize:resized', function resized() {
const $field = $(this);
$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');
autosize(autosizeEls);
autosize.update(autosizeEls);
});
......@@ -33,3 +33,20 @@
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
def full_path
return uncached_full_path unless RequestStore.active?
key = "routable/full_path/#{self.class.name}/#{self.id}"
RequestStore[key] ||= uncached_full_path
RequestStore[full_path_key] ||= uncached_full_path
end
def expires_full_path_cache
RequestStore.delete(full_path_key) if RequestStore.active?
@full_path = nil
end
def build_full_path
......@@ -135,6 +139,10 @@ module Routable
path_changed? || parent_changed?
end
def full_path_key
@full_path_key ||= "routable/full_path/#{self.class.name}/#{self.id}"
end
def build_full_name
if parent && name
parent.human_name + ' / ' + name
......
......@@ -977,6 +977,7 @@ class Project < ActiveRecord::Base
begin
gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
send_move_instructions(old_path_with_namespace)
expires_full_path_cache
@old_path_with_namespace = old_path_with_namespace
......@@ -1087,21 +1088,21 @@ class Project < ActiveRecord::Base
merge_requests.where(source_project_id: self.id)
end
def create_repository
def create_repository(force: false)
# Forked import is handled asynchronously
unless forked?
if gitlab_shell.add_repository(repository_storage_path, path_with_namespace)
repository.after_create
true
else
errors.add(:base, 'Failed to create repository via gitlab-shell')
false
end
return if forked? && !force
if gitlab_shell.add_repository(repository_storage_path, path_with_namespace)
repository.after_create
true
else
errors.add(:base, 'Failed to create repository via gitlab-shell')
false
end
end
def ensure_repository
create_repository unless repository_exists?
create_repository(force: true) unless repository_exists?
end
def repository_exists?
......
......@@ -78,6 +78,7 @@ module Projects
Gitlab::PagesTransfer.new.move_project(project.path, @old_namespace.full_path, @new_namespace.full_path)
project.old_path_with_namespace = @old_path
project.expires_full_path_cache
execute_system_hooks
end
......
......@@ -32,13 +32,16 @@
#{time_ago_in_words(runner.contacted_at)} ago
- else
Never
%td
.pull-right
= link_to 'Edit', admin_runner_path(runner), class: 'btn btn-sm'
%td.admin-runner-btn-group-cell
.pull-right.btn-group
= 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;
- 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
= link_to 'Resume', [:resume, :admin, runner], method: :get, class: 'btn btn-success btn-sm'
= link_to 'Remove', [:admin, runner], data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger 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
= 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 @@
%div
- if Gitlab::Recaptcha.enabled?
= recaptcha_tags
%div
.submit-container
= f.submit "Register", class: "btn-register btn"
.clearfix.submit-container
%p
......
......@@ -7,10 +7,9 @@
.form-group
.col-sm-10.col-sm-offset-2
.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
= 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.
= 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:
......@@ -93,7 +93,7 @@ For example, suppose we have a UX team with an Issue Board that contains:
- **Doing**
- **Frontend**
When done with something, they move the card to **Frontend**. The Frontend team's board looks like:
When done with something, they move the card to **Frontend**. The Frontend team's board looks like:
- **Frontend**
- **Doing**
......@@ -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
[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
multiple boards.
to improve their workflow with multiple boards.
## Issue Board terminology
......
......@@ -2,6 +2,8 @@ require 'securerandom'
module Gitlab
class Shell
GITLAB_SHELL_ENV_VARS = %w(GIT_TERMINAL_PROMPT).freeze
Error = Class.new(StandardError)
KeyAdder = Struct.new(:io) do
......@@ -67,8 +69,8 @@ module Gitlab
# add_repository("/path/to/storage", "gitlab/gitlab-ci")
#
def add_repository(storage, name)
Gitlab::Utils.system_silent([gitlab_shell_projects_path,
'add-project', storage, "#{name}.git"])
gitlab_shell_fast_execute([gitlab_shell_projects_path,
'add-project', storage, "#{name}.git"])
end
# Import repository
......@@ -82,10 +84,9 @@ module Gitlab
def import_repository(storage, name, url)
# Timeout should be less than 900 ideally, to prevent the memory killer
# to silently kill the process without knowing we are timing out here.
output, status = Popen.popen([gitlab_shell_projects_path, 'import-project',
storage, "#{name}.git", url, "#{Gitlab.config.gitlab_shell.git_timeout}"])
raise Error, output unless status.zero?
true
cmd = [gitlab_shell_projects_path, 'import-project',
storage, "#{name}.git", url, "#{Gitlab.config.gitlab_shell.git_timeout}"]
gitlab_shell_fast_execute_raise_error(cmd)
end
def list_remote_tags(storage, name, remote)
......@@ -131,9 +132,7 @@ module Gitlab
args << '--force' if forced
args << '--no-tags' if no_tags
output, status = Popen.popen(args)
raise Error, output unless status.zero?
true
gitlab_shell_fast_execute_raise_error(args)
end
# Move repository
......@@ -145,8 +144,8 @@ module Gitlab
# mv_repository("/path/to/storage", "gitlab/gitlab-ci", "randx/gitlab-ci-new")
#
def mv_repository(storage, path, new_path)
Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'mv-project',
storage, "#{path}.git", "#{new_path}.git"])
gitlab_shell_fast_execute([gitlab_shell_projects_path, 'mv-project',
storage, "#{path}.git", "#{new_path}.git"])
end
# Move repository storage
......@@ -173,9 +172,9 @@ module Gitlab
# 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)
Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'fork-project',
forked_from_storage, "#{path}.git", forked_to_storage,
fork_namespace])
gitlab_shell_fast_execute([gitlab_shell_projects_path, 'fork-project',
forked_from_storage, "#{path}.git", forked_to_storage,
fork_namespace])
end
# Remove repository from file system
......@@ -187,8 +186,8 @@ module Gitlab
# remove_repository("/path/to/storage", "gitlab/gitlab-ci")
#
def remove_repository(storage, name)
Gitlab::Utils.system_silent([gitlab_shell_projects_path,
'rm-project', storage, "#{name}.git"])
gitlab_shell_fast_execute([gitlab_shell_projects_path,
'rm-project', storage, "#{name}.git"])
end
# Add new key to gitlab-shell
......@@ -199,8 +198,8 @@ module Gitlab
def add_key(key_id, key_content)
return unless self.authorized_keys_enabled?
Gitlab::Utils.system_silent([gitlab_shell_keys_path,
'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)])
end
# Batch-add keys to authorized_keys
......@@ -225,7 +224,7 @@ module Gitlab
args = [gitlab_shell_keys_path, 'rm-key', key_id]
args << key_content if key_content
Gitlab::Utils.system_silent(args)
gitlab_shell_fast_execute(args)
end
# Remove all ssh keys from gitlab shell
......@@ -236,7 +235,7 @@ module Gitlab
def remove_all_keys
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
# Remove ssh keys from gitlab shell that are not in the DB
......@@ -424,5 +423,31 @@ module Gitlab
current_application_settings.authorized_keys_enabled
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
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) }
background do
project.team << [user, :master]
gitlab_sign_in(user)
sign_in(user)
visit project_path(project)
find('.shortcuts-wiki').trigger('click')
end
context 'in the user namespace' do
let(:project) { create(:project, namespace: user.namespace) }
context 'when wiki is empty' do
before do
find('.shortcuts-wiki').trigger('click')
end
scenario 'commit message field has value "Create home"' do
expect(page).to have_field('wiki[message]', with: 'Create home')
end
......@@ -67,10 +70,11 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do
context 'when wiki is not empty' do
before do
WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
find('.shortcuts-wiki').trigger('click')
end
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'
page.within '#modal-new-wiki' 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!')
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'
page.within '#modal-new-wiki' 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!')
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'
page.within '#modal-new-wiki' do
......@@ -134,7 +138,7 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do
end
end
scenario 'content has autocomplete', :js do
scenario 'content has autocomplete' do
click_link 'New page'
page.within '#modal-new-wiki' 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)) }
context 'when wiki is empty' do
before do
find('.shortcuts-wiki').trigger('click')
end
scenario 'commit message field has value "Create home"' do
expect(page).to have_field('wiki[message]', with: 'Create home')
end
......@@ -175,9 +183,10 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do
context 'when wiki is not empty' do
before do
WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
find('.shortcuts-wiki').trigger('click')
end
scenario 'via the "new wiki page" page', js: true do
scenario 'via the "new wiki page" page' do
click_link 'New page'
page.within '#modal-new-wiki' do
......
......@@ -32,6 +32,17 @@ describe 'Gitlab::Popen', lib: true, no_db: true do
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
before do
@output, @status = @klass.new.popen(%w(ls))
......@@ -45,7 +56,7 @@ describe 'Gitlab::Popen', lib: true, no_db: true do
before do
@output, @status = @klass.new.popen(%w[cat]) { |stdin| stdin.write 'hello' }
end
it { expect(@status).to be_zero }
it { expect(@output).to eq('hello') }
end
......
This diff is collapsed.
......@@ -3,14 +3,13 @@ require 'spec_helper'
describe Ci::Variable, models: true do
subject { build(:ci_variable) }
it { is_expected.to include_module(HasVariable) }
describe 'validations' do
# EE
before do
stub_licensed_features(variable_environment_scope: true)
end
it { is_expected.to include_module(HasVariable) }
it { is_expected.to validate_uniqueness_of(:key).scoped_to(:project_id, :environment_scope) }
end
......
......@@ -142,6 +142,19 @@ describe Group, 'Routable' do
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
let(:group) { create(:group) }
let(:nested_group) { create(:group, parent: group) }
......
......@@ -1458,6 +1458,8 @@ describe Project, models: true do
expect(project).to receive(:expire_caches_before_rename)
expect(project).to receive(:expires_full_path_cache)
project.rename_repo
end
......@@ -1586,7 +1588,7 @@ describe Project, models: true do
.with(project.repository_storage_path, project.path_with_namespace)
.and_return(true)
expect(project).to receive(:create_repository)
expect(project).to receive(:create_repository).with(force: true)
project.ensure_repository
end
......@@ -1599,6 +1601,19 @@ describe Project, models: true do
project.ensure_repository
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
describe 'handling import URL' do
......
......@@ -30,6 +30,12 @@ describe Projects::TransferService, services: true do
transfer_project(project, user, group)
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
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