Commit e039bbbd authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'master' into ce-to-ee-2018-01-25

* master: (34 commits)
  Add a lint check to restrict use of Rugged, EE version
  Fix EE offenses to the RSpec/SingleLineHook cop
  Update qa.rb
  Use flash message instead of regular text block
  Update common.rb
  Update gitlab-styles and re-enable the RSpec/SingleLineHook cop again
  Move EE-specific migrations under ee/db/
  Make Gitlab::Git::Repository#run_git private
  Update secret_values to support dynamic elements within parent
  Improve PostgreSQL warning message in Rake task
  Improve warning message about the replication status
  Add a warning for Postgresql < 9.6 when show replication status
  Refactored expand_section to use name instead of selector
  Refactored view/element definition to dry up
  add view/element, some small fixes and code refactor
  Small fixes and codestyle changes
  Reduced rename replication spec steps to the bare minimum.
  Dont send enter key
  Refactor to address codestyle feedback
  Make AdvancedSettings a Page and move `rename_to to it.
  ...
parents 8d99d9ee 595a616c
......@@ -13,11 +13,24 @@ AllCops:
- 'db/*'
- 'db/fixtures/**/*'
- 'db/geo/*'
- 'ee/db/geo/*'
- 'tmp/**/*'
- 'bin/**/*'
- 'generator_templates/**/*'
- 'builds/**/*'
# This cop checks whether some constant value isn't a
# mutable literal (e.g. array or hash).
Style/MutableConstant:
Enabled: true
Exclude:
- 'db/migrate/**/*'
- 'db/post_migrate/**/*'
- 'db/geo/migrate/**/*'
- 'ee/db/migrate/**/*'
- 'ee/db/post_migrate/**/*'
- 'ee/db/geo/migrate/**/*'
# Gitlab ###################################################################
Gitlab/ModuleWithInstanceVariables:
......
......@@ -342,10 +342,6 @@ RSpec/SharedContext:
Exclude:
- 'spec/features/admin/admin_groups_spec.rb'
# Offense count: 90
RSpec/SingleLineHook:
Enabled: false
# Offense count: 5
RSpec/VoidExpect:
Exclude:
......
......@@ -329,7 +329,7 @@ GEM
posix-spawn (~> 0.3)
gitlab-license (1.0.0)
gitlab-markup (1.6.3)
gitlab-styles (2.3.0)
gitlab-styles (2.3.1)
rubocop (~> 0.51)
rubocop-gitlab-security (~> 0.1.0)
rubocop-rspec (~> 1.19)
......
......@@ -2,18 +2,19 @@ import { n__ } from '../locale';
import { convertPermissionToBoolean } from '../lib/utils/common_utils';
export default class SecretValues {
constructor(container) {
constructor({
container,
valueSelector = '.js-secret-value',
placeholderSelector = '.js-secret-value-placeholder',
}) {
this.container = container;
this.valueSelector = valueSelector;
this.placeholderSelector = placeholderSelector;
}
init() {
this.values = this.container.querySelectorAll('.js-secret-value');
this.placeholders = this.container.querySelectorAll('.js-secret-value-placeholder');
this.revealButton = this.container.querySelector('.js-secret-value-reveal-button');
this.revealText = n__('Reveal value', 'Reveal values', this.values.length);
this.hideText = n__('Hide value', 'Hide values', this.values.length);
const isRevealed = convertPermissionToBoolean(this.revealButton.dataset.secretRevealStatus);
this.updateDom(isRevealed);
......@@ -28,15 +29,17 @@ export default class SecretValues {
}
updateDom(isRevealed) {
this.values.forEach((value) => {
const values = this.container.querySelectorAll(this.valueSelector);
values.forEach((value) => {
value.classList.toggle('hide', !isRevealed);
});
this.placeholders.forEach((placeholder) => {
const placeholders = this.container.querySelectorAll(this.placeholderSelector);
placeholders.forEach((placeholder) => {
placeholder.classList.toggle('hide', isRevealed);
});
this.revealButton.textContent = isRevealed ? this.hideText : this.revealText;
this.revealButton.textContent = isRevealed ? n__('Hide value', 'Hide values', values.length) : n__('Reveal value', 'Reveal values', values.length);
this.revealButton.dataset.secretRevealStatus = isRevealed;
}
}
......@@ -132,14 +132,17 @@ $(() => {
if (sidebarInfoEndpoint && newIssue.subscribed === undefined) {
newIssue.setFetchingState('subscriptions', true);
newIssue.setFetchingState('weight', true);
newIssue.setFetchingState('epic', true);
BoardService.getIssueInfo(sidebarInfoEndpoint)
.then(res => res.data)
.then((data) => {
newIssue.setFetchingState('subscriptions', false);
newIssue.setFetchingState('weight', false);
newIssue.setFetchingState('epic', false);
newIssue.updateData({
subscribed: data.subscribed,
weight: data.weight,
epic: data.epic,
});
})
.catch(() => {
......
......@@ -3,7 +3,9 @@ import SecretValues from '~/behaviors/secret_values';
export default () => {
const secretVariableTable = document.querySelector('.js-secret-variable-table');
if (secretVariableTable) {
const secretVariableTableValues = new SecretValues(secretVariableTable);
const secretVariableTableValues = new SecretValues({
container: secretVariableTable,
});
secretVariableTableValues.init();
}
};
......@@ -6,13 +6,17 @@ export default function () {
initSettingsPanels();
const runnerToken = document.querySelector('.js-secret-runner-token');
if (runnerToken) {
const runnerTokenSecretValue = new SecretValues(runnerToken);
const runnerTokenSecretValue = new SecretValues({
container: runnerToken,
});
runnerTokenSecretValue.init();
}
const secretVariableTable = document.querySelector('.js-secret-variable-table');
if (secretVariableTable) {
const secretVariableTableValues = new SecretValues(secretVariableTable);
const secretVariableTableValues = new SecretValues({
container: secretVariableTable,
});
secretVariableTableValues.init();
}
}
......@@ -537,7 +537,7 @@ module Ci
return unless sha
project.repository.gitlab_ci_yml_for(sha, ci_yaml_file_path)
rescue GRPC::NotFound, Rugged::ReferenceError, GRPC::Internal
rescue GRPC::NotFound, GRPC::Internal
nil
end
......
......@@ -45,14 +45,7 @@ class Deployment < ActiveRecord::Base
def includes_commit?(commit)
return false unless commit
# Before 8.10, deployments didn't have keep-around refs. Any deployment
# created before then could have a `sha` referring to a commit that no
# longer exists in the repository, so just ignore those.
begin
project.repository.ancestor?(commit.id, sha)
rescue Rugged::OdbError
false
end
project.repository.ancestor?(commit.id, sha)
end
def update_merge_request_metrics!
......
......@@ -173,16 +173,10 @@ class Repository
return []
end
raw_repository.gitaly_migrate(:commits_by_message) do |is_enabled|
commits =
if is_enabled
find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
else
find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
end
CommitCollection.new(project, commits, ref)
commits = raw_repository.find_commits_by_message(query, ref, path, limit, offset).map do |c|
commit(c)
end
CommitCollection.new(project, commits, ref)
end
def find_branch(name, fresh_repo: true)
......@@ -747,23 +741,6 @@ class Repository
Commit.order_by(collection: commits, order_by: order_by, sort: sort)
end
def refs_contains_sha(ref_type, sha)
args = %W(#{ref_type} --contains #{sha})
names = run_git(args).first
if names.respond_to?(:split)
names = names.split("\n").map(&:strip)
names.each do |name|
name.slice! '* '
end
names
else
[]
end
end
def branch_names_contains(sha)
refs_contains_sha('branch', sha)
end
......@@ -970,25 +947,6 @@ class Repository
end
end
def search_files_by_content(query, ref)
return [] if empty? || query.blank?
offset = 2
args = %W(grep -i -I -n -z --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref})
run_git(args).first.scrub.split(/^--$/)
end
def search_files_by_name(query, ref)
safe_query = Regexp.escape(query.sub(/^\/*/, ""))
return [] if empty? || safe_query.blank?
args = %W(ls-tree --full-tree -r #{ref || root_ref} --name-status | #{safe_query})
run_git(args).first.lines.map(&:strip)
end
def fetch_as_mirror(url, forced: false, refmap: :all_refs, remote_name: nil)
unless remote_name
remote_name = "tmp-#{SecureRandom.hex}"
......@@ -1036,6 +994,18 @@ class Repository
raw_repository.ls_files(actual_ref)
end
def search_files_by_content(query, ref)
return [] if empty? || query.blank?
raw_repository.search_files_by_content(query, ref)
end
def search_files_by_name(query, ref)
return [] if empty?
raw_repository.search_files_by_name(query, ref)
end
def copy_gitattributes(ref)
actual_ref = ref || root_ref
begin
......@@ -1202,25 +1172,4 @@ class Repository
def rugged_can_be_merged?(their_commit, our_commit)
!rugged.merge_commits(our_commit, their_commit).conflicts?
end
def find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
ref ||= root_ref
args = %W(
log #{ref} --pretty=%H --skip #{offset}
--max-count #{limit} --grep=#{query} --regexp-ignore-case
)
args = args.concat(%W(-- #{path})) if path.present?
git_log_results = run_git(args).first.lines
git_log_results.map { |c| commit(c.chomp) }.compact
end
def find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
raw_repository
.gitaly_commit_client
.commits_by_message(query, revision: ref, path: path, limit: limit, offset: offset)
.map { |c| commit(c) }
end
end
......@@ -3,7 +3,7 @@
.settings-header
%h4
Deploy Keys
%button.btn.js-settings-toggle.qa-expand-deploy-keys
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p
Deploy keys allow read-only or read-write (if enabled) access to your repository. Deploy keys can be used for CI, staging or production servers. You can create a deploy key or add an existing one.
......
......@@ -19,6 +19,7 @@
= custom_icon("icon_close", size: 15)
.js-issuable-update
= render "shared/boards/components/sidebar/assignee"
= render "shared/boards/components/sidebar/epic"
= render "shared/boards/components/sidebar/milestone"
= render "shared/boards/components/sidebar/due_date"
= render "shared/boards/components/sidebar/labels"
......
......@@ -20,10 +20,7 @@ module RepositoryCheck
# Historically some projects never had their wiki repos initialized;
# this happens on project creation now. Let's initialize an empty repo
# if it is not already there.
begin
project.create_wiki
rescue Rugged::RepositoryError
end
project.create_wiki
git_fsck(project.wiki.repository)
else
......
---
title: 'Geo: Improve replication status. Using pg_stat_wal_receiver'
merge_request:
author:
type: other
---
title: Add Epic information for selected issue in Issue boards sidebar
merge_request: 4104
author:
type: added
......@@ -12,3 +12,12 @@ unless ENV['SKIP_POST_DEPLOYMENT_MIGRATIONS']
ActiveRecord::Migrator.migrations_paths << path
end
end
migrate_paths = Rails.application.config.paths['db/migrate'].to_a
migrate_paths.each do |migrate_path|
absolute_migrate_path = Pathname.new(migrate_path).realpath(Rails.root)
ee_migrate_path = Rails.root.join('ee/', absolute_migrate_path.relative_path_from(Rails.root))
Rails.application.config.paths['db/migrate'] << ee_migrate_path.to_s
ActiveRecord::Migrator.migrations_paths << ee_migrate_path.to_s
end
# We don't want to ever call Rugged::Repository#fetch_attributes, because it has
# a lot of I/O overhead:
# <https://gitlab.com/gitlab-org/gitlab_git/commit/340e111e040ae847b614d35b4d3173ec48329015>
#
# While we don't do this from within the GitLab source itself, the Linguist gem
# has a dependency on Rugged and uses the gitattributes file when calculating
# repository-wide language statistics:
# <https://github.com/github/linguist/blob/v4.7.0/lib/linguist/lazy_blob.rb#L33-L36>
#
# The options passed by Linguist are those assumed by Gitlab::Git::InfoAttributes
# anyway, and there is no great efficiency gain from just fetching the listed
# attributes with our implementation, so we ignore the additional arguments.
#
module Rugged
class Repository
module UseGitlabGitAttributes
def fetch_attributes(name, *)
attributes.attributes(name)
end
def attributes
@attributes ||= Gitlab::Git::InfoAttributes.new(path)
end
end
prepend UseGitlabGitAttributes
end
end
......@@ -167,7 +167,7 @@ otherwise it may fail with an encryption error.
Secondary Geo nodes track data about what has been downloaded in a second
PostgreSQL database that is distinct from the production GitLab database.
The database configuration is set in `config/database_geo.yml`.
`db/geo` contains the schema and migrations for this database.
`ee/db/geo` contains the schema and migrations for this database.
To write a migration for the database, use the `GeoMigrationGenerator`:
......
......@@ -18,6 +18,10 @@
%strong exact order
they appear.
- unless Gitlab::Database.pg_stat_wal_receiver_supported?
= content_for :flash_message do
.alert.alert-warning WARNING: Please upgrade PostgreSQL to version 9.6 or greater. The status of the replication cannot be determined reliably with the current version.
- if @nodes.any?
#js-geo-nodes{ data: { primary_version: "#{Gitlab::VERSION}", primary_revision: "#{Gitlab::REVISION}", node_details_path: "#{admin_geo_nodes_path}", node_actions_allowed: "#{Gitlab::Database.read_write?}", node_edit_allowed: "#{Gitlab::Geo.license_allows?}" } }
- else
......
- return unless @group&.feature_available?(:epics) || @project&.group&.feature_available?(:epics)
.block.epic
.title
Epic
%span.js-epic-label-loading{ "v-if" => "issue.isFetching && issue.isFetching.epic" }
= icon('spinner spin', class: 'loading-icon')
.value.js-epic-label{ "v-if" => "issue.isFetching && !issue.isFetching.epic" }
%a.bold{ "v-if" => "issue.epic", ":href" => "issue.epic.url" }
{{ issue.epic.title }}
.no-value{ "v-if" => "!issue.epic" }
None
......@@ -12,6 +12,6 @@ class GeoMigrationGenerator < ActiveRecord::Generators::MigrationGenerator
def create_migration_file
set_local_assigns!
validate_file_name!
migration_template @migration_template, "db/geo/migrate/#{file_name}.rb"
migration_template @migration_template, "ee/db/geo/migrate/#{file_name}.rb"
end
end
......@@ -5,7 +5,7 @@ module Gitlab
DATABASE_CONFIG = 'config/database.yml'.freeze
GEO_DATABASE_CONFIG = 'config/database_geo.yml'.freeze
GEO_DB_DIR = 'db/geo'.freeze
GEO_DB_DIR = 'ee/db/geo'.freeze
def method_missing(method_name, *args, &block)
with_geo_db do
......
......@@ -7,7 +7,7 @@ module Gitlab
return '' unless Gitlab::Geo.secondary?
return 'The Geo database configuration file is missing.' unless Gitlab::Geo.geo_database_configured?
return 'The Geo node has a database that is not configured for streaming replication with the primary node.' unless self.database_secondary?
return 'The Geo node does not appear to be replicating data from the primary node.' unless self.db_replication_lag_seconds.present?
return 'The Geo node does not appear to be replicating data from the primary node.' if Gitlab::Database.pg_stat_wal_receiver_supported? && !self.streaming_active?
database_version = self.get_database_version.to_i
migration_version = self.get_migration_version.to_i
......@@ -76,6 +76,18 @@ module Gitlab
lag.present? ? lag.to_i : lag
end
def self.streaming_active?
# Get a streaming status
# This only works for Postgresql 9.6 and greater
status =
ActiveRecord::Base.connection.execute('
SELECT * FROM pg_stat_wal_receiver;')
.first
.fetch('status')
status == 'streaming'
end
end
end
end
......@@ -41,14 +41,14 @@ namespace :geo do
puts "Current version: #{Gitlab::Geo::DatabaseTasks.version}"
end
desc 'Drops and recreates the database from db/geo/schema.rb for the current environment and loads the seeds.'
desc 'Drops and recreates the database from ee/db/geo/schema.rb for the current environment and loads the seeds.'
task reset: [:environment] do
ns['drop'].invoke
ns['create'].invoke
ns['setup'].invoke
end
desc 'Load the seed data from db/geo/seeds.rb'
desc 'Load the seed data from ee/db/geo/seeds.rb'
task seed: [:environment] do
ns['abort_if_pending_migrations'].invoke
......@@ -97,7 +97,7 @@ namespace :geo do
Gitlab::Geo::DatabaseTasks.load_schema_current(:ruby, ENV['SCHEMA'])
end
desc 'Create a db/geo/schema.rb file that is portable against any DB supported by AR'
desc 'Create a ee/db/geo/schema.rb file that is portable against any DB supported by AR'
task dump: [:environment] do
Gitlab::Geo::DatabaseTasks::Schema.dump
......@@ -219,6 +219,12 @@ namespace :geo do
puts GeoNode.current_node_url
puts '-----------------------------------------------------'.color(:yellow)
unless Gitlab::Database.pg_stat_wal_receiver_supported?
puts
puts 'WARNING: Please upgrade PostgreSQL to version 9.6 or greater. The status of the replication cannot be determined reliably with the current version.'.color(:red)
puts
end
print 'GitLab version: '.rjust(COLUMN_WIDTH)
puts Gitlab::VERSION
......
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/953
#
module Gitlab
module BareRepositoryImport
class Repository
......
......@@ -54,6 +54,10 @@ module Gitlab
postgresql? && version.to_f >= 9.4
end
def self.pg_stat_wal_receiver_supported?
postgresql? && version.to_f >= 9.6
end
def self.nulls_last_order(field, direction = 'ASC')
order = "#{field} #{direction}"
......
......@@ -563,6 +563,8 @@ module Gitlab
return false if ancestor_id.nil? || descendant_id.nil?
merge_base_commit(ancestor_id, descendant_id) == ancestor_id
rescue Rugged::OdbError
false
end
# Returns true is +from+ is direct ancestor to +to+, otherwise false
......@@ -1125,23 +1127,6 @@ module Gitlab
target_ref
end
# Refactoring aid; allows us to copy code from app/models/repository.rb
def run_git(args, chdir: path, env: {}, nice: false, &block)
cmd = [Gitlab.config.git.bin_path, *args]
cmd.unshift("nice") if nice
circuit_breaker.perform do
popen(cmd, chdir, env, &block)
end
end
def run_git!(args, chdir: path, env: {}, nice: false, &block)
output, status = run_git(args, chdir: chdir, env: env, nice: nice, &block)
raise GitError, output unless status.zero?
output
end
# Refactoring aid; allows us to copy code from app/models/repository.rb
def run_git_with_timeout(args, timeout, env: {})
circuit_breaker.perform do
......@@ -1365,6 +1350,52 @@ module Gitlab
raise CommandError.new(e)
end
def refs_contains_sha(ref_type, sha)
args = %W(#{ref_type} --contains #{sha})
names = run_git(args).first
if names.respond_to?(:split)
names = names.split("\n").map(&:strip)
names.each do |name|
name.slice! '* '
end
names
else
[]
end
end
def search_files_by_content(query, ref)
return [] if empty? || query.blank?
offset = 2
args = %W(grep -i -I -n -z --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref})
run_git(args).first.scrub.split(/^--$/)
end
def search_files_by_name(query, ref)
safe_query = Regexp.escape(query.sub(/^\/*/, ""))
return [] if empty? || safe_query.blank?
args = %W(ls-tree --full-tree -r #{ref || root_ref} --name-status | #{safe_query})
run_git(args).first.lines.map(&:strip)
end
def find_commits_by_message(query, ref, path, limit, offset)
gitaly_migrate(:commits_by_message) do |is_enabled|
if is_enabled
find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
else
find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
end
end
end
private
def shell_write_ref(ref_path, ref, old_ref)
......@@ -1386,6 +1417,22 @@ module Gitlab
Rails.logger.error "Unable to create #{ref_path} reference for repository #{path}: #{ex}"
end
def run_git(args, chdir: path, env: {}, nice: false, &block)
cmd = [Gitlab.config.git.bin_path, *args]
cmd.unshift("nice") if nice
circuit_breaker.perform do
popen(cmd, chdir, env, &block)
end
end
def run_git!(args, chdir: path, env: {}, nice: false, &block)
output, status = run_git(args, chdir: chdir, env: env, nice: nice, &block)
raise GitError, output unless status.zero?
output
end
def fresh_worktree?(path)
File.exist?(path) && !clean_stuck_worktree(path)
end
......@@ -2161,6 +2208,26 @@ module Gitlab
def gitlab_projects_error
raise CommandError, @gitlab_projects.output
end
def find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
ref ||= root_ref
args = %W(
log #{ref} --pretty=%H --skip #{offset}
--max-count #{limit} --grep=#{query} --regexp-ignore-case
)
args = args.concat(%W(-- #{path})) if path.present?
git_log_results = run_git(args).first.lines
git_log_results.map { |c| commit(c.chomp) }.compact
end
def find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
gitaly_commit_client
.commits_by_message(query, revision: ref, path: path, limit: limit, offset: offset)
.map { |c| commit(c) }
end
end
end
end
......@@ -38,19 +38,27 @@ module Gitlab
from_id = case from
when NilClass
EMPTY_TREE_ID
when Rugged::Commit
from.oid
else
from
if from.respond_to?(:oid)
# This is meant to match a Rugged::Commit. This should be impossible in
# the future.
from.oid
else
from
end
end
to_id = case to
when NilClass
EMPTY_TREE_ID
when Rugged::Commit
to.oid
else
to
if to.respond_to?(:oid)
# This is meant to match a Rugged::Commit. This should be impossible in
# the future.
to.oid
else
to
end
end
request_params = diff_between_commits_request_params(from_id, to_id, options)
......
......@@ -57,10 +57,7 @@ module Gitlab
end
def commit_exists?(sha)
project.repository.lookup(sha)
true
rescue Rugged::Error
false
project.repository.commit(sha).present?
end
def collection_method
......
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/954
#
namespace :gitlab do
namespace :cleanup do
HASHED_REPOSITORY_NAME = '@hashed'.freeze
......
......@@ -107,18 +107,20 @@ module QA
autoload :New, 'qa/page/project/new'
autoload :Show, 'qa/page/project/show'
module Pipeline
autoload :Index, 'qa/page/project/pipeline/index'
autoload :Show, 'qa/page/project/pipeline/show'
end
module Settings
autoload :Common, 'qa/page/project/settings/common'
autoload :Advanced, 'qa/page/project/settings/advanced'
autoload :Main, 'qa/page/project/settings/main'
autoload :Repository, 'qa/page/project/settings/repository'
autoload :CICD, 'qa/page/project/settings/ci_cd'
autoload :DeployKeys, 'qa/page/project/settings/deploy_keys'
autoload :Runners, 'qa/page/project/settings/runners'
end
module Pipeline
autoload :Index, 'qa/page/project/pipeline/index'
autoload :Show, 'qa/page/project/pipeline/show'
end
end
module Profile
......
......@@ -3,10 +3,21 @@ module QA
module Dashboard
class Projects < Page::Base
view 'app/views/dashboard/projects/index.html.haml'
view 'app/views/shared/projects/_search_form.html.haml' do
element :form_filter_by_name, /form_tag.+id: 'project-filter-form'/
end
def go_to_project(name)
filter_by_name(name)
find_link(text: name).click
end
def filter_by_name(name)
page.within('form#project-filter-form') do
fill_in :name, with: name
end
end
end
end
end
......
......@@ -4,6 +4,7 @@ module QA
class Side < Page::Base
view 'app/views/layouts/nav/sidebar/_project.html.haml' do
element :settings_item
element :settings_link, 'link_to edit_project_path'
element :repository_link, "title: 'Repository'"
element :pipelines_settings_link, "title: 'CI / CD'"
element :top_level_items, '.sidebar-top-level-items'
......@@ -31,6 +32,12 @@ module QA
end
end
def go_to_settings
within_sidebar do
click_on 'Settings'
end
end
private
def hover_settings
......
module QA
module Page
module Project
module Settings
class Advanced < Page::Base
view 'app/views/projects/edit.html.haml' do
element :project_path_field, 'f.text_field :path'
element :project_name_field, 'f.text_field :name'
element :rename_project_button, "f.submit 'Rename project'"
end
def rename_to(path)
fill_project_name(path)
fill_project_path(path)
rename_project!
end
def fill_project_path(path)
fill_in :project_path, with: path
end
def fill_project_name(name)
fill_in :project_name, with: name
end
def rename_project!
click_on 'Rename project'
end
end
end
end
end
end
......@@ -3,20 +3,23 @@ module QA
module Project
module Settings
module Common
def expand(element_name)
page.within('#content-body') do
click_element(element_name)
yield
def self.included(base)
base.class_eval do
view 'app/views/projects/edit.html.haml' do
element :advanced_settings_expand, "= expanded ? 'Collapse' : 'Expand'"
end
end
end
# Click the Expand button present in the specified section
#
# @param [String] name present in the container in the DOM
def expand_section(name)
page.within('#content-body') do
page.within('section', text: name) do
click_button 'Expand'
click_button('Expand')
yield
yield if block_given?
end
end
end
......
module QA
module Page
module Project
module Settings
class Main < Page::Base
include Common
view 'app/views/projects/edit.html.haml' do
element :advanced_settings_section, 'Advanced settings'
end
def expand_advanced_settings(&block)
expand_section('Advanced settings') do
Advanced.perform(&block)
end
end
end
end
end
end
end
......@@ -6,11 +6,11 @@ module QA
include Common
view 'app/views/projects/deploy_keys/_index.html.haml' do
element :expand_deploy_keys
element :deploy_keys_section, 'Deploy Keys'
end
def expand_deploy_keys(&block)
expand(:expand_deploy_keys) do
expand_section('Deploy Keys') do
DeployKeys.perform(&block)
end
end
......
......@@ -23,11 +23,11 @@ module QA
# In case of an address that is a symbol we will try to guess address
# based on `Runtime::Scenario#something_address`.
#
def visit(address, page, &block)
def visit(address, page = nil, &block)
Browser::Session.new(address, page).perform(&block)
end
def self.visit(address, page, &block)
def self.visit(address, page = nil, &block)
new.visit(address, page, &block)
end
......
module QA
feature 'GitLab Geo project rename replication', :geo do
scenario 'user renames project' do
# create the project and push code
Runtime::Browser.visit(:geo_primary, QA::Page::Main::Login) do
Page::Main::Login.act { sign_in_using_credentials }
project = Factory::Resource::Project.fabricate! do |project|
project.name = 'geo-before-rename'
project.description = 'Geo project to be renamed'
end
geo_project_name = project.name
expect(project.name).to include 'geo-before-rename'
Factory::Repository::Push.fabricate! do |push|
push.project = project
push.file_name = 'README.md'
push.file_content = '# This is Geo project!'
push.commit_message = 'Add README.md'
end
# rename the project
Page::Menu::Main.act { go_to_projects }
Page::Dashboard::Projects.perform do |dashboard|
dashboard.go_to_project(geo_project_name)
end
Page::Menu::Side.act { go_to_settings }
geo_project_renamed = "geo-after-rename-#{SecureRandom.hex(8)}"
Page::Project::Settings::Main.perform do |settings|
settings.expand_advanced_settings do |page|
page.rename_to(geo_project_renamed)
end
end
sleep 2 # wait for replication
# check renamed project exist on secondary node
Runtime::Browser.visit(:geo_secondary, QA::Page::Main::Login) do
Page::Main::OAuth.act do
authorize! if needs_authorization?
end
expect(page).to have_content 'You are on a secondary (read-only) Geo node'
Page::Menu::Main.perform do |menu|
menu.go_to_projects
expect(page).to have_content(geo_project_renamed)
end
Page::Dashboard::Projects.perform do |dashboard|
dashboard.go_to_project(geo_project_renamed)
end
Page::Project::Show.perform do
expect(page).to have_content 'README.md'
expect(page).to have_content 'This is Geo project!'
end
end
end
end
end
end
......@@ -4,7 +4,7 @@ module QA
Runtime::Browser.visit(:geo_primary, QA::Page::Main::Login) do
Page::Main::Login.act { sign_in_using_credentials }
Factory::Resource::Project.fabricate! do |project|
project = Factory::Resource::Project.fabricate! do |project|
project.name = 'geo-project'
project.description = 'Geo test project'
end
......@@ -12,21 +12,11 @@ module QA
geo_project_name = Page::Project::Show.act { project_name }
expect(geo_project_name).to include 'geo-project'
Git::Repository.perform do |repository|
repository.location = Page::Project::Show.act do
choose_repository_clone_http
repository_location
end
repository.use_default_credentials
repository.act do
clone
configure_identity('GitLab QA', 'root@gitlab.com')
add_file('README.md', '# This is Geo project!')
commit('Add README.md')
push_changes
end
Factory::Repository::Push.fabricate! do |push|
push.file_name = 'README.md'
push.file_content = '# This is Geo project!'
push.commit_message = 'Add README.md'
push.project = project
end
Runtime::Browser.visit(:geo_secondary, QA::Page::Main::Login) do
......
#!/usr/bin/env ruby
ALLOWED = [
# https://gitlab.com/gitlab-org/gitaly/issues/760
'lib/elasticsearch/git/repository.rb',
# Can be deleted (?) once rugged is no longer used in production. Doesn't make Rugged calls.
'config/initializers/8_metrics.rb',
# Can be deleted once wiki's are fully (mandatory) migrated
'config/initializers/gollum.rb',
# Needs to be migrated, https://gitlab.com/gitlab-org/gitaly/issues/953
'lib/gitlab/bare_repository_import/repository.rb',
# Needs to be migrated, https://gitlab.com/gitlab-org/gitaly/issues/954
'lib/tasks/gitlab/cleanup.rake',
# https://gitlab.com/gitlab-org/gitaly/issues/961
'app/models/repository.rb',
# The only place where Rugged code is still allowed in production
'lib/gitlab/git/'
].freeze
rugged_lines = IO.popen(%w[git grep -i -n rugged -- app config lib], &:read).lines
rugged_lines = rugged_lines.reject { |l| l.start_with?(*ALLOWED) }
rugged_lines = rugged_lines.reject do |line|
code, _comment = line.split('# ', 2)
code !~ /rugged/i
end
exit if rugged_lines.empty?
puts "Using Rugged is only allowed in test and #{ALLOWED}\n\n"
puts rugged_lines
exit(false)
......@@ -13,7 +13,8 @@ tasks = [
%w[bundle exec rake gettext:lint],
%w[bundle exec rake lint:static_verification],
%w[scripts/lint-changelog-yaml],
%w[scripts/lint-conflicts.sh]
%w[scripts/lint-conflicts.sh],
%w[scripts/lint-rugged]
]
failed_tasks = tasks.reduce({}) do |failures, task|
......
......@@ -5,7 +5,8 @@ describe 'Issue Boards', :js do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:project) { create(:project, :public) }
let(:group) { create(:group) }
let(:project) { create(:project, :public, group: group) }
let!(:milestone) { create(:milestone, project: project) }
let!(:development) { create(:label, project: project, name: 'Development') }
let!(:stretch) { create(:label, project: project, name: 'Stretch') }
......@@ -141,6 +142,36 @@ describe 'Issue Boards', :js do
end
end
context 'epic' do
before do
stub_licensed_features(epics: true)
visit project_board_path(project, board)
wait_for_requests
end
context 'when the issue is not associated with an epic' do
it 'displays `None` for value of epic' do
click_card(card1)
wait_for_requests
expect(find('.js-epic-label').text).to have_content('None')
end
end
context 'when the issue is associated with an epic' do
let(:epic) { create(:epic, group: group) }
let!(:epic_issue) { create(:epic_issue, issue: issue1, epic: epic) }
it 'displays name of epic and links to it' do
click_card(card1)
wait_for_requests
expect(find('.js-epic-label')).to have_link(epic.title, href: epic_path(epic))
end
end
end
context 'weight' do
it 'displays weight async' do
click_card(card1)
......
......@@ -15,7 +15,7 @@ describe Gitlab::Geo::HealthCheck, :postgresql do
allow(described_class).to receive(:database_secondary?).and_return(true)
allow(described_class).to receive(:get_database_version).and_return('20170101')
allow(described_class).to receive(:get_migration_version).and_return('20170201')
allow(described_class).to receive(:db_replication_lag_seconds).and_return(0)
allow(described_class).to receive(:streaming_active?).and_return(true)
message = subject.perform_checks
......@@ -54,7 +54,7 @@ describe Gitlab::Geo::HealthCheck, :postgresql do
it 'returns an error when Geo database version does not match the latest migration version' do
allow(described_class).to receive(:database_secondary?).and_return(true)
allow(subject).to receive(:get_database_version) { 1 }
allow(described_class).to receive(:db_replication_lag_seconds).and_return(0)
allow(described_class).to receive(:streaming_active?).and_return(true)
expect(subject.perform_checks).to match(/Current Geo database version \([0-9]+\) does not match latest migration \([0-9]+\)/)
end
......@@ -62,17 +62,26 @@ describe Gitlab::Geo::HealthCheck, :postgresql do
it 'returns an error when latest migration version does not match the Geo database version' do
allow(described_class).to receive(:database_secondary?).and_return(true)
allow(subject).to receive(:get_migration_version) { 1 }
allow(described_class).to receive(:db_replication_lag_seconds).and_return(0)
allow(described_class).to receive(:streaming_active?).and_return(true)
expect(subject.perform_checks).to match(/Current Geo database version \([0-9]+\) does not match latest migration \([0-9]+\)/)
end
it 'returns an error when replication lag is not present' do
it 'returns an error when streaming is not active and Postgresql supports pg_stat_wal_receiver' do
allow(Gitlab::Database).to receive(:pg_stat_wal_receiver_supported?).and_return(true)
allow(described_class).to receive(:database_secondary?).and_return(true)
allow(described_class).to receive(:db_replication_lag_seconds).and_return(nil)
allow(described_class).to receive(:streaming_active?).and_return(false)
expect(subject.perform_checks).to match(/The Geo node does not appear to be replicating data from the primary node/)
end
it 'returns an error when streaming is not active and Postgresql does not support pg_stat_wal_receiver' do
allow(Gitlab::Database).to receive(:pg_stat_wal_receiver_supported?).and_return(false)
allow(described_class).to receive(:database_secondary?).and_return(true)
allow(described_class).to receive(:streaming_active?).and_return(false)
expect(subject.perform_checks).to be_empty
end
end
describe 'MySQL checks' do
......
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20171103152048_geo_drain_redis_queues')
require Rails.root.join('ee', 'db', 'post_migrate', '20171103152048_geo_drain_redis_queues')
describe GeoDrainRedisQueues, :clean_gitlab_redis_shared_state do
subject(:migration) { described_class.new }
......
require "spec_helper"
require Rails.root.join("db", "post_migrate", "20170811082658_remove_system_hook_from_geo_nodes.rb")
require 'spec_helper'
require Rails.root.join('ee', 'db', 'post_migrate', '20170811082658_remove_system_hook_from_geo_nodes.rb')
describe RemoveSystemHookFromGeoNodes, :migration do
let(:geo_nodes) { table(:geo_nodes) }
......
require 'spec_helper'
require Rails.root.join('db', 'migrate', '20170626202753_update_authorized_keys_file.rb')
require Rails.root.join('ee', 'db', 'migrate', '20170626202753_update_authorized_keys_file.rb')
describe UpdateAuthorizedKeysFile, :migration do
let(:migration) { described_class.new }
......
......@@ -474,7 +474,9 @@ describe GeoNodeStatus, :geo do
end
describe '#storage_shards_match?' do
before { stub_primary_node }
before do
stub_primary_node
end
set(:status) { create(:geo_node_status) }
let(:data) { GeoNodeStatusSerializer.new.represent(status).as_json }
......
import SecretValues from '~/behaviors/secret_values';
function generateFixtureMarkup(secrets, isRevealed) {
function generateValueMarkup(
secret,
valueClass = 'js-secret-value',
placeholderClass = 'js-secret-value-placeholder',
) {
return `
<div class="${placeholderClass}">
***
</div>
<div class="hide ${valueClass}">
${secret}
</div>
`;
}
function generateFixtureMarkup(secrets, isRevealed, valueClass, placeholderClass) {
return `
<div class="js-secret-container">
${secrets.map(secret => `
<div class="js-secret-value-placeholder">
***
</div>
<div class="hide js-secret-value">
${secret}
</div>
`).join('')}
${secrets.map(secret => generateValueMarkup(secret, valueClass, placeholderClass)).join('')}
<button
class="js-secret-value-reveal-button"
data-secret-reveal-status="${isRevealed}"
......@@ -21,11 +29,25 @@ function generateFixtureMarkup(secrets, isRevealed) {
`;
}
function setupSecretFixture(secrets, isRevealed) {
function setupSecretFixture(
secrets,
isRevealed,
valueClass = 'js-secret-value',
placeholderClass = 'js-secret-value-placeholder',
) {
const wrapper = document.createElement('div');
wrapper.innerHTML = generateFixtureMarkup(secrets, isRevealed);
const secretValues = new SecretValues(wrapper.querySelector('.js-secret-container'));
wrapper.innerHTML = generateFixtureMarkup(
secrets,
isRevealed,
valueClass,
placeholderClass,
);
const secretValues = new SecretValues({
container: wrapper.querySelector('.js-secret-container'),
valueSelector: `.${valueClass}`,
placeholderSelector: `.${placeholderClass}`,
});
secretValues.init();
return wrapper;
......@@ -49,7 +71,7 @@ describe('setupSecretValues', () => {
expect(revealButton.textContent).toEqual('Hide value');
});
it('should value hidden initially', () => {
it('should have value hidden initially', () => {
const wrapper = setupSecretFixture(secrets, false);
const values = wrapper.querySelectorAll('.js-secret-value');
const placeholders = wrapper.querySelectorAll('.js-secret-value-placeholder');
......@@ -143,4 +165,64 @@ describe('setupSecretValues', () => {
});
});
});
describe('with dynamic secrets', () => {
const secrets = ['mysecret123', 'happygoat456', 'tanuki789'];
it('should toggle values and placeholders', () => {
const wrapper = setupSecretFixture(secrets, false);
// Insert the new dynamic row
wrapper.querySelector('.js-secret-container').insertAdjacentHTML('afterbegin', generateValueMarkup('foobarbazdynamic'));
const revealButton = wrapper.querySelector('.js-secret-value-reveal-button');
const values = wrapper.querySelectorAll('.js-secret-value');
const placeholders = wrapper.querySelectorAll('.js-secret-value-placeholder');
revealButton.click();
expect(values.length).toEqual(4);
values.forEach((value) => {
expect(value.classList.contains('hide')).toEqual(false);
});
expect(placeholders.length).toEqual(4);
placeholders.forEach((placeholder) => {
expect(placeholder.classList.contains('hide')).toEqual(true);
});
revealButton.click();
expect(values.length).toEqual(4);
values.forEach((value) => {
expect(value.classList.contains('hide')).toEqual(true);
});
expect(placeholders.length).toEqual(4);
placeholders.forEach((placeholder) => {
expect(placeholder.classList.contains('hide')).toEqual(false);
});
});
});
describe('selector options', () => {
const secrets = ['mysecret123'];
it('should respect `valueSelector` and `placeholderSelector` options', () => {
const valueClass = 'js-some-custom-placeholder-selector';
const placeholderClass = 'js-some-custom-value-selector';
const wrapper = setupSecretFixture(secrets, false, valueClass, placeholderClass);
const values = wrapper.querySelectorAll(`.${valueClass}`);
const placeholders = wrapper.querySelectorAll(`.${placeholderClass}`);
const revealButton = wrapper.querySelector('.js-secret-value-reveal-button');
expect(values.length).toEqual(1);
expect(placeholders.length).toEqual(1);
revealButton.click();
expect(values.length).toEqual(1);
expect(values[0].classList.contains('hide')).toEqual(false);
expect(placeholders.length).toEqual(1);
expect(placeholders[0].classList.contains('hide')).toEqual(true);
});
});
});
......@@ -9,7 +9,9 @@ describe API::Entities::GeoNodeStatus, :postgresql do
subject { entity.as_json }
before { stub_primary_node }
before do
stub_primary_node
end
describe '#healthy' do
context 'when node is healthy' do
......@@ -127,7 +129,9 @@ describe API::Entities::GeoNodeStatus, :postgresql do
end
context 'secondary Geo node' do
before { stub_secondary_node }
before do
stub_secondary_node
end
it { is_expected.to have_key(:storage_shards) }
it { is_expected.not_to have_key(:storage_shards_match) }
......
......@@ -244,7 +244,7 @@ describe Gitlab::GithubImport::Importer::PullRequestsImporter do
it 'returns true when a commit exists' do
expect(project.repository)
.to receive(:lookup)
.to receive(:commit)
.with('123')
.and_return(double(:commit))
......@@ -253,9 +253,9 @@ describe Gitlab::GithubImport::Importer::PullRequestsImporter do
it 'returns false when a commit does not exist' do
expect(project.repository)
.to receive(:lookup)
.to receive(:commit)
.with('123')
.and_raise(Rugged::OdbError)
.and_return(nil)
expect(importer.commit_exists?('123')).to eq(false)
end
......
......@@ -5,7 +5,11 @@ require 'spec_helper'
describe ActiveRecord::Schema do
let(:latest_migration_timestamp) do
migrations = Dir[Rails.root.join('db', 'migrate', '*'), Rails.root.join('db', 'post_migrate', '*')]
migrations_paths =
%w[db ee/db].product(%w[migrate post_migrate]).each_with_object([]) do |migration_dir, memo|
memo << Rails.root.join(*migration_dir, '*')
end
migrations = Dir[*migrations_paths]
migrations.map { |migration| File.basename(migration).split('_').first.to_i }.max
end
......
......@@ -2478,7 +2478,7 @@ describe Repository do
let(:commit) { repository.commit }
let(:ancestor) { commit.parents.first }
context 'with Gitaly enabled' do
shared_examples '#ancestor?' do
it 'it is an ancestor' do
expect(repository.ancestor?(ancestor.id, commit.id)).to eq(true)
end
......@@ -2492,27 +2492,19 @@ describe Repository do
expect(repository.ancestor?(ancestor.id, nil)).to eq(false)
expect(repository.ancestor?(nil, nil)).to eq(false)
end
end
context 'with Gitaly disabled' do
before do
allow(Gitlab::GitalyClient).to receive(:enabled?).and_return(false)
allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:is_ancestor).and_return(false)
end
it 'it is an ancestor' do
expect(repository.ancestor?(ancestor.id, commit.id)).to eq(true)
it 'returns false for invalid commit IDs' do
expect(repository.ancestor?(commit.id, Gitlab::Git::BLANK_SHA)).to eq(false)
expect(repository.ancestor?( Gitlab::Git::BLANK_SHA, commit.id)).to eq(false)
end
end
it 'it is not an ancestor' do
expect(repository.ancestor?(commit.id, ancestor.id)).to eq(false)
end
context 'with Gitaly enabled' do
it_behaves_like('#ancestor?')
end
it 'returns false on nil-values' do
expect(repository.ancestor?(nil, commit.id)).to eq(false)
expect(repository.ancestor?(ancestor.id, nil)).to eq(false)
expect(repository.ancestor?(nil, nil)).to eq(false)
end
context 'with Gitaly disabled', :skip_gitaly_mock do
it_behaves_like('#ancestor?')
end
end
......
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