summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarin Jankovski <marin@gitlab.com>2019-05-16 18:33:03 +0000
committerMarin Jankovski <marin@gitlab.com>2019-05-16 18:33:03 +0000
commit69b4b2708cec18e3bcb76f611b3b809f487adffd (patch)
treed3e4922411d4c8ac06a78cc7bbaefcbe707b0166
parent4b35811443690caddd52ab289e3786e9636c9b6c (diff)
parentabac5047f5f6a8cde5bd539b22a5995e6ab2f01a (diff)
downloadgitlab-ce-69b4b2708cec18e3bcb76f611b3b809f487adffd.tar.gz
Merge branch '11-11-stable-prepare-rc3' into '11-11-stable'
Prepare 11.11.0-rc3 release See merge request gitlab-org/gitlab-ce!28313
-rw-r--r--app/assets/javascripts/main.js2
-rw-r--r--app/assets/stylesheets/framework/ci_variable_list.scss1
-rw-r--r--app/models/project.rb8
-rw-r--r--app/policies/project_policy.rb4
-rw-r--r--app/services/projects/after_import_service.rb2
-rw-r--r--app/services/projects/lfs_pointers/lfs_download_link_list_service.rb12
-rw-r--r--app/views/ci/variables/_index.html.haml1
-rw-r--r--app/views/ci/variables/_variable_header.html.haml16
-rw-r--r--app/views/ci/variables/_variable_row.html.haml18
-rw-r--r--app/views/projects/branches/_branch.html.haml2
-rw-r--r--app/views/shared/members/_member.html.haml2
-rw-r--r--changelogs/unreleased/61550-next-badge.yml5
-rw-r--r--changelogs/unreleased/ce-11542-remove-non-semantic-use-of-row-in-member-listing-controls.yml5
-rw-r--r--changelogs/unreleased/fix-project-visibility-level-validation.yml5
-rw-r--r--changelogs/unreleased/include-ee-fixtures.yml5
-rw-r--r--changelogs/unreleased/sh-fix-lfs-download-errors.yml5
-rw-r--r--config/initializers/seed_fu.rb4
-rw-r--r--doc/administration/geo/disaster_recovery/background_verification.md36
-rw-r--r--doc/administration/geo/disaster_recovery/img/checksum-differences-admin-project-page.pngbin0 -> 202130 bytes
-rw-r--r--doc/administration/geo/disaster_recovery/img/checksum-differences-admin-projects.pngbin0 -> 85997 bytes
-rw-r--r--doc/development/README.md1
-rw-r--r--doc/development/session.md65
-rw-r--r--doc/install/installation.md30
-rw-r--r--doc/integration/salesforce.md8
-rw-r--r--doc/user/admin_area/settings/continuous_integration.md3
-rw-r--r--locale/gitlab.pot15
-rw-r--r--qa/qa/page/base.rb8
-rw-r--r--qa/qa/page/project/branches/show.rb16
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb6
-rw-r--r--qa/qa/support/page/logging.rb22
-rw-r--r--qa/qa/support/waiter.rb4
-rw-r--r--qa/spec/page/logging_spec.rb9
-rw-r--r--spec/javascripts/ci_variable_list/ajax_variable_list_spec.js2
-rw-r--r--spec/models/project_spec.rb7
-rw-r--r--spec/services/projects/after_import_service_spec.rb2
-rw-r--r--spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb20
-rw-r--r--spec/support/features/variable_list_shared_examples.rb36
37 files changed, 312 insertions, 75 deletions
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index b503c746801..213d5c6521a 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -147,7 +147,7 @@ function deferredInitialisation() {
const canaryBadge = document.querySelector('.js-canary-badge');
const canaryLink = document.querySelector('.js-canary-link');
if (canaryBadge) {
- canaryBadge.classList.remove('hidden');
+ canaryBadge.classList.add('hidden');
}
if (canaryLink) {
canaryLink.classList.add('hidden');
diff --git a/app/assets/stylesheets/framework/ci_variable_list.scss b/app/assets/stylesheets/framework/ci_variable_list.scss
index d9b0e4558ad..28d7492b99c 100644
--- a/app/assets/stylesheets/framework/ci_variable_list.scss
+++ b/app/assets/stylesheets/framework/ci_variable_list.scss
@@ -47,6 +47,7 @@
display: flex;
align-items: flex-start;
width: 100%;
+ padding-bottom: $gl-padding;
@include media-breakpoint-down(xs) {
display: block;
diff --git a/app/models/project.rb b/app/models/project.rb
index 61d245478ca..68b5c299df4 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -337,8 +337,8 @@ class Project < ApplicationRecord
validates :star_count, numericality: { greater_than_or_equal_to: 0 }
validate :check_personal_projects_limit, on: :create
validate :check_repository_path_availability, on: :update, if: ->(project) { project.renamed? }
- validate :visibility_level_allowed_by_group, if: -> { changes.has_key?(:visibility_level) }
- validate :visibility_level_allowed_as_fork, if: -> { changes.has_key?(:visibility_level) }
+ validate :visibility_level_allowed_by_group, if: :should_validate_visibility_level?
+ validate :visibility_level_allowed_as_fork, if: :should_validate_visibility_level?
validate :check_wiki_path_conflict
validate :validate_pages_https_only, if: -> { changes.has_key?(:pages_https_only) }
validates :repository_storage,
@@ -892,6 +892,10 @@ class Project < ApplicationRecord
self.errors.add(:limit_reached, error % { limit: limit })
end
+ def should_validate_visibility_level?
+ new_record? || changes.has_key?(:visibility_level)
+ end
+
def visibility_level_allowed_by_group
return if visibility_level_allowed_by_group?
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 76544249688..3218c04b219 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -488,6 +488,10 @@ class ProjectPolicy < BasePolicy
def team_access_level
return -1 if @user.nil?
+ lookup_access_level!
+ end
+
+ def lookup_access_level!
# NOTE: max_member_access has its own cache
project.team.max_member_access(@user.id)
end
diff --git a/app/services/projects/after_import_service.rb b/app/services/projects/after_import_service.rb
index afb9048e87b..bbdde4408d2 100644
--- a/app/services/projects/after_import_service.rb
+++ b/app/services/projects/after_import_service.rb
@@ -9,7 +9,7 @@ module Projects
end
def execute
- Projects::HousekeepingService.new(@project, :gc).execute do
+ Projects::HousekeepingService.new(@project).execute do
repository.delete_all_refs_except(RESERVED_REF_PREFIXES)
end
rescue Projects::HousekeepingService::LeaseTaken => e
diff --git a/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb b/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb
index 05974948505..9b72480d18b 100644
--- a/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb
@@ -37,7 +37,17 @@ module Projects
raise DownloadLinksError, response.message unless response.success?
- parse_response_links(response['objects'])
+ # Since the LFS Batch API may return a Content-Ttpe of
+ # application/vnd.git-lfs+json
+ # (https://github.com/git-lfs/git-lfs/blob/master/docs/api/batch.md#requests),
+ # HTTParty does not know this is actually JSON.
+ data = JSON.parse(response.body)
+
+ raise DownloadLinksError, "LFS Batch API did return any objects" unless data.is_a?(Hash) && data.key?('objects')
+
+ parse_response_links(data['objects'])
+ rescue JSON::ParserError
+ raise DownloadLinksError, "LFS Batch API response is not JSON"
end
def parse_response_links(objects_response)
diff --git a/app/views/ci/variables/_index.html.haml b/app/views/ci/variables/_index.html.haml
index dc9ccb6cc39..464b9faf282 100644
--- a/app/views/ci/variables/_index.html.haml
+++ b/app/views/ci/variables/_index.html.haml
@@ -10,6 +10,7 @@
.hide.alert.alert-danger.js-ci-variable-error-box
%ul.ci-variable-list
+ = render 'ci/variables/variable_header'
- @variables.each.each do |variable|
= render 'ci/variables/variable_row', form_field: 'variables', variable: variable
= render 'ci/variables/variable_row', form_field: 'variables'
diff --git a/app/views/ci/variables/_variable_header.html.haml b/app/views/ci/variables/_variable_header.html.haml
new file mode 100644
index 00000000000..d3b7a5ae883
--- /dev/null
+++ b/app/views/ci/variables/_variable_header.html.haml
@@ -0,0 +1,16 @@
+- only_key_value = local_assigns.fetch(:only_key_value, false)
+
+%li.ci-variable-row.m-0.d-none.d-sm-block
+ .d-flex.w-100.align-items-center.pb-2
+ .bold.table-section.section-15.append-right-10
+ = s_('CiVariables|Type')
+ .bold.table-section.section-15.append-right-10
+ = s_('CiVariables|Key')
+ .bold.table-section.section-15.append-right-10
+ = s_('CiVariables|Value')
+ - unless only_key_value
+ .bold.table-section.section-20
+ = s_('CiVariables|State')
+ .bold.table-section.section-20
+ = s_('CiVariables|Masked')
+ = render_if_exists 'ci/variables/environment_scope_header'
diff --git a/app/views/ci/variables/_variable_row.html.haml b/app/views/ci/variables/_variable_row.html.haml
index 37257b3aa1c..b4930b41c09 100644
--- a/app/views/ci/variables/_variable_row.html.haml
+++ b/app/views/ci/variables/_variable_row.html.haml
@@ -20,18 +20,18 @@
- masked_input_name = "#{form_field}[variables_attributes][][masked]"
%li.js-row.ci-variable-row{ data: { is_persisted: "#{!id.nil?}" } }
- .ci-variable-row-body
+ .ci-variable-row-body.border-bottom
%input.js-ci-variable-input-id{ type: "hidden", name: id_input_name, value: id }
%input.js-ci-variable-input-destroy{ type: "hidden", name: destroy_input_name }
- %select.js-ci-variable-input-variable-type.ci-variable-body-item.form-control.select-control{ name: variable_type_input_name }
+ %select.js-ci-variable-input-variable-type.ci-variable-body-item.form-control.select-control.table-section.section-15{ name: variable_type_input_name }
= options_for_select(ci_variable_type_options, variable_type)
- %input.js-ci-variable-input-key.ci-variable-body-item.qa-ci-variable-input-key.form-control{ type: "text",
+ %input.js-ci-variable-input-key.ci-variable-body-item.qa-ci-variable-input-key.form-control.table-section.section-15{ type: "text",
name: key_input_name,
value: key,
placeholder: s_('CiVariables|Input variable key') }
- .ci-variable-body-item.gl-show-field-errors
+ .ci-variable-body-item.gl-show-field-errors.table-section.section-15.border-top-0.p-0
.form-control.js-secret-value-placeholder.qa-ci-variable-input-value{ class: ('hide' unless id) }
- = '*' * 20
+ = '*' * 17
%textarea.js-ci-variable-input-value.js-secret-value.qa-ci-variable-input-value.form-control{ class: ('hide' if id),
rows: 1,
name: value_input_name,
@@ -41,7 +41,7 @@
= s_("CiVariables|Cannot use Masked Variable with current value")
= link_to icon('question-circle'), help_page_path('ci/variables/README', anchor: 'masked-variables'), target: '_blank', rel: 'noopener noreferrer'
- unless only_key_value
- .ci-variable-body-item.ci-variable-protected-item
+ .ci-variable-body-item.ci-variable-protected-item.table-section.section-20.mr-0.border-top-0
.append-right-default
= s_("CiVariable|Protected")
%button{ type: 'button',
@@ -55,7 +55,7 @@
%span.toggle-icon
= sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked')
= sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked')
- .ci-variable-body-item.ci-variable-masked-item
+ .ci-variable-body-item.ci-variable-masked-item.table-section.section-20.mr-0.border-top-0
.append-right-default
= s_("CiVariable|Masked")
%button{ type: 'button',
@@ -70,5 +70,5 @@
= sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked')
= sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked')
= render_if_exists 'ci/variables/environment_scope', form_field: form_field, variable: variable
- %button.js-row-remove-button.ci-variable-row-remove-button{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') }
- = icon('minus-circle')
+ %button.js-row-remove-button.ci-variable-row-remove-button.table-section.section-5.border-top-0{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') }
+ = icon('minus-circle')
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index 91c51d5e091..0e15f581ddc 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -10,7 +10,7 @@
.branch-info
.branch-title
= sprite_icon('fork', size: 12)
- = link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated-100 ref-name prepend-left-8' do
+ = link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated-100 ref-name prepend-left-8 qa-branch-name' do
= branch.name
- if branch.name == @repository.root_ref
%span.badge.badge-primary.prepend-left-5 default
diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml
index 2e5747121b6..2db1f67a793 100644
--- a/app/views/shared/members/_member.html.haml
+++ b/app/views/shared/members/_member.html.haml
@@ -53,7 +53,7 @@
= time_ago_with_tooltip(member.created_at)
- if show_roles
- current_resource = @project || @group
- .controls.member-controls.row
+ .controls.member-controls
- if show_controls && member.source == current_resource
- if member.can_resend_invite?
diff --git a/changelogs/unreleased/61550-next-badge.yml b/changelogs/unreleased/61550-next-badge.yml
new file mode 100644
index 00000000000..122e394a68c
--- /dev/null
+++ b/changelogs/unreleased/61550-next-badge.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes next badge being always visible
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/ce-11542-remove-non-semantic-use-of-row-in-member-listing-controls.yml b/changelogs/unreleased/ce-11542-remove-non-semantic-use-of-row-in-member-listing-controls.yml
new file mode 100644
index 00000000000..c2dcd309abd
--- /dev/null
+++ b/changelogs/unreleased/ce-11542-remove-non-semantic-use-of-row-in-member-listing-controls.yml
@@ -0,0 +1,5 @@
+---
+title: Remove non-semantic use of `.row` in member listing controls
+merge_request: 28204
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-project-visibility-level-validation.yml b/changelogs/unreleased/fix-project-visibility-level-validation.yml
new file mode 100644
index 00000000000..9581a475842
--- /dev/null
+++ b/changelogs/unreleased/fix-project-visibility-level-validation.yml
@@ -0,0 +1,5 @@
+---
+title: Fix project visibility level validation
+merge_request: 28305
+author: Peter Marko
+type: fixed
diff --git a/changelogs/unreleased/include-ee-fixtures.yml b/changelogs/unreleased/include-ee-fixtures.yml
new file mode 100644
index 00000000000..ba500d92de3
--- /dev/null
+++ b/changelogs/unreleased/include-ee-fixtures.yml
@@ -0,0 +1,5 @@
+---
+title: Add EE fixtures to SeedFu list
+merge_request: 28241
+author:
+type: other
diff --git a/changelogs/unreleased/sh-fix-lfs-download-errors.yml b/changelogs/unreleased/sh-fix-lfs-download-errors.yml
new file mode 100644
index 00000000000..ad67df6bb06
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-lfs-download-errors.yml
@@ -0,0 +1,5 @@
+---
+title: Properly handle LFS Batch API response in project import
+merge_request: 28223
+author:
+type: fixed
diff --git a/config/initializers/seed_fu.rb b/config/initializers/seed_fu.rb
new file mode 100644
index 00000000000..2e48e41a311
--- /dev/null
+++ b/config/initializers/seed_fu.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+if Gitlab.ee?
+ SeedFu.fixture_paths += %W[ee/db/fixtures ee/db/fixtures/#{Rails.env}]
+end
diff --git a/doc/administration/geo/disaster_recovery/background_verification.md b/doc/administration/geo/disaster_recovery/background_verification.md
index 7d2fd51f834..c7299b6e196 100644
--- a/doc/administration/geo/disaster_recovery/background_verification.md
+++ b/doc/administration/geo/disaster_recovery/background_verification.md
@@ -157,6 +157,42 @@ For wikis:
sudo -u git -H bundle exec rake geo:verification:wiki:reset RAILS_ENV=production
```
+## Reconcile differences with checksum mismatches
+
+If the **primary** and **secondary** nodes have a checksum verification mismatch, the cause may not be apparent. To find the cause of a checksum mismatch:
+
+1. Navigate to the **Admin Area > Projects** dashboard on the **primary** node, find the
+ project that you want to check the checksum differences and click on the
+ **Edit** button:
+ ![Projects dashboard](img/checksum-differences-admin-projects.png)
+
+1. On the project admin page get the **Gitaly storage name**, and **Gitaly relative path**:
+ ![Project admin page](img/checksum-differences-admin-project-page.png)
+
+1. Navigate to the project's repository directory on both **primary** and **secondary** nodes. For an installation from source, the path is usually `/home/git/repositories`. For Omnibus installs, the path is usually `/var/opt/gitlab/git-data/repositories`. Note that if `git_data_dirs` is customized, check the directory layout on your server to be sure.
+
+ ```sh
+ cd /var/opt/gitlab/git-data/repositories
+ ```
+
+1. Run the following command on the **primary** node, redirecting the output to a file:
+
+ ```sh
+ git show-ref --head | grep -E "HEAD|(refs/(heads|tags|keep-around|merge-requests|environments|notes)/)" > primary-node-refs
+ ```
+
+1. Run the following command on the **secondary** node, redirecting the output to a file:
+
+ ```sh
+ git show-ref --head | grep -E "HEAD|(refs/(heads|tags|keep-around|merge-requests|environments|notes)/)" > secondary-node-refs
+ ```
+
+1. Copy the files from the previous steps on the same system, and do a diff between the contents:
+
+ ```sh
+ diff primary-node-refs secondary-node-refs
+ ```
+
## Current limitations
Until [issue #5064][ee-5064] is completed, background verification doesn't cover
diff --git a/doc/administration/geo/disaster_recovery/img/checksum-differences-admin-project-page.png b/doc/administration/geo/disaster_recovery/img/checksum-differences-admin-project-page.png
new file mode 100644
index 00000000000..fd51523104b
--- /dev/null
+++ b/doc/administration/geo/disaster_recovery/img/checksum-differences-admin-project-page.png
Binary files differ
diff --git a/doc/administration/geo/disaster_recovery/img/checksum-differences-admin-projects.png b/doc/administration/geo/disaster_recovery/img/checksum-differences-admin-projects.png
new file mode 100644
index 00000000000..b2a6da69d3d
--- /dev/null
+++ b/doc/administration/geo/disaster_recovery/img/checksum-differences-admin-projects.png
Binary files differ
diff --git a/doc/development/README.md b/doc/development/README.md
index 83a1145c020..cfbbd5aaeaa 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -44,6 +44,7 @@ description: 'Learn how to contribute to GitLab.'
- [`Gemfile` guidelines](gemfile.md)
- [Pry debugging](pry_debugging.md)
- [Sidekiq debugging](sidekiq_debugging.md)
+- [Accessing session data](session.md)
- [Gotchas](gotchas.md) to avoid
- [Avoid modules with instance variables](module_with_instance_variables.md) if possible
- [How to dump production data to staging](db_dump.md)
diff --git a/doc/development/session.md b/doc/development/session.md
new file mode 100644
index 00000000000..9edce3dbda0
--- /dev/null
+++ b/doc/development/session.md
@@ -0,0 +1,65 @@
+# Accessing session data
+
+Session data in GitLab is stored in Redis and can be accessed in a variety of ways.
+
+During a web request, for example:
+
+- Rails provides access to the session from within controllers through [`ActionDispatch::Session`](https://guides.rubyonrails.org/action_controller_overview.html#session).
+- Outside of controllers, it is possible to access the session through `Gitlab::Session`.
+
+Outside of a web request it is still possible to access sessions stored in Redis. For example:
+
+- Session IDs and contents can be [looked up directly in Redis](#redis).
+- Data about the UserAgent associated with the session can be accessed through `ActiveSession`.
+
+When storing values in a session it is best to:
+
+- Use simple primitives and avoid storing objects to avoid marshaling complications.
+- Clean up after unneeded variables to keep memory usage in Redis down.
+
+## Gitlab::Session
+
+Sometimes you might want to persist data in the session instead of another store like the database. `Gitlab::Session` lets you access this without passing the session around extensively. For example, you could access it from within a policy without having to pass the session through to each place permissions are checked from.
+
+The session has a hash-like interface, just like when using it from a controller. There is also `NamespacedSessionStore` for storing key-value data in a hash.
+
+```ruby
+# Lookup a value stored in the current session
+Gitlab::Session.current[:my_feature]
+
+# Modify the current session stored in redis
+Gitlab::Session.current[:my_feature] = value
+
+# Store key-value data namespaced under a key
+Gitlab::NamespacedSessionStore.new(:my_feature)[some_key] = value
+
+# Set the session for a block of code, such as for tests
+Gitlab::Session.with_session(my_feature: value) do
+ # Code that uses Session.current[:my_feature]
+end
+```
+
+## Redis
+
+Session data can be accessed directly through Redis. This can let you check up on a browser session when debugging.
+
+```ruby
+# Get a list of sessions
+session_ids = Gitlab::Redis::SharedState.with do |redis|
+ redis.smembers("#{Gitlab::Redis::SharedState::USER_SESSIONS_LOOKUP_NAMESPACE}:#{user.id}")
+end
+
+# Retrieve a specific session
+session_data = Gitlab::Redis::SharedState.with { |redis| redis.get("#{Gitlab::Redis::SharedState::SESSION_NAMESPACE}:#{session_id}") }
+Marshal.load(session_data)
+```
+
+## Getting device information with ActiveSession
+
+The [**Active Sessions** page on a user's profile](../user/profile/active_sessions.md) displays information about the device used to access each session. The methods used there to list sessions can also be useful for development.
+
+```ruby
+# Get list of sessions for a given user
+# Includes session_id and data from the UserAgent
+ActiveSession.list(user)
+```
diff --git a/doc/install/installation.md b/doc/install/installation.md
index c694f0ed691..9548aee5fe3 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -448,6 +448,18 @@ sudo -u git cp config/database.yml.postgresql config/database.yml
# MySQL only:
sudo -u git cp config/database.yml.mysql config/database.yml
+# PostgreSQL only:
+# Remove host, username, and password lines from config/database.yml.
+# Once modified, the `production` settings will be as follows:
+#
+# production:
+# adapter: postgresql
+# encoding: unicode
+# database: gitlabhq_production
+# pool: 10
+#
+sudo -u git -H editor config/database.yml
+
# MySQL and remote PostgreSQL only:
# Update username/password in config/database.yml.
# You only need to adapt the production settings (first part).
@@ -565,6 +577,18 @@ sudo -u git -H editor config.toml
For more information about configuring Gitaly see
[doc/administration/gitaly](../administration/gitaly).
+### Start Gitaly
+
+Gitaly must be running for the next section.
+
+```sh
+gitlab_path=/home/git/gitlab
+gitaly_path=/home/git/gitaly
+
+sudo -u git -H $gitlab_path/bin/daemon_with_pidfile $gitlab_path/tmp/pids/gitaly.pid \
+ $gitaly_path/gitaly $gitaly_path/config.toml >> $gitlab_path/log/gitaly.log 2>&1 &
+```
+
### Initialize Database and Activate Advanced Features
```sh
@@ -640,6 +664,12 @@ sudo -u git -H yarn install --production --pure-lockfile
sudo -u git -H bundle exec rake gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
```
+If `rake` fails with `JavaScript heap out of memory` error, try to run it with `NODE_OPTIONS` set as follows.
+
+```sh
+sudo -u git -H bundle exec rake gitlab:assets:compile RAILS_ENV=production NODE_ENV=production NODE_OPTIONS="--max_old_space_size=4096"
+```
+
### Start Your GitLab Instance
```sh
diff --git a/doc/integration/salesforce.md b/doc/integration/salesforce.md
index 18d42486fd6..8a99641a256 100644
--- a/doc/integration/salesforce.md
+++ b/doc/integration/salesforce.md
@@ -1,15 +1,15 @@
# SalesForce OmniAuth Provider
-You can integrate your GitLab instance with [SalesForce](https://www.salesforce.com/) to enable users to login to your GitLab instance with their SalesForce account.
+You can integrate your GitLab instance with [SalesForce](https://www.salesforce.com/) to enable users to login to your GitLab instance with their SalesForce account.
## Create SalesForce Application
To enable SalesForce OmniAuth provider, you must use SalesForce's credentials for your GitLab instance.
-To get the credentials (a pair of Client ID and Client Secret), you must register an application on UltraAuth.
+To get the credentials (a pair of Client ID and Client Secret), you must register an application on SalesForces.
1. Sign in to [SalesForce](https://www.salesforce.com/).
-1. Navigate to **Platform Tools/Apps** and click on **New Connected App**.
+1. Navigate to **Platform Tools/Apps/App Manager** and click on **New Connected App**.
1. Fill in the application details into the following fields:
- **Connected App Name** and **API Name**: Set to any value but consider something like `<Organization>'s GitLab`, `<Your Name>'s GitLab`, or something else that is descriptive.
@@ -64,7 +64,7 @@ To get the credentials (a pair of Client ID and Client Secret), you must registe
}
```
1. Change `SALESFORCE_CLIENT_ID` to the Consumer Key from the SalesForce connected application page.
-1. Change `SALESFORCE_CLIENT_SECRET` to the Client Secret from the SalesForce connected application page.
+1. Change `SALESFORCE_CLIENT_SECRET` to the Consumer Secret from the SalesForce connected application page.
![SalesForce App Secret Details](img/salesforce_app_secret_details.png)
1. Save the configuration file.
diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md
index 834a1e2423a..42fc4efa4ce 100644
--- a/doc/user/admin_area/settings/continuous_integration.md
+++ b/doc/user/admin_area/settings/continuous_integration.md
@@ -98,8 +98,7 @@ the group.
NOTE: **Note:**
Only available on GitLab.com.
-If you have a Group with a [paid plan](https://about.gitlab.com/pricing/#gitlab-com) on GitLab.com,
-then you can purchase additional CI minutes so your pipelines will not be blocked after you have
+You can purchase additional CI minutes so your pipelines will not be blocked after you have
used all your CI minutes from your main quota.
In order to purchase additional minutes, you should follow these steps:
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index b9fc3e00cff..b67818768e3 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1894,9 +1894,24 @@ msgstr ""
msgid "CiVariables|Input variable value"
msgstr ""
+msgid "CiVariables|Key"
+msgstr ""
+
+msgid "CiVariables|Masked"
+msgstr ""
+
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|State"
+msgstr ""
+
+msgid "CiVariables|Type"
+msgstr ""
+
+msgid "CiVariables|Value"
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index 9fabf83e2ce..c395e5f6011 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -113,8 +113,8 @@ module QA
has_css?(element_selector_css(name), wait: wait, text: text)
end
- def has_no_element?(name, wait: Capybara.default_max_wait_time)
- has_no_css?(element_selector_css(name), wait: wait)
+ def has_no_element?(name, text: nil, wait: Capybara.default_max_wait_time)
+ has_no_css?(element_selector_css(name), wait: wait, text: text)
end
def has_text?(text)
@@ -129,8 +129,8 @@ module QA
has_no_css?('.fa-spinner', wait: Capybara.default_max_wait_time)
end
- def within_element(name)
- page.within(element_selector_css(name)) do
+ def within_element(name, text: nil)
+ page.within(element_selector_css(name), text: text) do
yield
end
end
diff --git a/qa/qa/page/project/branches/show.rb b/qa/qa/page/project/branches/show.rb
index 922a6ddb086..762eacdab15 100644
--- a/qa/qa/page/project/branches/show.rb
+++ b/qa/qa/page/project/branches/show.rb
@@ -7,6 +7,7 @@ module QA
class Show < Page::Base
view 'app/views/projects/branches/_branch.html.haml' do
element :remove_btn
+ element :branch_name
end
view 'app/views/projects/branches/_panel.html.haml' do
element :all_branches
@@ -27,11 +28,9 @@ module QA
finished_loading?
end
- def has_branch_title?(branch_title)
+ def has_no_branch?(branch_name)
within_element(:all_branches) do
- within(".item-title") do
- has_text?(branch_title)
- end
+ has_no_element?(:branch_name, text: branch_name, wait: Support::Waiter::DEFAULT_MAX_WAIT_TIME)
end
end
@@ -48,15 +47,6 @@ module QA
click_element(:delete_merged_branches)
end
end
-
- def wait_for_texts_not_to_be_visible(texts)
- text_not_visible = wait do
- texts.all? do |text|
- has_no_text?(text)
- end
- end
- raise "Expected text(s) #{texts} not to be visible" unless text_not_visible
- end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb
index c2c2b6da90a..cf6c24fa873 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb
@@ -73,10 +73,9 @@ module QA
Page::Project::Branches::Show.perform do |branches_view|
branches_view.delete_branch(third_branch)
+ expect(branches_view).to have_no_branch(third_branch)
end
- expect(page).not_to have_content(third_branch)
-
Page::Project::Branches::Show.perform(&:delete_merged_branches)
expect(page).to have_content(
@@ -85,8 +84,7 @@ module QA
page.refresh
Page::Project::Branches::Show.perform do |branches_view|
- branches_view.wait_for_texts_not_to_be_visible([commit_message_of_second_branch])
- expect(branches_view).not_to have_branch_title(second_branch)
+ expect(branches_view).to have_no_branch(second_branch)
end
end
end
diff --git a/qa/qa/support/page/logging.rb b/qa/qa/support/page/logging.rb
index 69b6332ecce..ff505fdbddd 100644
--- a/qa/qa/support/page/logging.rb
+++ b/qa/qa/support/page/logging.rb
@@ -76,23 +76,18 @@ module QA
super
end
- def has_element?(name, text: nil, wait: Capybara.default_max_wait_time)
+ def has_element?(name, **kwargs)
found = super
- msg = ["has_element? :#{name}"]
- msg << %Q(with text "#{text}") if text
- msg << "(wait: #{wait})"
- msg << "returned: #{found}"
-
- log(msg.compact.join(' '))
+ log_has_element_or_not('has_element?', name, found, **kwargs)
found
end
- def has_no_element?(name, wait: Capybara.default_max_wait_time)
+ def has_no_element?(name, **kwargs)
found = super
- log("has_no_element? :#{name} returned #{found}")
+ log_has_element_or_not('has_no_element?', name, found, **kwargs)
found
end
@@ -149,6 +144,15 @@ module QA
def log(msg)
QA::Runtime::Logger.debug(msg)
end
+
+ def log_has_element_or_not(method, name, found, **kwargs)
+ msg = ["#{method} :#{name}"]
+ msg << %Q(with text "#{kwargs[:text]}") if kwargs[:text]
+ msg << "(wait: #{kwargs[:wait] || Capybara.default_max_wait_time})"
+ msg << "returned: #{found}"
+
+ log(msg.compact.join(' '))
+ end
end
end
end
diff --git a/qa/qa/support/waiter.rb b/qa/qa/support/waiter.rb
index 21a399b4a5f..fdcf2d7e157 100644
--- a/qa/qa/support/waiter.rb
+++ b/qa/qa/support/waiter.rb
@@ -3,9 +3,11 @@
module QA
module Support
module Waiter
+ DEFAULT_MAX_WAIT_TIME = 60
+
module_function
- def wait(max: 60, interval: 0.1)
+ def wait(max: DEFAULT_MAX_WAIT_TIME, interval: 0.1)
QA::Runtime::Logger.debug("with wait: max #{max}; interval #{interval}")
start = Time.now
diff --git a/qa/spec/page/logging_spec.rb b/qa/spec/page/logging_spec.rb
index 707a7ff6d98..99e96b81a51 100644
--- a/qa/spec/page/logging_spec.rb
+++ b/qa/spec/page/logging_spec.rb
@@ -93,7 +93,14 @@ describe QA::Support::Page::Logging do
allow(page).to receive(:has_no_css?).and_return(true)
expect { subject.has_no_element?(:element) }
- .to output(/has_no_element\? :element returned true/).to_stdout_from_any_process
+ .to output(/has_no_element\? :element \(wait: 2\) returned: true/).to_stdout_from_any_process
+ end
+
+ it 'logs has_no_element? with text' do
+ allow(page).to receive(:has_no_css?).and_return(true)
+
+ expect { subject.has_no_element?(:element, text: "more text") }
+ .to output(/has_no_element\? :element with text \"more text\" \(wait: 2\) returned: true/).to_stdout_from_any_process
end
it 'logs has_text?' do
diff --git a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
index 481b1a4d4b0..2839922fbd3 100644
--- a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
+++ b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
@@ -113,7 +113,7 @@ describe('AjaxFormVariableList', () => {
it('hides secret values', done => {
mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(200, {});
- const row = container.querySelector('.js-row:first-child');
+ const row = container.querySelector('.js-row');
const valueInput = row.querySelector('.js-ci-variable-input-value');
const valuePlaceholder = row.querySelector('.js-secret-value-placeholder');
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 2a17bd6002e..425096d7e80 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -214,6 +214,13 @@ describe Project do
expect(project2).not_to be_valid
end
+ it 'validates the visibility' do
+ expect_any_instance_of(described_class).to receive(:visibility_level_allowed_as_fork).and_call_original
+ expect_any_instance_of(described_class).to receive(:visibility_level_allowed_by_group).and_call_original
+
+ create(:project)
+ end
+
describe 'wiki path conflict' do
context "when the new path has been used by the wiki of other Project" do
it 'has an error on the name attribute' do
diff --git a/spec/services/projects/after_import_service_spec.rb b/spec/services/projects/after_import_service_spec.rb
index 95c11f71c5e..51d3fd18881 100644
--- a/spec/services/projects/after_import_service_spec.rb
+++ b/spec/services/projects/after_import_service_spec.rb
@@ -15,7 +15,7 @@ describe Projects::AfterImportService do
describe '#execute' do
before do
allow(Projects::HousekeepingService)
- .to receive(:new).with(project, :gc).and_return(housekeeping_service)
+ .to receive(:new).with(project).and_return(housekeeping_service)
allow(housekeeping_service)
.to receive(:execute).and_yield
diff --git a/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb
index d8427d0bf78..80debcd3a7a 100644
--- a/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb
+++ b/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb
@@ -33,7 +33,10 @@ describe Projects::LfsPointers::LfsDownloadLinkListService do
before do
allow(project).to receive(:lfs_enabled?).and_return(true)
- allow(Gitlab::HTTP).to receive(:post).and_return(objects_response)
+ response = instance_double(HTTParty::Response)
+ allow(response).to receive(:body).and_return(objects_response.to_json)
+ allow(response).to receive(:success?).and_return(true)
+ allow(Gitlab::HTTP).to receive(:post).and_return(response)
end
describe '#execute' do
@@ -89,6 +92,21 @@ describe Projects::LfsPointers::LfsDownloadLinkListService do
expect { subject.send(:get_download_links, new_oids) }.to raise_error(described_class::DownloadLinksError)
end
+
+ shared_examples 'JSON parse errors' do |body|
+ it 'raises error' do
+ response = instance_double(HTTParty::Response)
+ allow(response).to receive(:body).and_return(body)
+ allow(response).to receive(:success?).and_return(true)
+ allow(Gitlab::HTTP).to receive(:post).and_return(response)
+
+ expect { subject.send(:get_download_links, new_oids) }.to raise_error(described_class::DownloadLinksError)
+ end
+ end
+
+ it_behaves_like 'JSON parse errors', '{'
+ it_behaves_like 'JSON parse errors', '{}'
+ it_behaves_like 'JSON parse errors', '{ foo: 123 }'
end
describe '#parse_response_links' do
diff --git a/spec/support/features/variable_list_shared_examples.rb b/spec/support/features/variable_list_shared_examples.rb
index 693b796fbdc..92a19dd22a2 100644
--- a/spec/support/features/variable_list_shared_examples.rb
+++ b/spec/support/features/variable_list_shared_examples.rb
@@ -17,7 +17,7 @@ shared_examples 'variable list' do
visit page_path
# We check the first row because it re-sorts to alphabetical order on refresh
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-key').value).to eq('key')
expect(find('.js-ci-variable-input-value', visible: false).value).to eq('key_value')
end
@@ -38,7 +38,7 @@ shared_examples 'variable list' do
visit page_path
# We check the first row because it re-sorts to alphabetical order on refresh
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-key').value).to eq('key')
expect(find('.js-ci-variable-input-value', visible: false).value).to eq('key_value')
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('true')
@@ -59,7 +59,7 @@ shared_examples 'variable list' do
visit page_path
# We check the first row because it re-sorts to alphabetical order on refresh
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-key').value).to eq('key')
expect(find('.js-ci-variable-input-value', visible: false).value).to eq('key_value')
expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('true')
@@ -116,19 +116,19 @@ shared_examples 'variable list' do
page.within('.js-ci-variable-list-section') do
expect(first('.js-ci-variable-input-key').value).to eq(variable.key)
expect(first('.js-ci-variable-input-value', visible: false).value).to eq(variable.value)
- expect(page).to have_content('*' * 20)
+ expect(page).to have_content('*' * 17)
click_button('Reveal value')
expect(first('.js-ci-variable-input-key').value).to eq(variable.key)
expect(first('.js-ci-variable-input-value').value).to eq(variable.value)
- expect(page).not_to have_content('*' * 20)
+ expect(page).not_to have_content('*' * 17)
click_button('Hide value')
expect(first('.js-ci-variable-input-key').value).to eq(variable.key)
expect(first('.js-ci-variable-input-value', visible: false).value).to eq(variable.value)
- expect(page).to have_content('*' * 20)
+ expect(page).to have_content('*' * 17)
end
end
@@ -149,7 +149,7 @@ shared_examples 'variable list' do
page.within('.js-ci-variable-list-section') do
click_button('Reveal value')
- page.within('.js-row:nth-child(1)') do
+ page.within('.js-row:nth-child(2)') do
find('.js-ci-variable-input-key').set('new_key')
find('.js-ci-variable-input-value').set('new_value')
end
@@ -159,7 +159,7 @@ shared_examples 'variable list' do
visit page_path
- page.within('.js-row:nth-child(1)') do
+ page.within('.js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-key').value).to eq('new_key')
expect(find('.js-ci-variable-input-value', visible: false).value).to eq('new_value')
end
@@ -181,7 +181,7 @@ shared_examples 'variable list' do
visit page_path
# We check the first row because it re-sorts to alphabetical order on refresh
- page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(3)') do
find('.ci-variable-protected-item .js-project-feature-toggle').click
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('true')
@@ -193,7 +193,7 @@ shared_examples 'variable list' do
visit page_path
# We check the first row because it re-sorts to alphabetical order on refresh
- page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(3)') do
expect(find('.js-ci-variable-input-key').value).to eq('unprotected_key')
expect(find('.js-ci-variable-input-value', visible: false).value).to eq('unprotected_value')
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('true')
@@ -215,7 +215,7 @@ shared_examples 'variable list' do
visit page_path
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
find('.ci-variable-protected-item .js-project-feature-toggle').click
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('false')
@@ -226,7 +226,7 @@ shared_examples 'variable list' do
visit page_path
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-key').value).to eq('protected_key')
expect(find('.js-ci-variable-input-value', visible: false).value).to eq('protected_value')
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('false')
@@ -234,7 +234,7 @@ shared_examples 'variable list' do
end
it 'edits variable to be unmasked' do
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('true')
find('.ci-variable-masked-item .js-project-feature-toggle').click
@@ -247,13 +247,13 @@ shared_examples 'variable list' do
visit page_path
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('false')
end
end
it 'edits variable to be masked' do
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('true')
find('.ci-variable-masked-item .js-project-feature-toggle').click
@@ -266,7 +266,7 @@ shared_examples 'variable list' do
visit page_path
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('false')
find('.ci-variable-masked-item .js-project-feature-toggle').click
@@ -279,7 +279,7 @@ shared_examples 'variable list' do
visit page_path
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('true')
end
end
@@ -302,7 +302,7 @@ shared_examples 'variable list' do
expect(page).to have_selector('.js-row', count: 4)
# Remove the `akey` variable
- page.within('.js-row:nth-child(2)') do
+ page.within('.js-row:nth-child(3)') do
first('.js-row-remove-button').click
end