summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue1
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list_item.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue60
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue2
-rw-r--r--app/controllers/application_controller.rb1
-rw-r--r--app/controllers/concerns/invalid_utf8_error_handler.rb25
-rw-r--r--app/views/devise/shared/_tabs_ldap.html.haml2
-rw-r--r--app/views/errors/precondition_failed.html.haml8
-rw-r--r--app/views/projects/commits/_commit.html.haml2
-rw-r--r--changelogs/unreleased/enable-force-write-auth-keys-restore.yml5
-rw-r--r--changelogs/unreleased/fa-handle_invalid_utf8_errors.yml5
-rw-r--r--changelogs/unreleased/fix-committer-typo.yml5
-rw-r--r--changelogs/unreleased/leipert-fix-mr-widget-header-margins.yml5
-rw-r--r--changelogs/unreleased/osw-clean-up-phase-for-diff-files-removal.yml5
-rw-r--r--db/post_migrate/20180913051323_consume_remaining_diff_files_deletion_jobs.rb23
-rw-r--r--doc/raketasks/backup_restore.md2
-rw-r--r--lib/quality/helm_client.rb47
-rw-r--r--lib/quality/kubernetes_client.rb32
-rw-r--r--lib/tasks/gitlab/shell.rake46
-rw-r--r--qa/qa/factory/resource/user.rb5
-rw-r--r--qa/qa/page/base.rb4
-rw-r--r--qa/qa/page/main/login.rb29
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb30
-rwxr-xr-xscripts/review_apps/automated_cleanup.rb109
-rwxr-xr-xscripts/review_apps/review-apps.sh184
-rw-r--r--spec/controllers/application_controller_spec.rb34
-rw-r--r--spec/features/projects/tree/create_directory_spec.rb2
-rw-r--r--spec/features/projects/tree/create_file_spec.rb2
-rw-r--r--spec/helpers/commits_helper_spec.rb2
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/list_item_spec.js4
-rw-r--r--spec/javascripts/ide/components/repo_commit_section_spec.js59
-rw-r--r--spec/javascripts/vue_mr_widget/mr_widget_options_spec.js4
-rw-r--r--spec/lib/quality/helm_client_spec.rb62
-rw-r--r--spec/lib/quality/kubernetes_client_spec.rb25
-rw-r--r--spec/models/commit_spec.rb8
-rw-r--r--spec/services/merge_requests/rebase_service_spec.rb2
36 files changed, 733 insertions, 112 deletions
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue b/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue
index c3ca147e850..3aca38399fb 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue
@@ -52,6 +52,7 @@ export default {
</strong>
<changed-file-icon
:file="activeFile"
+ class="ml-0"
/>
<div class="ml-auto">
<button
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
index 10c78a80302..ee0e72cd05f 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
@@ -120,10 +120,6 @@ export default {
:css-classes="iconClass"
/>
</div>
- <component
- :is="actionComponent"
- :path="file.path"
- />
</div>
</div>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
index 4c3f8dff3c4..acfdab3a015 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
@@ -28,11 +28,17 @@ export default {
return this.mr.divergedCommitsCount > 0;
},
commitsBehindText() {
- return sprintf(s__('mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch'), {
- commitsBehindLinkStart: `<a href="${_.escape(this.mr.targetBranchPath)}">`,
- commitsBehind: n__('%d commit behind', '%d commits behind', this.mr.divergedCommitsCount),
- commitsBehindLinkEnd: '</a>',
- }, false);
+ return sprintf(
+ s__(
+ 'mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch',
+ ),
+ {
+ commitsBehindLinkStart: `<a href="${_.escape(this.mr.targetBranchPath)}">`,
+ commitsBehind: n__('%d commit behind', '%d commits behind', this.mr.divergedCommitsCount),
+ commitsBehindLinkEnd: '</a>',
+ },
+ false,
+ );
},
branchNameClipboardData() {
// This supports code in app/assets/javascripts/copy_to_clipboard.js that
@@ -45,17 +51,24 @@ export default {
},
webIdePath() {
if (this.mr.canPushToSourceBranch) {
- return mergeUrlParams({
- target_project: this.mr.sourceProjectFullPath !== this.mr.targetProjectFullPath ?
- this.mr.targetProjectFullPath : '',
- }, webIDEUrl(`/${this.mr.sourceProjectFullPath}/merge_requests/${this.mr.iid}`));
+ return mergeUrlParams(
+ {
+ target_project:
+ this.mr.sourceProjectFullPath !== this.mr.targetProjectFullPath
+ ? this.mr.targetProjectFullPath
+ : '',
+ },
+ webIDEUrl(`/${this.mr.sourceProjectFullPath}/merge_requests/${this.mr.iid}`),
+ );
}
return null;
},
ideButtonTitle() {
return !this.mr.canPushToSourceBranch
- ? s__('mrWidget|You are not allowed to edit this project directly. Please fork to make changes.')
+ ? s__(
+ 'mrWidget|You are not allowed to edit this project directly. Please fork to make changes.',
+ )
: '';
},
},
@@ -104,37 +117,34 @@ export default {
<div
v-if="mr.isOpen"
- class="branch-actions"
+ class="branch-actions d-flex"
>
- <span
+ <a
+ v-if="!mr.sourceBranchRemoved"
v-tooltip
+ :href="webIdePath"
:title="ideButtonTitle"
+ :class="{ disabled: !mr.canPushToSourceBranch }"
+ class="btn btn-default js-web-ide d-none d-md-inline-block append-right-8"
data-placement="bottom"
tabindex="0"
+ role="button"
>
- <a
- v-if="!mr.sourceBranchRemoved"
- :href="webIdePath"
- :class="{ disabled: !mr.canPushToSourceBranch }"
- class="btn btn-default inline js-web-ide d-none d-md-inline-block"
- role="button"
- >
- {{ s__("mrWidget|Open in Web IDE") }}
- </a>
- </span>
+ {{ s__("mrWidget|Open in Web IDE") }}
+ </a>
<button
:disabled="mr.sourceBranchRemoved"
data-target="#modal_merge_info"
data-toggle="modal"
- class="btn btn-default inline js-check-out-branch"
+ class="btn btn-default js-check-out-branch append-right-default"
type="button"
>
{{ s__("mrWidget|Check out branch") }}
</button>
- <span class="dropdown prepend-left-10">
+ <span class="dropdown">
<button
type="button"
- class="btn inline dropdown-toggle"
+ class="btn dropdown-toggle"
data-toggle="dropdown"
aria-label="Download as"
aria-haspopup="true"
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index b5eaaf054e7..0e445a29de4 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -114,6 +114,8 @@ export default {
},
beforeDestroy() {
eventHub.$off('mr.discussion.updated', this.checkStatus);
+ this.pollingInterval.destroy();
+ this.deploymentsInterval.destroy();
},
methods: {
createService(store) {
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 838527aaa41..fb2808edf47 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -12,6 +12,7 @@ class ApplicationController < ActionController::Base
include WorkhorseHelper
include EnforcesTwoFactorAuthentication
include WithPerformanceBar
+ include InvalidUTF8ErrorHandler
before_action :authenticate_sessionless_user!
before_action :authenticate_user!
diff --git a/app/controllers/concerns/invalid_utf8_error_handler.rb b/app/controllers/concerns/invalid_utf8_error_handler.rb
new file mode 100644
index 00000000000..a7ea0d00a43
--- /dev/null
+++ b/app/controllers/concerns/invalid_utf8_error_handler.rb
@@ -0,0 +1,25 @@
+module InvalidUTF8ErrorHandler
+ extend ActiveSupport::Concern
+
+ included do
+ rescue_from ArgumentError, with: :handle_invalid_utf8
+ end
+
+ private
+
+ def handle_invalid_utf8(error)
+ if error.message == "invalid byte sequence in UTF-8"
+ render_412
+ else
+ raise(error)
+ end
+ end
+
+ def render_412
+ respond_to do |format|
+ format.html { render "errors/precondition_failed", layout: "errors", status: 412 }
+ format.js { render json: { error: 'Invalid UTF-8' }, status: :precondition_failed, content_type: 'application/json' }
+ format.any { head :precondition_failed }
+ end
+ end
+end
diff --git a/app/views/devise/shared/_tabs_ldap.html.haml b/app/views/devise/shared/_tabs_ldap.html.haml
index 3764e86dd8b..7dced0942f5 100644
--- a/app/views/devise/shared/_tabs_ldap.html.haml
+++ b/app/views/devise/shared/_tabs_ldap.html.haml
@@ -10,4 +10,4 @@
= link_to 'Standard', '#login-pane', class: 'nav-link qa-standard-tab', 'data-toggle' => 'tab'
- if allow_signup?
%li.nav-item
- = link_to 'Register', '#register-pane', class: 'nav-link', 'data-toggle' => 'tab'
+ = link_to 'Register', '#register-pane', class: 'nav-link qa-register-tab', 'data-toggle' => 'tab'
diff --git a/app/views/errors/precondition_failed.html.haml b/app/views/errors/precondition_failed.html.haml
new file mode 100644
index 00000000000..aa3869f33a9
--- /dev/null
+++ b/app/views/errors/precondition_failed.html.haml
@@ -0,0 +1,8 @@
+- content_for(:title, 'Encoding Error')
+%img{ :alt => "GitLab Logo", :src => image_path('logo.svg') }
+ %h1
+ 412
+.container
+ %h3 Precondition failed
+ %hr
+ %p Page can't be loaded because of invalid parameters.
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 7951a5ddc9e..45b4f03fa0c 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -37,7 +37,7 @@
%button.text-expander.js-toggle-button
= sprite_icon('ellipsis_h', size: 12)
- .commiter
+ .committer
- commit_author_link = commit_author_link(commit, avatar: false, size: 24)
- commit_timeago = time_ago_with_tooltip(commit.authored_date, placement: 'bottom')
- commit_text = _('%{commit_author_link} authored %{commit_timeago}') % { commit_author_link: commit_author_link, commit_timeago: commit_timeago }
diff --git a/changelogs/unreleased/enable-force-write-auth-keys-restore.yml b/changelogs/unreleased/enable-force-write-auth-keys-restore.yml
new file mode 100644
index 00000000000..f6c83cc7950
--- /dev/null
+++ b/changelogs/unreleased/enable-force-write-auth-keys-restore.yml
@@ -0,0 +1,5 @@
+---
+title: Enable the ability to use the force env for rebuilding authorized_keys during a restore
+merge_request: 21896
+author:
+type: fixed
diff --git a/changelogs/unreleased/fa-handle_invalid_utf8_errors.yml b/changelogs/unreleased/fa-handle_invalid_utf8_errors.yml
new file mode 100644
index 00000000000..9cae193d858
--- /dev/null
+++ b/changelogs/unreleased/fa-handle_invalid_utf8_errors.yml
@@ -0,0 +1,5 @@
+---
+title: Render 412 when invalid UTF-8 parameters are passed to controller
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/fix-committer-typo.yml b/changelogs/unreleased/fix-committer-typo.yml
new file mode 100644
index 00000000000..6033912b6c0
--- /dev/null
+++ b/changelogs/unreleased/fix-committer-typo.yml
@@ -0,0 +1,5 @@
+---
+title: Fix committer typo
+merge_request: 21899
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/leipert-fix-mr-widget-header-margins.yml b/changelogs/unreleased/leipert-fix-mr-widget-header-margins.yml
new file mode 100644
index 00000000000..9c23244d48b
--- /dev/null
+++ b/changelogs/unreleased/leipert-fix-mr-widget-header-margins.yml
@@ -0,0 +1,5 @@
+---
+title: Fix merge request header margins
+merge_request: 21878
+author:
+type: other
diff --git a/changelogs/unreleased/osw-clean-up-phase-for-diff-files-removal.yml b/changelogs/unreleased/osw-clean-up-phase-for-diff-files-removal.yml
new file mode 100644
index 00000000000..03189d934a7
--- /dev/null
+++ b/changelogs/unreleased/osw-clean-up-phase-for-diff-files-removal.yml
@@ -0,0 +1,5 @@
+---
+title: Add clean-up phase for ScheduleDiffFilesDeletion migration
+merge_request: 21734
+author:
+type: other
diff --git a/db/post_migrate/20180913051323_consume_remaining_diff_files_deletion_jobs.rb b/db/post_migrate/20180913051323_consume_remaining_diff_files_deletion_jobs.rb
new file mode 100644
index 00000000000..ed9422a3894
--- /dev/null
+++ b/db/post_migrate/20180913051323_consume_remaining_diff_files_deletion_jobs.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class ConsumeRemainingDiffFilesDeletionJobs < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ MIGRATION = 'ScheduleDiffFilesDeletion'.freeze
+ TMP_INDEX = 'tmp_partial_diff_id_with_files_index'.freeze
+
+ def up
+ # Perform any ongoing background migration that might still be scheduled.
+ Gitlab::BackgroundMigration.steal(MIGRATION)
+
+ remove_concurrent_index_by_name(:merge_request_diffs, TMP_INDEX)
+ end
+
+ def down
+ add_concurrent_index(:merge_request_diffs, :id, where: "(state NOT IN ('without_files', 'empty'))", name: TMP_INDEX)
+ end
+end
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index 1d29f6d4e43..98fce7efb0b 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -523,7 +523,7 @@ more of the following options:
- `BACKUP=timestamp_of_backup` - Required if more than one backup exists.
Read what the [backup timestamp is about](#backup-timestamp).
-- `force=yes` - Does not ask if the authorized_keys file should get regenerated and assumes 'yes' for warning that database tables will be removed.
+- `force=yes` - Does not ask if the authorized_keys file should get regenerated and assumes 'yes' for warning that database tables will be removed, enabling the "Write to authorized_keys file" setting, and updating LDAP providers.
If you are restoring into directories that are mountpoints you will need to make
sure these directories are empty before attempting a restore. Otherwise GitLab
diff --git a/lib/quality/helm_client.rb b/lib/quality/helm_client.rb
new file mode 100644
index 00000000000..49d953da681
--- /dev/null
+++ b/lib/quality/helm_client.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'time'
+require_relative '../gitlab/popen' unless defined?(Gitlab::Popen)
+
+module Quality
+ class HelmClient
+ attr_reader :namespace
+
+ Release = Struct.new(:name, :revision, :last_update, :status, :chart, :namespace) do
+ def revision
+ @revision ||= self[:revision].to_i
+ end
+
+ def last_update
+ @last_update ||= Time.parse(self[:last_update])
+ end
+ end
+
+ def initialize(namespace: ENV['KUBE_NAMESPACE'])
+ @namespace = namespace
+ end
+
+ def releases(args: [])
+ command = ['list', %(--namespace "#{namespace}"), *args]
+
+ run_command(command)
+ .stdout
+ .lines
+ .select { |line| line.include?(namespace) }
+ .map { |line| Release.new(*line.split(/\t/).map(&:strip)) }
+ end
+
+ def delete(release_name:)
+ run_command(['delete', '--purge', release_name])
+ end
+
+ private
+
+ def run_command(command)
+ final_command = ['helm', *command].join(' ')
+ puts "Running command: `#{final_command}`" # rubocop:disable Rails/Output
+
+ Gitlab::Popen.popen_with_detail([final_command])
+ end
+ end
+end
diff --git a/lib/quality/kubernetes_client.rb b/lib/quality/kubernetes_client.rb
new file mode 100644
index 00000000000..e366a688e3e
--- /dev/null
+++ b/lib/quality/kubernetes_client.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require_relative '../gitlab/popen' unless defined?(Gitlab::Popen)
+
+module Quality
+ class KubernetesClient
+ attr_reader :namespace
+
+ def initialize(namespace: ENV['KUBE_NAMESPACE'])
+ @namespace = namespace
+ end
+
+ def cleanup(release_name:)
+ command = ['kubectl']
+ command << %(-n "#{namespace}" get ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa 2>&1)
+ command << '|' << %(grep "#{release_name}")
+ command << '|' << "awk '{print $1}'"
+ command << '|' << %(xargs kubectl -n "#{namespace}" delete)
+ command << '||' << 'true'
+
+ run_command(command)
+ end
+
+ private
+
+ def run_command(command)
+ puts "Running command: `#{command.join(' ')}`" # rubocop:disable Rails/Output
+
+ Gitlab::Popen.popen_with_detail(command)
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake
index 4fcbbbf8c9d..0ebc6f00793 100644
--- a/lib/tasks/gitlab/shell.rake
+++ b/lib/tasks/gitlab/shell.rake
@@ -92,9 +92,11 @@ namespace :gitlab do
def setup
warn_user_is_not_gitlab
+ ensure_write_to_authorized_keys_is_enabled
+
unless ENV['force'] == 'yes'
- puts "This will rebuild an authorized_keys file."
- puts "You will lose any data stored in authorized_keys file."
+ puts "This task will now rebuild the authorized_keys file."
+ puts "You will lose any data stored in the authorized_keys file."
ask_to_continue
puts ""
end
@@ -118,4 +120,44 @@ namespace :gitlab do
puts "Quitting...".color(:red)
exit 1
end
+
+ def ensure_write_to_authorized_keys_is_enabled
+ return if Gitlab::CurrentSettings.current_application_settings.authorized_keys_enabled
+
+ puts authorized_keys_is_disabled_warning
+
+ unless ENV['force'] == 'yes'
+ puts 'Do you want to permanently enable the "Write to authorized_keys file" setting now?'
+ ask_to_continue
+ end
+
+ puts 'Enabling the "Write to authorized_keys file" setting...'
+ Gitlab::CurrentSettings.current_application_settings.update!(authorized_keys_enabled: true)
+
+ puts 'Successfully enabled "Write to authorized_keys file"!'
+ puts ''
+ end
+
+ def authorized_keys_is_disabled_warning
+ <<-MSG.strip_heredoc
+ WARNING
+
+ The "Write to authorized_keys file" setting is disabled, which prevents
+ the file from being rebuilt!
+
+ It should be enabled for most GitLab installations. Large installations
+ may wish to disable it as part of speeding up SSH operations.
+
+ See https://docs.gitlab.com/ee/administration/operations/fast_ssh_key_lookup.html
+
+ If you did not intentionally disable this option in Admin Area > Settings,
+ then you may have been affected by the 9.3.0 bug in which the new setting
+ was disabled by default.
+
+ https://gitlab.com/gitlab-org/gitlab-ee/issues/2738
+
+ It was reverted in 9.3.1 and fixed in 9.3.3, however, if Settings were
+ saved while the setting was unchecked, then it is still disabled.
+ MSG
+ end
end
diff --git a/qa/qa/factory/resource/user.rb b/qa/qa/factory/resource/user.rb
index eac2a873bd5..34b52223b2d 100644
--- a/qa/qa/factory/resource/user.rb
+++ b/qa/qa/factory/resource/user.rb
@@ -37,7 +37,10 @@ module QA
product(:password) { |factory| factory.password }
def fabricate!
- Page::Menu::Main.perform { |main| main.sign_out }
+ # Don't try to log-out if we're not logged-in
+ if Page::Menu::Main.act { has_personal_area?(wait: 0) }
+ Page::Menu::Main.perform { |main| main.sign_out }
+ end
if credentials_given?
Page::Main::Login.perform do |login|
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index 30e35bf7abb..a87536671c6 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -76,6 +76,10 @@ module QA
find_element(name).set(content)
end
+ def has_element?(name)
+ has_css?(element_selector_css(name))
+ end
+
def within_element(name)
page.within(element_selector_css(name)) do
yield
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index e9e49964e63..89542b49d0e 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -23,6 +23,7 @@ module QA
view 'app/views/devise/shared/_tabs_ldap.html.haml' do
element :ldap_tab
element :standard_tab
+ element :register_tab
end
view 'app/views/devise/shared/_tabs_normal.html.haml' do
@@ -35,7 +36,7 @@ module QA
# we need to wait for the instance to start. That said, in some cases
# we are already logged-in so we check both cases here.
wait(max: 500) do
- page.has_css?('.login-page') ||
+ has_css?('.login-page') ||
Page::Menu::Main.act { has_personal_area?(wait: 0) }
end
end
@@ -78,12 +79,28 @@ module QA
'/users/sign_in'
end
+ def has_sign_in_tab?
+ has_element?(:sign_in_tab)
+ end
+
+ def has_ldap_tab?
+ has_element?(:ldap_tab)
+ end
+
+ def has_standard_tab?
+ has_element?(:standard_tab)
+ end
+
def sign_in_tab?
- page.has_button?('Sign in')
+ has_css?(".active", text: 'Sign in')
end
def ldap_tab?
- page.has_link?('LDAP')
+ has_css?(".active", text: 'LDAP')
+ end
+
+ def standard_tab?
+ has_css?(".active", text: 'Standard')
end
def switch_to_sign_in_tab
@@ -113,8 +130,8 @@ module QA
end
def sign_in_using_gitlab_credentials(user)
- switch_to_sign_in_tab unless sign_in_tab?
- switch_to_standard_tab if ldap_tab?
+ switch_to_sign_in_tab if has_sign_in_tab?
+ switch_to_standard_tab if has_standard_tab?
fill_element :login_field, user.username
fill_element :password_field, user.password
@@ -122,7 +139,7 @@ module QA
end
def set_initial_password_if_present
- return unless page.has_content?('Change your password')
+ return unless has_content?('Change your password')
fill_element :password_field, Runtime::User.password
fill_element :password_confirmation, Runtime::User.password
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
new file mode 100644
index 00000000000..478a5cb9c4c
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module QA
+ shared_examples 'registration and login' do
+ it 'user registers and logs in' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+
+ Factory::Resource::User.fabricate!
+
+ # TODO, since `Signed in successfully` message was removed
+ # this is the only way to tell if user is signed in correctly.
+ #
+ Page::Menu::Main.perform do |menu|
+ expect(menu).to have_personal_area
+ end
+ end
+ end
+
+ context :manage do
+ describe 'standard' do
+ it_behaves_like 'registration and login'
+ end
+ end
+
+ context :manage, :orchestrated, :ldap do
+ describe 'while LDAP is enabled' do
+ it_behaves_like 'registration and login'
+ end
+ end
+end
diff --git a/scripts/review_apps/automated_cleanup.rb b/scripts/review_apps/automated_cleanup.rb
new file mode 100755
index 00000000000..ea53f89c844
--- /dev/null
+++ b/scripts/review_apps/automated_cleanup.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+require 'gitlab'
+require_relative File.expand_path('../../lib/quality/helm_client.rb', __dir__)
+require_relative File.expand_path('../../lib/quality/kubernetes_client.rb', __dir__)
+
+class AutomatedCleanup
+ attr_reader :project_path, :gitlab_token, :cleaned_up_releases
+
+ def initialize(project_path: ENV['CI_PROJECT_PATH'], gitlab_token: ENV['GITLAB_BOT_REVIEW_APPS_CLEANUP_TOKEN'])
+ @project_path = project_path
+ @gitlab_token = gitlab_token
+ @cleaned_up_releases = []
+ end
+
+ def gitlab
+ @gitlab ||= begin
+ Gitlab.configure do |config|
+ config.endpoint = 'https://gitlab.com/api/v4'
+ # gitlab-bot's token "GitLab review apps cleanup"
+ config.private_token = gitlab_token
+ end
+
+ Gitlab
+ end
+ end
+
+ def helm
+ @helm ||= Quality::HelmClient.new
+ end
+
+ def kubernetes
+ @kubernetes ||= Quality::KubernetesClient.new
+ end
+
+ def perform_gitlab_environment_cleanup!(days_for_stop:, days_for_delete:)
+ puts "Checking for review apps not updated in the last #{days_for_stop} days..."
+
+ checked_environments = []
+ delete_threshold = threshold_time(days: days_for_delete)
+ stop_threshold = threshold_time(days: days_for_stop)
+ gitlab.deployments(project_path, per_page: 50).auto_paginate do |deployment|
+ next unless deployment.environment.name.start_with?('review/')
+ next if checked_environments.include?(deployment.environment.slug)
+
+ puts
+
+ checked_environments << deployment.environment.slug
+ deployed_at = Time.parse(deployment.created_at)
+
+ if deployed_at < delete_threshold
+ print_release_state(subject: 'Review app', release_name: deployment.environment.slug, release_date: deployment.created_at, action: 'deleting')
+ gitlab.delete_environment(project_path, deployment.environment.id)
+ cleaned_up_releases << deployment.environment.slug
+ elsif deployed_at < stop_threshold
+ print_release_state(subject: 'Review app', release_name: deployment.environment.slug, release_date: deployment.created_at, action: 'stopping')
+ gitlab.stop_environment(project_path, deployment.environment.id)
+ cleaned_up_releases << deployment.environment.slug
+ else
+ print_release_state(subject: 'Review app', release_name: deployment.environment.slug, release_date: deployment.created_at, action: 'leaving')
+ end
+ end
+ end
+
+ def perform_helm_releases_cleanup!(days:)
+ puts "Checking for Helm releases not updated in the last #{days} days..."
+
+ threshold_day = threshold_time(days: days)
+ helm.releases(args: ['--deployed', '--failed', '--date', '--reverse', '--max 25']).each do |release|
+ next if cleaned_up_releases.include?(release.name)
+
+ if release.last_update < threshold_day
+ print_release_state(subject: 'Release', release_name: release.name, release_date: release.last_update, action: 'cleaning')
+ helm.delete(release_name: release.name)
+ kubernetes.cleanup(release_name: release.name)
+ else
+ print_release_state(subject: 'Release', release_name: release.name, release_date: release.last_update, action: 'leaving')
+ end
+ end
+ end
+
+ def threshold_time(days:)
+ Time.now - days * 24 * 3600
+ end
+
+ def print_release_state(subject:, release_name:, release_date:, action:)
+ puts "\n#{subject} '#{release_name}' was last deployed on #{release_date}: #{action} it."
+ end
+end
+
+def timed(task)
+ start = Time.now
+ yield(self)
+ puts "#{task} finished in #{Time.now - start} seconds.\n"
+end
+
+automated_cleanup = AutomatedCleanup.new
+
+timed('Review apps cleanup') do
+ automated_cleanup.perform_gitlab_environment_cleanup!(days_for_stop: 5, days_for_delete: 6)
+end
+
+puts
+
+timed('Helm releases cleanup') do
+ automated_cleanup.perform_helm_releases_cleanup!(days: 7)
+end
+
+exit(0)
diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh
new file mode 100755
index 00000000000..78293464265
--- /dev/null
+++ b/scripts/review_apps/review-apps.sh
@@ -0,0 +1,184 @@
+[[ "$TRACE" ]] && set -x
+export TILLER_NAMESPACE="$KUBE_NAMESPACE"
+
+function check_kube_domain() {
+ if [ -z ${REVIEW_APPS_DOMAIN+x} ]; then
+ echo "In order to deploy or use Review Apps, REVIEW_APPS_DOMAIN variable must be set"
+ echo "You can do it in Auto DevOps project settings or defining a variable at group or project level"
+ echo "You can also manually add it in .gitlab-ci.yml"
+ false
+ else
+ true
+ fi
+}
+
+function download_gitlab_chart() {
+ curl -o gitlab.tar.bz2 https://gitlab.com/charts/gitlab/-/archive/$GITLAB_HELM_CHART_REF/gitlab-$GITLAB_HELM_CHART_REF.tar.bz2
+ tar -xjf gitlab.tar.bz2
+ cd gitlab-$GITLAB_HELM_CHART_REF
+
+ helm init --client-only
+ helm repo add gitlab https://charts.gitlab.io
+ helm dependency update
+ helm dependency build
+}
+
+function ensure_namespace() {
+ kubectl describe namespace "$KUBE_NAMESPACE" || kubectl create namespace "$KUBE_NAMESPACE"
+}
+
+function install_tiller() {
+ echo "Checking Tiller..."
+ helm init --upgrade
+ kubectl rollout status -n "$TILLER_NAMESPACE" -w "deployment/tiller-deploy"
+ if ! helm version --debug; then
+ echo "Failed to init Tiller."
+ return 1
+ fi
+ echo ""
+}
+
+function create_secret() {
+ echo "Create secret..."
+
+ kubectl create secret generic -n "$KUBE_NAMESPACE" \
+ $CI_ENVIRONMENT_SLUG-gitlab-initial-root-password \
+ --from-literal=password=$REVIEW_APPS_ROOT_PASSWORD \
+ --dry-run -o json | kubectl apply -f -
+}
+
+function previousDeployFailed() {
+ set +e
+ echo "Checking for previous deployment of $CI_ENVIRONMENT_SLUG"
+ deployment_status=$(helm status $CI_ENVIRONMENT_SLUG >/dev/null 2>&1)
+ status=$?
+ # if `status` is `0`, deployment exists, has a status
+ if [ $status -eq 0 ]; then
+ echo "Previous deployment found, checking status"
+ deployment_status=$(helm status $CI_ENVIRONMENT_SLUG | grep ^STATUS | cut -d' ' -f2)
+ echo "Previous deployment state: $deployment_status"
+ if [[ "$deployment_status" == "FAILED" || "$deployment_status" == "PENDING_UPGRADE" || "$deployment_status" == "PENDING_INSTALL" ]]; then
+ status=0;
+ else
+ status=1;
+ fi
+ else
+ echo "Previous deployment NOT found."
+ fi
+ set -e
+ return $status
+}
+
+function deploy() {
+ track="${1-stable}"
+ name="$CI_ENVIRONMENT_SLUG"
+
+ if [[ "$track" != "stable" ]]; then
+ name="$name-$track"
+ fi
+
+ replicas="1"
+ service_enabled="false"
+ postgres_enabled="$POSTGRES_ENABLED"
+ gitlab_migrations_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-rails-ce"
+ gitlab_sidekiq_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-sidekiq-ce"
+ gitlab_unicorn_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-unicorn-ce"
+ gitlab_gitaly_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitaly"
+ gitlab_shell_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-shell"
+ gitlab_workhorse_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-workhorse-ce"
+
+ if [[ "$CI_PROJECT_NAME" == "gitlab-ee" ]]; then
+ gitlab_migrations_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-rails-ee"
+ gitlab_sidekiq_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-sidekiq-ee"
+ gitlab_unicorn_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-unicorn-ee"
+ gitlab_workhorse_image_repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-workhorse-ee"
+ fi
+
+ # canary uses stable db
+ [[ "$track" == "canary" ]] && postgres_enabled="false"
+
+ env_track=$( echo $track | tr -s '[:lower:]' '[:upper:]' )
+ env_slug=$( echo ${CI_ENVIRONMENT_SLUG//-/_} | tr -s '[:lower:]' '[:upper:]' )
+
+ if [[ "$track" == "stable" ]]; then
+ # for stable track get number of replicas from `PRODUCTION_REPLICAS`
+ eval new_replicas=\$${env_slug}_REPLICAS
+ service_enabled="true"
+ else
+ # for all tracks get number of replicas from `CANARY_PRODUCTION_REPLICAS`
+ eval new_replicas=\$${env_track}_${env_slug}_REPLICAS
+ fi
+ if [[ -n "$new_replicas" ]]; then
+ replicas="$new_replicas"
+ fi
+
+ # Cleanup and previous installs, as FAILED and PENDING_UPGRADE will cause errors with `upgrade`
+ if [ "$CI_ENVIRONMENT_SLUG" != "production" ] && previousDeployFailed ; then
+ echo "Deployment in bad state, cleaning up $CI_ENVIRONMENT_SLUG"
+ delete
+ cleanup
+ fi
+ helm repo add gitlab https://charts.gitlab.io/
+ helm dep update .
+
+HELM_CMD=$(cat << EOF
+ helm upgrade --install \
+ --wait \
+ --timeout 600 \
+ --set releaseOverride="$CI_ENVIRONMENT_SLUG" \
+ --set global.hosts.hostSuffix="$HOST_SUFFIX" \
+ --set global.hosts.domain="$REVIEW_APPS_DOMAIN" \
+ --set certmanager.install=false \
+ --set global.ingress.configureCertmanager=false \
+ --set global.ingress.tls.secretName=tls-cert \
+ --set global.ingress.annotations."external-dns\.alpha\.kubernetes\.io/ttl"="10"
+ --set gitlab.unicorn.resources.requests.cpu=200m \
+ --set gitlab.sidekiq.resources.requests.cpu=100m \
+ --set gitlab.gitlab-shell.resources.requests.cpu=100m \
+ --set redis.resources.requests.cpu=100m \
+ --set minio.resources.requests.cpu=100m \
+ --set gitlab.migrations.image.repository="$gitlab_migrations_image_repository" \
+ --set gitlab.migrations.image.tag="$CI_COMMIT_REF_NAME" \
+ --set gitlab.sidekiq.image.repository="$gitlab_sidekiq_image_repository" \
+ --set gitlab.sidekiq.image.tag="$CI_COMMIT_REF_NAME" \
+ --set gitlab.unicorn.image.repository="$gitlab_unicorn_image_repository" \
+ --set gitlab.unicorn.image.tag="$CI_COMMIT_REF_NAME" \
+ --set gitlab.gitaly.image.repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitaly" \
+ --set gitlab.gitaly.image.tag="v$GITALY_VERSION" \
+ --set gitlab.gitlab-shell.image.repository="registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-shell" \
+ --set gitlab.gitlab-shell.image.tag="v$GITLAB_SHELL_VERSION" \
+ --set gitlab.unicorn.workhorse.image="$gitlab_workhorse_image_repository" \
+ --set gitlab.unicorn.workhorse.tag="$CI_COMMIT_REF_NAME" \
+ --namespace="$KUBE_NAMESPACE" \
+ --version="$CI_PIPELINE_ID-$CI_JOB_ID" \
+ "$name" \
+ .
+EOF
+)
+
+ echo "Deploying with:"
+ echo $HELM_CMD
+
+ eval $HELM_CMD
+}
+
+function delete() {
+ track="${1-stable}"
+ name="$CI_ENVIRONMENT_SLUG"
+
+ if [[ "$track" != "stable" ]]; then
+ name="$name-$track"
+ fi
+
+ echo "Deleting release '$name'..."
+ helm delete --purge "$name" || true
+}
+
+function cleanup() {
+ echo "Cleaning up $CI_ENVIRONMENT_SLUG..."
+ kubectl -n "$KUBE_NAMESPACE" get ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa 2>&1 \
+ | grep "$CI_ENVIRONMENT_SLUG" \
+ | awk '{print $1}' \
+ | xargs kubectl -n "$KUBE_NAMESPACE" delete \
+ || true
+}
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index fbf116e533b..7202cee04ea 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -694,4 +694,38 @@ describe ApplicationController do
expect(response).to have_gitlab_http_status(403)
end
end
+
+ context 'when invalid UTF-8 parameters are received' do
+ controller(described_class) do
+ def index
+ params[:text].split(' ')
+
+ render json: :ok
+ end
+ end
+
+ before do
+ sign_in user
+ end
+
+ context 'html' do
+ it 'renders 412' do
+ get :index, text: "hi \255"
+
+ expect(response).to have_gitlab_http_status(412)
+ expect(response).to render_template :precondition_failed
+ end
+ end
+
+ context 'js' do
+ it 'renders 412' do
+ get :index, text: "hi \255", format: :js
+
+ json_response = JSON.parse(response.body)
+
+ expect(response).to have_gitlab_http_status(412)
+ expect(json_response['error']).to eq('Invalid UTF-8')
+ end
+ end
+ end
end
diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb
index 9e58280b868..2cb2a23b7be 100644
--- a/spec/features/projects/tree/create_directory_spec.rb
+++ b/spec/features/projects/tree/create_directory_spec.rb
@@ -43,7 +43,7 @@ describe 'Multi-file editor new directory', :js do
find('.js-ide-commit-mode').click
find('.multi-file-commit-list-item').hover
- first('.multi-file-discard-btn .btn').click
+ click_button 'Stage'
fill_in('commit-message', with: 'commit message ide')
diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb
index a04d3566a7e..9f5524da8e9 100644
--- a/spec/features/projects/tree/create_file_spec.rb
+++ b/spec/features/projects/tree/create_file_spec.rb
@@ -35,7 +35,7 @@ describe 'Multi-file editor new file', :js do
find('.js-ide-commit-mode').click
find('.multi-file-commit-list-item').hover
- first('.multi-file-discard-btn .btn').click
+ click_button 'Stage'
fill_in('commit-message', with: 'commit message ide')
diff --git a/spec/helpers/commits_helper_spec.rb b/spec/helpers/commits_helper_spec.rb
index 4b6c7c33e5b..9c0e55739d6 100644
--- a/spec/helpers/commits_helper_spec.rb
+++ b/spec/helpers/commits_helper_spec.rb
@@ -37,7 +37,7 @@ describe CommitsHelper do
.not_to include('onmouseover="alert(1)"')
end
- it 'escapes the commiter name' do
+ it 'escapes the committer name' do
user = build_stubbed(:user, name: 'Foo <script>alert("XSS")</script>')
commit = double(committer: user, committer_name: '', committer_email: '')
diff --git a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
index b45ae5bbb0f..bf48d7bfdad 100644
--- a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
@@ -33,10 +33,6 @@ describe('Multi-file editor commit sidebar list item', () => {
expect(vm.$el.querySelector('.multi-file-commit-list-path').textContent).toContain(f.path);
});
- it('renders actionn button', () => {
- expect(vm.$el.querySelector('.multi-file-discard-btn')).not.toBeNull();
- });
-
it('opens a closed file in the editor when clicking the file path', done => {
spyOn(vm, 'openPendingTab').and.callThrough();
spyOn(router, 'push');
diff --git a/spec/javascripts/ide/components/repo_commit_section_spec.js b/spec/javascripts/ide/components/repo_commit_section_spec.js
index d09ccd7ac34..6c726c1e154 100644
--- a/spec/javascripts/ide/components/repo_commit_section_spec.js
+++ b/spec/javascripts/ide/components/repo_commit_section_spec.js
@@ -103,65 +103,6 @@ describe('RepoCommitSection', () => {
});
});
- it('adds changed files into staged files', done => {
- vm.$el.querySelector('.multi-file-discard-btn .btn').click();
- vm
- .$nextTick()
- .then(() => vm.$el.querySelector('.multi-file-discard-btn .btn').click())
- .then(vm.$nextTick)
- .then(() => {
- expect(vm.$el.querySelector('.ide-commit-list-container').textContent).toContain(
- 'There are no unstaged changes',
- );
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('stages a single file', done => {
- vm.$el.querySelector('.multi-file-discard-btn .btn').click();
-
- Vue.nextTick(() => {
- expect(
- vm.$el
- .querySelector('.ide-commit-list-container')
- .querySelectorAll('.multi-file-commit-list > li').length,
- ).toBe(1);
-
- done();
- });
- });
-
- it('discards a single file', done => {
- vm.$el.querySelector('.multi-file-commit-list li:first-child .js-modal-primary-action').click();
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.ide-commit-list-container').textContent).not.toContain('file1');
- expect(
- vm.$el
- .querySelector('.ide-commit-list-container')
- .querySelectorAll('.multi-file-commit-list > li').length,
- ).toBe(1);
-
- done();
- });
- });
-
- it('unstages a single file', done => {
- vm.$el
- .querySelectorAll('.multi-file-discard-btn')[2]
- .querySelector('.btn')
- .click();
-
- Vue.nextTick(() => {
- expect(
- vm.$el.querySelectorAll('.ide-commit-list-container')[1].querySelectorAll('li').length,
- ).toBe(1);
-
- done();
- });
- });
-
describe('mounted', () => {
it('opens last opened file', () => {
expect(store.state.openFiles.length).toBe(1);
diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
index 6342ea00436..6ac7138743b 100644
--- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
+++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
@@ -27,6 +27,10 @@ describe('mrWidgetOptions', () => {
});
});
+ afterEach(() => {
+ vm.$destroy();
+ });
+
describe('data', () => {
it('should instantiate Store and Service', () => {
expect(vm.mr).toBeDefined();
diff --git a/spec/lib/quality/helm_client_spec.rb b/spec/lib/quality/helm_client_spec.rb
new file mode 100644
index 00000000000..553cd8719de
--- /dev/null
+++ b/spec/lib/quality/helm_client_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Quality::HelmClient do
+ let(:namespace) { 'review-apps-ee' }
+ let(:release_name) { 'my-release' }
+ let(:raw_helm_list_result) do
+ <<~OUTPUT
+ NAME REVISION UPDATED STATUS CHART NAMESPACE
+ review-improve-re-2dsd9d 1 Tue Jul 31 15:53:17 2018 FAILED gitlab-0.3.4 #{namespace}
+ review-11-1-stabl-3r2fso 1 Mon Jul 30 22:44:14 2018 FAILED gitlab-0.3.3 #{namespace}
+ review-49375-css-fk664j 1 Thu Jul 19 11:01:30 2018 FAILED gitlab-0.2.4 #{namespace}
+ OUTPUT
+ end
+
+ subject { described_class.new(namespace: namespace) }
+
+ describe '#releases' do
+ it 'calls helm list with default arguments' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(helm list --namespace "#{namespace}")])
+ .and_return(Gitlab::Popen::Result.new([], ''))
+
+ subject.releases
+ end
+
+ it 'calls helm list with given arguments' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(helm list --namespace "#{namespace}" --deployed)])
+ .and_return(Gitlab::Popen::Result.new([], ''))
+
+ subject.releases(args: ['--deployed'])
+ end
+
+ it 'returns a list of Release objects' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with([%(helm list --namespace "#{namespace}" --deployed)])
+ .and_return(Gitlab::Popen::Result.new([], raw_helm_list_result))
+
+ releases = subject.releases(args: ['--deployed'])
+
+ expect(releases.size).to eq(3)
+ expect(releases[0].name).to eq('review-improve-re-2dsd9d')
+ expect(releases[0].revision).to eq(1)
+ expect(releases[0].last_update).to eq(Time.parse('Tue Jul 31 15:53:17 2018'))
+ expect(releases[0].status).to eq('FAILED')
+ expect(releases[0].chart).to eq('gitlab-0.3.4')
+ expect(releases[0].namespace).to eq(namespace)
+ end
+ end
+
+ describe '#delete' do
+ it 'calls helm delete with default arguments' do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with(["helm delete --purge #{release_name}"])
+ .and_return(Gitlab::Popen::Result.new([], '', '', 0))
+
+ expect(subject.delete(release_name: release_name).status).to eq(0)
+ end
+ end
+end
diff --git a/spec/lib/quality/kubernetes_client_spec.rb b/spec/lib/quality/kubernetes_client_spec.rb
new file mode 100644
index 00000000000..3c0c0d0977a
--- /dev/null
+++ b/spec/lib/quality/kubernetes_client_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Quality::KubernetesClient do
+ subject { described_class.new(namespace: 'review-apps-ee') }
+
+ describe '#cleanup' do
+ it 'calls kubectl with the correct arguments' do
+ # popen_with_detail will receive an array with a bunch of arguments; we're
+ # only concerned with it having the correct namespace and release name
+ expect(Gitlab::Popen).to receive(:popen_with_detail) do |args|
+ expect(args)
+ .to satisfy_one { |arg| arg.start_with?('-n "review-apps-ee" get') }
+ expect(args)
+ .to satisfy_one { |arg| arg == 'grep "my-release"' }
+ expect(args)
+ .to satisfy_one { |arg| arg.end_with?('-n "review-apps-ee" delete') }
+ end
+
+ # We're not verifying the output here, just silencing it
+ expect { subject.cleanup(release_name: 'my-release') }.to output.to_stdout
+ end
+ end
+end
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index a6957095166..ed41ff7a0fa 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -270,11 +270,11 @@ eos
let(:issue) { create :issue, project: project }
let(:other_project) { create(:project, :public) }
let(:other_issue) { create :issue, project: other_project }
- let(:commiter) { create :user }
+ let(:committer) { create :user }
before do
- project.add_developer(commiter)
- other_project.add_developer(commiter)
+ project.add_developer(committer)
+ other_project.add_developer(committer)
end
it 'detects issues that this commit is marked as closing' do
@@ -282,7 +282,7 @@ eos
allow(commit).to receive_messages(
safe_message: "Fixes ##{issue.iid} and #{ext_ref}",
- committer_email: commiter.email
+ committer_email: committer.email
)
expect(commit.closes_issues).to include(issue)
diff --git a/spec/services/merge_requests/rebase_service_spec.rb b/spec/services/merge_requests/rebase_service_spec.rb
index 2703da7ae44..427a2d63a88 100644
--- a/spec/services/merge_requests/rebase_service_spec.rb
+++ b/spec/services/merge_requests/rebase_service_spec.rb
@@ -87,7 +87,7 @@ describe MergeRequests::RebaseService do
expect(merge_request.reload.rebase_commit_sha).to eq(head_sha)
end
- it 'logs correct author and commiter' do
+ it 'logs correct author and committer' do
head_commit = merge_request.source_project.repository.commit(merge_request.source_branch)
expect(head_commit.author_email).to eq('dmitriy.zaporozhets@gmail.com')