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/ide/components/ide_side_bar.vue6
-rw-r--r--app/assets/javascripts/ide/components/ide_tree_list.vue6
-rw-r--r--app/assets/javascripts/ide/components/repo_loading_file.vue42
-rw-r--r--app/assets/javascripts/notes.js6
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue6
-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/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue24
-rw-r--r--app/assets/javascripts/vue_shared/components/skeleton_loading_container.vue37
-rw-r--r--app/controllers/application_controller.rb1
-rw-r--r--app/controllers/concerns/invalid_utf8_error_handler.rb25
-rw-r--r--app/controllers/dashboard/application_controller.rb2
-rw-r--r--app/controllers/dashboard/groups_controller.rb2
-rw-r--r--app/controllers/dashboard/labels_controller.rb2
-rw-r--r--app/controllers/dashboard/milestones_controller.rb2
-rw-r--r--app/controllers/dashboard/projects_controller.rb2
-rw-r--r--app/controllers/dashboard/snippets_controller.rb2
-rw-r--r--app/controllers/dashboard/todos_controller.rb2
-rw-r--r--app/controllers/explore/application_controller.rb2
-rw-r--r--app/controllers/explore/groups_controller.rb2
-rw-r--r--app/controllers/explore/projects_controller.rb2
-rw-r--r--app/controllers/explore/snippets_controller.rb2
-rw-r--r--app/controllers/google_api/authorizations_controller.rb2
-rw-r--r--app/controllers/groups/application_controller.rb2
-rw-r--r--app/controllers/groups/avatars_controller.rb2
-rw-r--r--app/controllers/groups/boards_controller.rb2
-rw-r--r--app/controllers/groups/children_controller.rb2
-rw-r--r--app/controllers/groups/group_members_controller.rb2
-rw-r--r--app/controllers/groups/labels_controller.rb2
-rw-r--r--app/controllers/groups/milestones_controller.rb2
-rw-r--r--app/controllers/groups/runners_controller.rb2
-rw-r--r--app/controllers/groups/settings/ci_cd_controller.rb2
-rw-r--r--app/controllers/groups/shared_projects_controller.rb2
-rw-r--r--app/controllers/groups/uploads_controller.rb2
-rw-r--r--app/controllers/groups/variables_controller.rb2
-rw-r--r--app/controllers/import/base_controller.rb2
-rw-r--r--app/controllers/import/bitbucket_controller.rb2
-rw-r--r--app/controllers/import/fogbugz_controller.rb2
-rw-r--r--app/controllers/import/gitea_controller.rb2
-rw-r--r--app/controllers/import/github_controller.rb2
-rw-r--r--app/controllers/import/gitlab_controller.rb2
-rw-r--r--app/controllers/import/gitlab_projects_controller.rb2
-rw-r--r--app/controllers/import/google_code_controller.rb2
-rw-r--r--app/controllers/import/manifest_controller.rb2
-rw-r--r--app/controllers/ldap/omniauth_callbacks_controller.rb2
-rw-r--r--app/controllers/oauth/applications_controller.rb2
-rw-r--r--app/controllers/oauth/authorizations_controller.rb2
-rw-r--r--app/controllers/oauth/authorized_applications_controller.rb2
-rw-r--r--app/controllers/profiles/accounts_controller.rb2
-rw-r--r--app/controllers/profiles/active_sessions_controller.rb2
-rw-r--r--app/controllers/profiles/application_controller.rb2
-rw-r--r--app/controllers/profiles/avatars_controller.rb2
-rw-r--r--app/controllers/profiles/chat_names_controller.rb2
-rw-r--r--app/controllers/profiles/emails_controller.rb2
-rw-r--r--app/controllers/profiles/gpg_keys_controller.rb2
-rw-r--r--app/controllers/profiles/keys_controller.rb2
-rw-r--r--app/controllers/profiles/notifications_controller.rb2
-rw-r--r--app/controllers/profiles/passwords_controller.rb2
-rw-r--r--app/controllers/profiles/personal_access_tokens_controller.rb2
-rw-r--r--app/controllers/profiles/preferences_controller.rb2
-rw-r--r--app/controllers/profiles/two_factor_auths_controller.rb4
-rw-r--r--app/controllers/profiles/u2f_registrations_controller.rb2
-rw-r--r--app/models/project.rb7
-rw-r--r--app/models/project_feature.rb26
-rw-r--r--app/models/site_statistic.rb2
-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/frozen-string-app-controller-more.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--db/post_migrate/20180917172041_remove_wikis_count_from_site_statistics.rb6
-rw-r--r--db/schema.rb3
-rw-r--r--doc/raketasks/backup_restore.md2
-rw-r--r--doc/user/project/repository/index.md4
-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--lib/tasks/gitlab/site_statistics.rake8
-rw-r--r--package.json2
-rw-r--r--qa/qa/factory/resource/fork.rb37
-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/page/main/sign_up.rb2
-rw-r--r--qa/qa/page/menu/main.rb9
-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/factories/site_statistics.rb1
-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/ide/components/repo_loading_file_spec.js63
-rw-r--r--spec/javascripts/vue_mr_widget/mr_widget_options_spec.js4
-rw-r--r--spec/javascripts/vue_shared/components/skeleton_loading_container_spec.js49
-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/models/project_feature_spec.rb42
-rw-r--r--spec/models/site_statistic_spec.rb2
-rw-r--r--spec/services/groups/destroy_service_spec.rb8
-rw-r--r--spec/services/merge_requests/rebase_service_spec.rb2
-rw-r--r--spec/tasks/gitlab/site_statistics_rake_spec.rb5
-rw-r--r--yarn.lock6
115 files changed, 930 insertions, 439 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/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue
index 4771c58a11d..5620d6a6244 100644
--- a/app/assets/javascripts/ide/components/ide_side_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_side_bar.vue
@@ -1,6 +1,6 @@
<script>
import { mapState, mapGetters } from 'vuex';
-import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
+import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading';
import IdeTree from './ide_tree.vue';
import ResizablePanel from './resizable_panel.vue';
import ActivityBar from './activity_bar.vue';
@@ -13,7 +13,7 @@ import { activityBarViews } from '../constants';
export default {
components: {
- SkeletonLoadingContainer,
+ SkeletonLoading,
ResizablePanel,
ActivityBar,
CommitSection,
@@ -56,7 +56,7 @@ export default {
:key="n"
class="multi-file-loading-container"
>
- <skeleton-loading-container />
+ <skeleton-loading />
</div>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue
index e658d1bf956..ff53314d275 100644
--- a/app/assets/javascripts/ide/components/ide_tree_list.vue
+++ b/app/assets/javascripts/ide/components/ide_tree_list.vue
@@ -1,7 +1,7 @@
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue';
-import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
+import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading';
import FileRow from '~/vue_shared/components/file_row.vue';
import NavDropdown from './nav_dropdown.vue';
import FileRowExtra from './file_row_extra.vue';
@@ -9,7 +9,7 @@ import FileRowExtra from './file_row_extra.vue';
export default {
components: {
Icon,
- SkeletonLoadingContainer,
+ SkeletonLoading,
NavDropdown,
FileRow,
},
@@ -51,7 +51,7 @@ export default {
:key="n"
class="multi-file-loading-container"
>
- <skeleton-loading-container />
+ <skeleton-loading />
</div>
</template>
<template v-else>
diff --git a/app/assets/javascripts/ide/components/repo_loading_file.vue b/app/assets/javascripts/ide/components/repo_loading_file.vue
deleted file mode 100644
index 7a5ede82253..00000000000
--- a/app/assets/javascripts/ide/components/repo_loading_file.vue
+++ /dev/null
@@ -1,42 +0,0 @@
-<script>
- import { mapState } from 'vuex';
- import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-
- export default {
- components: {
- skeletonLoadingContainer,
- },
- computed: {
- ...mapState([
- 'leftPanelCollapsed',
- ]),
- },
- };
-</script>
-
-<template>
- <tr
- class="loading-file"
- aria-label="Loading files"
- >
- <td class="multi-file-table-col-name">
- <skeleton-loading-container
- :small="true"
- />
- </td>
- <template v-if="!leftPanelCollapsed">
- <td class="d-none d-sm-none d-md-block">
- <skeleton-loading-container
- :small="true"
- />
- </td>
-
- <td class="d-none d-sm-block">
- <skeleton-loading-container
- :small="true"
- class="animation-container-right"
- />
- </td>
- </template>
- </tr>
-</template>
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 0c966e0808a..e2f485e37eb 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -16,7 +16,7 @@ import 'vendor/jquery.atwho';
import AjaxCache from '~/lib/utils/ajax_cache';
import Vue from 'vue';
import syntaxHighlight from '~/syntax_highlight';
-import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
+import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading';
import axios from './lib/utils/axios_utils';
import { getLocationHash } from './lib/utils/url_utility';
import Flash from './flash';
@@ -1293,10 +1293,10 @@ export default class Notes {
new Vue({
el,
components: {
- SkeletonLoadingContainer,
+ SkeletonLoading,
},
render(createElement) {
- return createElement('skeleton-loading-container');
+ return createElement('skeleton-loading');
},
});
}
diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue
index 802be022ba6..d9161210fe6 100644
--- a/app/assets/javascripts/notes/components/diff_with_note.vue
+++ b/app/assets/javascripts/notes/components/diff_with_note.vue
@@ -3,13 +3,13 @@ import { mapState, mapActions } from 'vuex';
import imageDiffHelper from '~/image_diff/helpers/index';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import DiffFileHeader from '~/diffs/components/diff_file_header.vue';
-import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
+import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading';
import { trimFirstCharOfLineContent } from '~/diffs/store/utils';
export default {
components: {
DiffFileHeader,
- SkeletonLoadingContainer,
+ SkeletonLoading,
},
props: {
discussion: {
@@ -142,7 +142,7 @@ export default {
class="line_content js-success-lazy-load"
>
<span></span>
- <skeleton-loading-container />
+ <skeleton-loading />
<span></span>
</td>
</tr>
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/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
index a10deb93f0f..27689a55b67 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
@@ -2,14 +2,14 @@
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import $ from 'jquery';
-import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
+import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading';
const { CancelToken } = axios;
let axiosSource;
export default {
components: {
- SkeletonLoadingContainer,
+ SkeletonLoading,
},
props: {
content: {
@@ -81,7 +81,7 @@ export default {
<div
ref="markdown-preview"
class="md md-previewer">
- <skeleton-loading-container v-if="isLoading" />
+ <skeleton-loading v-if="isLoading" />
<div
v-else
v-html="previewContent">
diff --git a/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue b/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue
index 2eb6c20b2c0..f11a7699f27 100644
--- a/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/skeleton_note.vue
@@ -1,3 +1,14 @@
+<script>
+import SkeletonLoading from '@gitlab-org/gitlab-ui/dist/components/base/skeleton_loading';
+
+export default {
+ name: 'SkeletonNote',
+ components: {
+ SkeletonLoading,
+ },
+};
+</script>
+
<template>
<li class="timeline-entry note">
<div class="timeline-entry-inner">
@@ -6,20 +17,9 @@
<div class="timeline-content">
<div class="note-header"></div>
<div class="note-body">
- <skeleton-loading-container />
+ <skeleton-loading />
</div>
</div>
</div>
</li>
</template>
-
-<script>
-import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-
-export default {
- name: 'SkeletonNote',
- components: {
- skeletonLoadingContainer,
- },
-};
-</script>
diff --git a/app/assets/javascripts/vue_shared/components/skeleton_loading_container.vue b/app/assets/javascripts/vue_shared/components/skeleton_loading_container.vue
deleted file mode 100644
index 4a5ffbe5d5a..00000000000
--- a/app/assets/javascripts/vue_shared/components/skeleton_loading_container.vue
+++ /dev/null
@@ -1,37 +0,0 @@
-<script>
- export default {
- props: {
- small: {
- type: Boolean,
- required: false,
- default: false,
- },
- lines: {
- type: Number,
- required: false,
- default: 3,
- },
- },
- computed: {
- lineClasses() {
- return new Array(this.lines).fill().map((_, i) => `skeleton-line-${i + 1}`);
- },
- },
- };
-</script>
-
-<template>
- <div
- :class="{
- 'animation-container-small': small,
- }"
- class="animation-container"
- >
- <div
- v-for="(css, index) in lineClasses"
- :key="index"
- :class="css"
- >
- </div>
- </div>
-</template>
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/controllers/dashboard/application_controller.rb b/app/controllers/dashboard/application_controller.rb
index 9fb5c525425..cee0753a021 100644
--- a/app/controllers/dashboard/application_controller.rb
+++ b/app/controllers/dashboard/application_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Dashboard::ApplicationController < ApplicationController
include ControllerWithCrossProjectAccessCheck
diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb
index 79f563bef86..f82cde8e10a 100644
--- a/app/controllers/dashboard/groups_controller.rb
+++ b/app/controllers/dashboard/groups_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Dashboard::GroupsController < Dashboard::ApplicationController
include GroupTree
diff --git a/app/controllers/dashboard/labels_controller.rb b/app/controllers/dashboard/labels_controller.rb
index 9dcb3a0eb6d..89d87c2d5c8 100644
--- a/app/controllers/dashboard/labels_controller.rb
+++ b/app/controllers/dashboard/labels_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Dashboard::LabelsController < Dashboard::ApplicationController
def index
respond_to do |format|
diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb
index 78f7f6d4e23..6e17bc212e4 100644
--- a/app/controllers/dashboard/milestones_controller.rb
+++ b/app/controllers/dashboard/milestones_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Dashboard::MilestonesController < Dashboard::ApplicationController
include MilestoneActions
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index e8f796f17ca..e9686ed8d06 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Dashboard::ProjectsController < Dashboard::ApplicationController
include ParamsBackwardCompatibility
include RendersMemberAccess
diff --git a/app/controllers/dashboard/snippets_controller.rb b/app/controllers/dashboard/snippets_controller.rb
index 0ba97e4fd59..161c22046f9 100644
--- a/app/controllers/dashboard/snippets_controller.rb
+++ b/app/controllers/dashboard/snippets_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Dashboard::SnippetsController < Dashboard::ApplicationController
skip_cross_project_access_check :index
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index 231a23427f7..b82caf30a91 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Dashboard::TodosController < Dashboard::ApplicationController
include ActionView::Helpers::NumberHelper
diff --git a/app/controllers/explore/application_controller.rb b/app/controllers/explore/application_controller.rb
index baf54520b9c..8eee3742d89 100644
--- a/app/controllers/explore/application_controller.rb
+++ b/app/controllers/explore/application_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Explore::ApplicationController < ApplicationController
skip_before_action :authenticate_user!
diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb
index fa0a0f68fbc..67db797b80a 100644
--- a/app/controllers/explore/groups_controller.rb
+++ b/app/controllers/explore/groups_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Explore::GroupsController < Explore::ApplicationController
include GroupTree
diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb
index 03a2ee07fea..7ecbc32cf4e 100644
--- a/app/controllers/explore/projects_controller.rb
+++ b/app/controllers/explore/projects_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Explore::ProjectsController < Explore::ApplicationController
include ParamsBackwardCompatibility
include RendersMemberAccess
diff --git a/app/controllers/explore/snippets_controller.rb b/app/controllers/explore/snippets_controller.rb
index d3f0e033068..76ed142c939 100644
--- a/app/controllers/explore/snippets_controller.rb
+++ b/app/controllers/explore/snippets_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Explore::SnippetsController < Explore::ApplicationController
def index
@snippets = SnippetsFinder.new(current_user).execute
diff --git a/app/controllers/google_api/authorizations_controller.rb b/app/controllers/google_api/authorizations_controller.rb
index 5551057ff55..dd9f5af61b3 100644
--- a/app/controllers/google_api/authorizations_controller.rb
+++ b/app/controllers/google_api/authorizations_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module GoogleApi
class AuthorizationsController < ApplicationController
def callback
diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb
index 62213561898..5f92333c2c3 100644
--- a/app/controllers/groups/application_controller.rb
+++ b/app/controllers/groups/application_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Groups::ApplicationController < ApplicationController
include RoutableActions
include ControllerWithCrossProjectAccessCheck
diff --git a/app/controllers/groups/avatars_controller.rb b/app/controllers/groups/avatars_controller.rb
index 35a61b359c8..8e4dc2bb6e9 100644
--- a/app/controllers/groups/avatars_controller.rb
+++ b/app/controllers/groups/avatars_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Groups::AvatarsController < Groups::ApplicationController
before_action :authorize_admin_group!
diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb
index e892d1f8dbf..8d259b4052e 100644
--- a/app/controllers/groups/boards_controller.rb
+++ b/app/controllers/groups/boards_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Groups::BoardsController < Groups::ApplicationController
include BoardsResponses
diff --git a/app/controllers/groups/children_controller.rb b/app/controllers/groups/children_controller.rb
index 0e8125d6113..d549f793ad7 100644
--- a/app/controllers/groups/children_controller.rb
+++ b/app/controllers/groups/children_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class ChildrenController < Groups::ApplicationController
before_action :group
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index 7dc51f4c357..0bc082246a1 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Groups::GroupMembersController < Groups::ApplicationController
include MembershipActions
include MembersPresentation
diff --git a/app/controllers/groups/labels_controller.rb b/app/controllers/groups/labels_controller.rb
index ae31313db64..cb9ab35de85 100644
--- a/app/controllers/groups/labels_controller.rb
+++ b/app/controllers/groups/labels_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Groups::LabelsController < Groups::ApplicationController
include ToggleSubscriptionAction
diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb
index 6bdc0f79ef2..a7cee426cf1 100644
--- a/app/controllers/groups/milestones_controller.rb
+++ b/app/controllers/groups/milestones_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Groups::MilestonesController < Groups::ApplicationController
include MilestoneActions
diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb
index 1036b4e6ed3..dd8fbf7a029 100644
--- a/app/controllers/groups/runners_controller.rb
+++ b/app/controllers/groups/runners_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Groups::RunnersController < Groups::ApplicationController
# Proper policies should be implemented per
# https://gitlab.com/gitlab-org/gitlab-ce/issues/45894
diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb
index 4bf6a2a3ad1..6d9a225b771 100644
--- a/app/controllers/groups/settings/ci_cd_controller.rb
+++ b/app/controllers/groups/settings/ci_cd_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
module Settings
class CiCdController < Groups::ApplicationController
diff --git a/app/controllers/groups/shared_projects_controller.rb b/app/controllers/groups/shared_projects_controller.rb
index 7dec1f5f402..30b7bfc70ae 100644
--- a/app/controllers/groups/shared_projects_controller.rb
+++ b/app/controllers/groups/shared_projects_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class SharedProjectsController < Groups::ApplicationController
respond_to :json
diff --git a/app/controllers/groups/uploads_controller.rb b/app/controllers/groups/uploads_controller.rb
index 74760194a1f..7e5cdae0ce3 100644
--- a/app/controllers/groups/uploads_controller.rb
+++ b/app/controllers/groups/uploads_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Groups::UploadsController < Groups::ApplicationController
include UploadsActions
include WorkhorseRequest
diff --git a/app/controllers/groups/variables_controller.rb b/app/controllers/groups/variables_controller.rb
index 4d8a20de017..4f641de0357 100644
--- a/app/controllers/groups/variables_controller.rb
+++ b/app/controllers/groups/variables_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Groups
class VariablesController < Groups::ApplicationController
before_action :authorize_admin_build!
diff --git a/app/controllers/import/base_controller.rb b/app/controllers/import/base_controller.rb
index 14b8c6e4137..042b6b1264f 100644
--- a/app/controllers/import/base_controller.rb
+++ b/app/controllers/import/base_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Import::BaseController < ApplicationController
private
diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb
index f885e04b198..1b30b4dda36 100644
--- a/app/controllers/import/bitbucket_controller.rb
+++ b/app/controllers/import/bitbucket_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Import::BitbucketController < Import::BaseController
before_action :verify_bitbucket_import_enabled
before_action :bitbucket_auth, except: :callback
diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb
index df96d181153..5a439e6de78 100644
--- a/app/controllers/import/fogbugz_controller.rb
+++ b/app/controllers/import/fogbugz_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Import::FogbugzController < Import::BaseController
before_action :verify_fogbugz_import_enabled
before_action :user_map, only: [:new_user_map, :create_user_map]
diff --git a/app/controllers/import/gitea_controller.rb b/app/controllers/import/gitea_controller.rb
index fbd851c64a7..382c684a408 100644
--- a/app/controllers/import/gitea_controller.rb
+++ b/app/controllers/import/gitea_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Import::GiteaController < Import::GithubController
def new
if session[access_token_key].present? && session[host_key].present?
diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb
index f8b43b4fde5..1dfa814cdd5 100644
--- a/app/controllers/import/github_controller.rb
+++ b/app/controllers/import/github_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Import::GithubController < Import::BaseController
before_action :verify_import_enabled
before_action :provider_auth, only: [:status, :jobs, :create]
diff --git a/app/controllers/import/gitlab_controller.rb b/app/controllers/import/gitlab_controller.rb
index bdc402b0a77..498de0b07b8 100644
--- a/app/controllers/import/gitlab_controller.rb
+++ b/app/controllers/import/gitlab_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Import::GitlabController < Import::BaseController
MAX_PROJECT_PAGES = 15
PER_PAGE_PROJECTS = 100
diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb
index f21c38a4c27..354fba5d204 100644
--- a/app/controllers/import/gitlab_projects_controller.rb
+++ b/app/controllers/import/gitlab_projects_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Import::GitlabProjectsController < Import::BaseController
before_action :whitelist_query_limiting, only: [:create]
before_action :verify_gitlab_project_import_enabled
diff --git a/app/controllers/import/google_code_controller.rb b/app/controllers/import/google_code_controller.rb
index e9387d0ad14..331f06c3dd6 100644
--- a/app/controllers/import/google_code_controller.rb
+++ b/app/controllers/import/google_code_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Import::GoogleCodeController < Import::BaseController
before_action :verify_google_code_import_enabled
before_action :user_map, only: [:new_user_map, :create_user_map]
diff --git a/app/controllers/import/manifest_controller.rb b/app/controllers/import/manifest_controller.rb
index 4ed9dca2475..320cd45b925 100644
--- a/app/controllers/import/manifest_controller.rb
+++ b/app/controllers/import/manifest_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Import::ManifestController < Import::BaseController
before_action :whitelist_query_limiting, only: [:create]
before_action :verify_import_enabled
diff --git a/app/controllers/ldap/omniauth_callbacks_controller.rb b/app/controllers/ldap/omniauth_callbacks_controller.rb
index fb24edb8602..5e872804448 100644
--- a/app/controllers/ldap/omniauth_callbacks_controller.rb
+++ b/app/controllers/ldap/omniauth_callbacks_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Ldap::OmniauthCallbacksController < OmniauthCallbacksController
extend ::Gitlab::Utils::Override
diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb
index 9e700f648f4..b50f140dc80 100644
--- a/app/controllers/oauth/applications_controller.rb
+++ b/app/controllers/oauth/applications_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
include Gitlab::GonHelper
include Gitlab::Allowable
diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb
index 05190103767..894a6a431e3 100644
--- a/app/controllers/oauth/authorizations_controller.rb
+++ b/app/controllers/oauth/authorizations_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
layout 'profile'
diff --git a/app/controllers/oauth/authorized_applications_controller.rb b/app/controllers/oauth/authorized_applications_controller.rb
index 656107d2b26..a59ade559b3 100644
--- a/app/controllers/oauth/authorized_applications_controller.rb
+++ b/app/controllers/oauth/authorized_applications_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController
include PageLayoutHelper
diff --git a/app/controllers/profiles/accounts_controller.rb b/app/controllers/profiles/accounts_controller.rb
index fd9cb9fca3e..cb3180f4196 100644
--- a/app/controllers/profiles/accounts_controller.rb
+++ b/app/controllers/profiles/accounts_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::AccountsController < Profiles::ApplicationController
include AuthHelper
diff --git a/app/controllers/profiles/active_sessions_controller.rb b/app/controllers/profiles/active_sessions_controller.rb
index f1e77d68acd..efe7ede5efa 100644
--- a/app/controllers/profiles/active_sessions_controller.rb
+++ b/app/controllers/profiles/active_sessions_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::ActiveSessionsController < Profiles::ApplicationController
def index
@sessions = ActiveSession.list(current_user)
diff --git a/app/controllers/profiles/application_controller.rb b/app/controllers/profiles/application_controller.rb
index c8be288b9a0..52b046ef64f 100644
--- a/app/controllers/profiles/application_controller.rb
+++ b/app/controllers/profiles/application_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::ApplicationController < ApplicationController
layout 'profile'
end
diff --git a/app/controllers/profiles/avatars_controller.rb b/app/controllers/profiles/avatars_controller.rb
index 4f030ded80f..3378a09628c 100644
--- a/app/controllers/profiles/avatars_controller.rb
+++ b/app/controllers/profiles/avatars_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::AvatarsController < Profiles::ApplicationController
def destroy
@user = current_user
diff --git a/app/controllers/profiles/chat_names_controller.rb b/app/controllers/profiles/chat_names_controller.rb
index a186c5f36a8..2e78b9e6dc7 100644
--- a/app/controllers/profiles/chat_names_controller.rb
+++ b/app/controllers/profiles/chat_names_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::ChatNamesController < Profiles::ApplicationController
before_action :chat_name_token, only: [:new]
before_action :chat_name_params, only: [:new, :create, :deny]
diff --git a/app/controllers/profiles/emails_controller.rb b/app/controllers/profiles/emails_controller.rb
index a39824ec9c8..503eda250b4 100644
--- a/app/controllers/profiles/emails_controller.rb
+++ b/app/controllers/profiles/emails_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::EmailsController < Profiles::ApplicationController
before_action :find_email, only: [:destroy, :resend_confirmation_instructions]
diff --git a/app/controllers/profiles/gpg_keys_controller.rb b/app/controllers/profiles/gpg_keys_controller.rb
index c32507756e8..8c34a66c374 100644
--- a/app/controllers/profiles/gpg_keys_controller.rb
+++ b/app/controllers/profiles/gpg_keys_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::GpgKeysController < Profiles::ApplicationController
before_action :set_gpg_key, only: [:destroy, :revoke]
diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb
index 6035258667e..01801c31327 100644
--- a/app/controllers/profiles/keys_controller.rb
+++ b/app/controllers/profiles/keys_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::KeysController < Profiles::ApplicationController
skip_before_action :authenticate_user!, only: [:get_keys]
diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb
index 00bd2040b9a..b719b70c56e 100644
--- a/app/controllers/profiles/notifications_controller.rb
+++ b/app/controllers/profiles/notifications_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::NotificationsController < Profiles::ApplicationController
# rubocop: disable CodeReuse/ActiveRecord
def show
diff --git a/app/controllers/profiles/passwords_controller.rb b/app/controllers/profiles/passwords_controller.rb
index b8ccc6e3c99..a0391d677c4 100644
--- a/app/controllers/profiles/passwords_controller.rb
+++ b/app/controllers/profiles/passwords_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::PasswordsController < Profiles::ApplicationController
skip_before_action :check_password_expiration, only: [:new, :create]
skip_before_action :check_two_factor_requirement, only: [:new, :create]
diff --git a/app/controllers/profiles/personal_access_tokens_controller.rb b/app/controllers/profiles/personal_access_tokens_controller.rb
index b357741e3fb..4b6ec2697b7 100644
--- a/app/controllers/profiles/personal_access_tokens_controller.rb
+++ b/app/controllers/profiles/personal_access_tokens_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
def index
set_index_vars
diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb
index ed0f98179eb..37ac11dc6a1 100644
--- a/app/controllers/profiles/preferences_controller.rb
+++ b/app/controllers/profiles/preferences_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::PreferencesController < Profiles::ApplicationController
before_action :user
diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb
index 29ff18a1219..ba94196b2f9 100644
--- a/app/controllers/profiles/two_factor_auths_controller.rb
+++ b/app/controllers/profiles/two_factor_auths_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
skip_before_action :check_two_factor_requirement
@@ -30,7 +32,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
unless two_factor_grace_period_expired?
grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours
- flash.now[:alert] << " You need to do this before #{l(grace_period_deadline)}."
+ flash.now[:alert] = flash.now[:alert] + " You need to do this before #{l(grace_period_deadline)}."
end
end
diff --git a/app/controllers/profiles/u2f_registrations_controller.rb b/app/controllers/profiles/u2f_registrations_controller.rb
index e3d7737f44a..e6a154fb6aa 100644
--- a/app/controllers/profiles/u2f_registrations_controller.rb
+++ b/app/controllers/profiles/u2f_registrations_controller.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Profiles::U2fRegistrationsController < Profiles::ApplicationController
def destroy
u2f_registration = current_user.u2f_registrations.find(params[:id])
diff --git a/app/models/project.rb b/app/models/project.rb
index 2b2dff3a718..1156a400a21 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -86,7 +86,7 @@ class Project < ActiveRecord::Base
after_create :create_project_feature, unless: :project_feature
after_create -> { SiteStatistic.track(STATISTICS_ATTRIBUTE) }
- before_destroy :untrack_site_statistics
+ before_destroy -> { SiteStatistic.untrack(STATISTICS_ATTRIBUTE) }
after_create :create_ci_cd_settings,
unless: :ci_cd_settings,
@@ -2110,11 +2110,6 @@ class Project < ActiveRecord::Base
Gitlab::PagesTransfer.new.rename_project(path_before, self.path, namespace.full_path)
end
- def untrack_site_statistics
- SiteStatistic.untrack(STATISTICS_ATTRIBUTE)
- self.project_feature.untrack_statistics_for_deletion!
- end
-
# rubocop: disable CodeReuse/ServiceClass
def execute_rename_repository_hooks!(full_path_before)
# When we import a project overwriting the original project, there
diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb
index d74cb2506ba..4a0324e8b5c 100644
--- a/app/models/project_feature.rb
+++ b/app/models/project_feature.rb
@@ -21,7 +21,6 @@ class ProjectFeature < ActiveRecord::Base
ENABLED = 20
FEATURES = %i(issues merge_requests wiki snippets builds repository).freeze
- STATISTICS_ATTRIBUTE = 'wikis_count'.freeze
class << self
def access_level_attribute(feature)
@@ -55,9 +54,6 @@ class ProjectFeature < ActiveRecord::Base
default_value_for :wiki_access_level, value: ENABLED, allows_nil: false
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
- after_create ->(model) { SiteStatistic.track(STATISTICS_ATTRIBUTE) if model.wiki_enabled? }
- after_update :update_site_statistics
-
def feature_available?(feature, user)
get_permission(user, access_level(feature))
end
@@ -82,30 +78,8 @@ class ProjectFeature < ActiveRecord::Base
issues_access_level > DISABLED
end
- # This is a workaround for the removal hooks not been triggered when removing a Project.
- #
- # ProjectFeature is removed using database cascade index rule.
- # This method is called by Project model when deletion starts.
- def untrack_statistics_for_deletion!
- return unless wiki_enabled?
-
- SiteStatistic.untrack(STATISTICS_ATTRIBUTE)
- end
-
private
- def update_site_statistics
- return unless wiki_access_level_changed?
-
- if self.wiki_access_level_was == DISABLED
- # possible new states are PRIVATE / ENABLED, both should be tracked
- SiteStatistic.track(STATISTICS_ATTRIBUTE)
- elsif self.wiki_access_level == DISABLED
- # old state was either PRIVATE / ENABLED, only untrack if new state is DISABLED
- SiteStatistic.untrack(STATISTICS_ATTRIBUTE)
- end
- end
-
# Validates builds and merge requests access level
# which cannot be higher than repository access level
def repository_children_level
diff --git a/app/models/site_statistic.rb b/app/models/site_statistic.rb
index 48324570f0b..3a7912ed53a 100644
--- a/app/models/site_statistic.rb
+++ b/app/models/site_statistic.rb
@@ -4,7 +4,7 @@ class SiteStatistic < ActiveRecord::Base
# prevents the creation of multiple rows
default_value_for :id, 1
- COUNTER_ATTRIBUTES = %w(repositories_count wikis_count).freeze
+ COUNTER_ATTRIBUTES = %w(repositories_count).freeze
REQUIRED_SCHEMA_VERSION = 20180629153018
# Tracks specific attribute
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/frozen-string-app-controller-more.yml b/changelogs/unreleased/frozen-string-app-controller-more.yml
new file mode 100644
index 00000000000..ea2c81e7afc
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-app-controller-more.yml
@@ -0,0 +1,5 @@
+---
+title: Enable more frozen string in app/controllers/
+merge_request:
+author: gfyoung
+type: performance
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/db/post_migrate/20180917172041_remove_wikis_count_from_site_statistics.rb b/db/post_migrate/20180917172041_remove_wikis_count_from_site_statistics.rb
new file mode 100644
index 00000000000..0a39abe3bdf
--- /dev/null
+++ b/db/post_migrate/20180917172041_remove_wikis_count_from_site_statistics.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+class RemoveWikisCountFromSiteStatistics < ActiveRecord::Migration
+ def change
+ remove_column :site_statistics, :wikis_count, :integer
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 16e3c44513b..f92d8005dfb 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20180914201132) do
+ActiveRecord::Schema.define(version: 20180917172041) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -1908,7 +1908,6 @@ ActiveRecord::Schema.define(version: 20180914201132) do
create_table "site_statistics", force: :cascade do |t|
t.integer "repositories_count", default: 0, null: false
- t.integer "wikis_count", default: 0, null: false
end
create_table "snippets", force: :cascade do |t|
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/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index 1c3915a5fdd..4d016277824 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -158,7 +158,9 @@ Find it under your project's **Repository > Graph**.
## Repository Languages
For the default branch of each repository, GitLab will determine what programming languages
-were used and display this on the projects pages.
+were used and display this on the projects pages. If this information is missing, it will
+be added after updating the default branch on the project. This process can take up to 5
+minutes.
![Repository Languages bar](img/repository_languages.png)
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/lib/tasks/gitlab/site_statistics.rake b/lib/tasks/gitlab/site_statistics.rake
index 7d24ec72a9d..d97f11b2ed5 100644
--- a/lib/tasks/gitlab/site_statistics.rake
+++ b/lib/tasks/gitlab/site_statistics.rake
@@ -10,14 +10,6 @@ namespace :gitlab do
SiteStatistic.update_all('repositories_count = (SELECT COUNT(*) FROM projects)')
end
puts 'OK!'.color(:green)
-
- print '* Wikis... '
- SiteStatistic.transaction do
- # see https://gitlab.com/gitlab-org/gitlab-ce/issues/48967
- ActiveRecord::Base.connection.execute('SET LOCAL statement_timeout TO 0') if Gitlab::Database.postgresql?
- SiteStatistic.update_all('wikis_count = (SELECT COUNT(*) FROM project_features WHERE wiki_access_level != 0)')
- end
- puts 'OK!'.color(:green)
puts
end
end
diff --git a/package.json b/package.json
index 0059934bdfe..94707fe3587 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,7 @@
},
"dependencies": {
"@gitlab-org/gitlab-svgs": "^1.29.0",
- "@gitlab-org/gitlab-ui": "^1.5.1",
+ "@gitlab-org/gitlab-ui": "^1.7.0",
"autosize": "^4.0.0",
"axios": "^0.17.1",
"babel-core": "^6.26.3",
diff --git a/qa/qa/factory/resource/fork.rb b/qa/qa/factory/resource/fork.rb
index 01969c31438..92050eaba2a 100644
--- a/qa/qa/factory/resource/fork.rb
+++ b/qa/qa/factory/resource/fork.rb
@@ -13,8 +13,43 @@ module QA
product(:user) { |factory| factory.user }
+ def visit_project_with_retry
+ # The user intermittently fails to stay signed in after visiting the
+ # project page. The new user is registered and then signs in and a
+ # screenshot shows that signing in was successful. Then the project
+ # page is visited but a screenshot shows the user is no longer signed
+ # in. It's difficult to reproduce locally but GDK logs don't seem to
+ # show anything unexpected. This method attempts to work around the
+ # problem and capture data to help troubleshoot.
+
+ Capybara::Screenshot.screenshot_and_save_page
+
+ start = Time.now
+
+ while Time.now - start < 20
+ push.project.visit!
+
+ puts "Visited project page"
+ Capybara::Screenshot.screenshot_and_save_page
+
+ return if Page::Menu::Main.act { has_personal_area?(wait: 0) }
+
+ puts "Not signed in. Attempting to sign in again."
+ Capybara::Screenshot.screenshot_and_save_page
+
+ Page::Menu::Main.act { sign_out }
+
+ Page::Main::Login.perform do |login|
+ login.sign_in_using_credentials(user)
+ end
+ end
+
+ raise "Failed to load project page and stay logged in"
+ end
+
def fabricate!
- push.project.visit!
+ visit_project_with_retry
+
Page::Project::Show.act { fork_project }
Page::Project::Fork::New.perform do |fork_new|
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/page/main/sign_up.rb b/qa/qa/page/main/sign_up.rb
index 33ab56236f4..64cd395de78 100644
--- a/qa/qa/page/main/sign_up.rb
+++ b/qa/qa/page/main/sign_up.rb
@@ -19,7 +19,7 @@ module QA
fill_in :new_user_password, with: user.password
click_button 'Register'
- Page::Menu::Main.act { has_personal_area? }
+ Page::Menu::Main.act { assert_has_personal_area }
end
end
end
diff --git a/qa/qa/page/menu/main.rb b/qa/qa/page/menu/main.rb
index 36e7285f7b7..2ae86bbc7dc 100644
--- a/qa/qa/page/menu/main.rb
+++ b/qa/qa/page/menu/main.rb
@@ -61,8 +61,13 @@ module QA
end
def has_personal_area?(wait: Capybara.default_max_wait_time)
- # No need to wait, either we're logged-in, or not.
- using_wait_time(wait) { page.has_selector?('.qa-user-avatar') }
+ using_wait_time(wait) do
+ page.has_selector?(element_selector_css(:user_avatar))
+ end
+ end
+
+ def assert_has_personal_area
+ raise "Failed to sign in" unless has_personal_area?
end
private
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/factories/site_statistics.rb b/spec/factories/site_statistics.rb
index dd8c795515a..2533d0eecc2 100644
--- a/spec/factories/site_statistics.rb
+++ b/spec/factories/site_statistics.rb
@@ -2,6 +2,5 @@ FactoryBot.define do
factory :site_statistics, class: 'SiteStatistic' do
id 1
repositories_count 999
- wikis_count 555
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/ide/components/repo_loading_file_spec.js b/spec/javascripts/ide/components/repo_loading_file_spec.js
deleted file mode 100644
index 7c20b8302f9..00000000000
--- a/spec/javascripts/ide/components/repo_loading_file_spec.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import repoLoadingFile from '~/ide/components/repo_loading_file.vue';
-import { resetStore } from '../helpers';
-
-describe('RepoLoadingFile', () => {
- let vm;
-
- function createComponent() {
- const RepoLoadingFile = Vue.extend(repoLoadingFile);
-
- return new RepoLoadingFile({
- store,
- }).$mount();
- }
-
- function assertLines(lines) {
- lines.forEach((line, n) => {
- const index = n + 1;
- expect(line.classList.contains(`skeleton-line-${index}`)).toBeTruthy();
- });
- }
-
- function assertColumns(columns) {
- columns.forEach(column => {
- const container = column.querySelector('.animation-container');
- const lines = [...container.querySelectorAll(':scope > div')];
-
- expect(container).toBeTruthy();
- expect(lines.length).toEqual(3);
- assertLines(lines);
- });
- }
-
- afterEach(() => {
- vm.$destroy();
-
- resetStore(vm.$store);
- });
-
- it('renders 3 columns of animated LoC', () => {
- vm = createComponent();
- const columns = [...vm.$el.querySelectorAll('td')];
-
- expect(columns.length).toEqual(3);
- assertColumns(columns);
- });
-
- it('renders 1 column of animated LoC if isMini', done => {
- vm = createComponent();
- vm.$store.state.leftPanelCollapsed = true;
- vm.$store.state.openFiles.push('test');
-
- vm.$nextTick(() => {
- const columns = [...vm.$el.querySelectorAll('td')];
-
- expect(columns.length).toEqual(1);
- assertColumns(columns);
-
- done();
- });
- });
-});
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/javascripts/vue_shared/components/skeleton_loading_container_spec.js b/spec/javascripts/vue_shared/components/skeleton_loading_container_spec.js
deleted file mode 100644
index 34487885cf0..00000000000
--- a/spec/javascripts/vue_shared/components/skeleton_loading_container_spec.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import Vue from 'vue';
-import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-
-describe('Skeleton loading container', () => {
- let vm;
-
- beforeEach(() => {
- const component = Vue.extend(skeletonLoadingContainer);
- vm = mountComponent(component);
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('renders 3 skeleton lines by default', () => {
- expect(vm.$el.querySelector('.skeleton-line-3')).not.toBeNull();
- });
-
- it('renders in full mode by default', () => {
- expect(vm.$el.classList.contains('animation-container-small')).toBeFalsy();
- });
-
- describe('small', () => {
- beforeEach((done) => {
- vm.small = true;
-
- Vue.nextTick(done);
- });
-
- it('renders in small mode', () => {
- expect(vm.$el.classList.contains('animation-container-small')).toBeTruthy();
- });
- });
-
- describe('lines', () => {
- beforeEach((done) => {
- vm.lines = 5;
-
- Vue.nextTick(done);
- });
-
- it('renders 5 lines', () => {
- expect(vm.$el.querySelector('.skeleton-line-5')).not.toBeNull();
- expect(vm.$el.querySelector('.skeleton-line-6')).toBeNull();
- });
- });
-});
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/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index 10617edec0f..cd7f77024da 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -119,46 +119,4 @@ describe ProjectFeature do
end
end
end
-
- context 'Site Statistics' do
- set(:project_with_wiki) { create(:project, :wiki_enabled) }
- set(:project_without_wiki) { create(:project, :wiki_disabled) }
-
- context 'when creating a project' do
- it 'tracks wiki availability when wikis are enabled by default' do
- expect { create(:project) }.to change { SiteStatistic.fetch.wikis_count }.by(1)
- end
-
- it 'does not track wiki availability when wikis are disabled by default' do
- expect { create(:project, :wiki_disabled) }.not_to change { SiteStatistic.fetch.wikis_count }
- end
- end
-
- context 'when updating a project_feature' do
- it 'untracks wiki availability when disabling wiki access' do
- expect { project_with_wiki.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED) }
- .to change { SiteStatistic.fetch.wikis_count }.by(-1)
- end
-
- it 'tracks again wiki availability when re-enabling wiki access as public' do
- expect { project_without_wiki.project_feature.update_attribute(:wiki_access_level, ProjectFeature::ENABLED) }
- .to change { SiteStatistic.fetch.wikis_count }.by(1)
- end
-
- it 'tracks again wiki availability when re-enabling wiki access as private' do
- expect { project_without_wiki.project_feature.update_attribute(:wiki_access_level, ProjectFeature::PRIVATE) }
- .to change { SiteStatistic.fetch.wikis_count }.by(1)
- end
- end
-
- context 'when removing a project' do
- it 'untracks wiki availability when removing a project with previous wiki access' do
- expect { project_with_wiki.destroy }.to change { SiteStatistic.fetch.wikis_count }.by(-1)
- end
-
- it 'does not untrack wiki availability when removing a project without wiki access' do
- expect { project_without_wiki.destroy }.not_to change { SiteStatistic.fetch.wikis_count }
- end
- end
- end
end
diff --git a/spec/models/site_statistic_spec.rb b/spec/models/site_statistic_spec.rb
index 9b056fbf332..0e739900065 100644
--- a/spec/models/site_statistic_spec.rb
+++ b/spec/models/site_statistic_spec.rb
@@ -25,7 +25,6 @@ describe SiteStatistic do
it 'increases the attribute counter' do
expect { described_class.track('repositories_count') }.to change { statistics.reload.repositories_count }.by(1)
- expect { described_class.track('wikis_count') }.to change { statistics.reload.wikis_count }.by(1)
end
it 'doesnt increase the attribute counter when an exception happens during transaction' do
@@ -56,7 +55,6 @@ describe SiteStatistic do
it 'decreases the attribute counter' do
expect { described_class.untrack('repositories_count') }.to change { statistics.reload.repositories_count }.by(-1)
- expect { described_class.untrack('wikis_count') }.to change { statistics.reload.wikis_count }.by(-1)
end
it 'doesnt decrease the attribute counter when an exception happens during transaction' do
diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb
index 97a88b5d697..d80d0f5a8a8 100644
--- a/spec/services/groups/destroy_service_spec.rb
+++ b/spec/services/groups/destroy_service_spec.rb
@@ -35,14 +35,6 @@ describe Groups::DestroyService do
it { expect(NotificationSetting.unscoped.all).not_to include(notification_setting) }
end
- context 'site statistics' do
- it 'doesnt trigger project deletion hooks twice' do
- expect_any_instance_of(Project).to receive(:untrack_site_statistics).once
-
- destroy_group(group, user, async)
- end
- end
-
context 'mattermost team' do
let!(:chat_team) { create(:chat_team, namespace: group) }
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')
diff --git a/spec/tasks/gitlab/site_statistics_rake_spec.rb b/spec/tasks/gitlab/site_statistics_rake_spec.rb
index 20f0df65e63..c43ce25a540 100644
--- a/spec/tasks/gitlab/site_statistics_rake_spec.rb
+++ b/spec/tasks/gitlab/site_statistics_rake_spec.rb
@@ -6,7 +6,7 @@ describe 'rake gitlab:refresh_site_statistics' do
Rake.application.rake_require 'tasks/gitlab/site_statistics'
create(:project)
- SiteStatistic.fetch.update(repositories_count: 0, wikis_count: 0)
+ SiteStatistic.fetch.update(repositories_count: 0)
end
let(:task) { 'gitlab:refresh_site_statistics' }
@@ -15,10 +15,9 @@ describe 'rake gitlab:refresh_site_statistics' do
run_rake_task(task)
expect(SiteStatistic.fetch.repositories_count).to eq(1)
- expect(SiteStatistic.fetch.wikis_count).to eq(1)
end
it 'displays message listing counters' do
- expect { run_rake_task(task) }.to output(/Updating Site Statistics counters:.* Repositories\.\.\. OK!.* Wikis\.\.\. OK!/m).to_stdout
+ expect { run_rake_task(task) }.to output(/Updating Site Statistics counters:.* Repositories\.\.\. OK!/m).to_stdout
end
end
diff --git a/yarn.lock b/yarn.lock
index 026c422522c..397fd4a2c13 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -164,9 +164,9 @@
version "1.29.0"
resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.29.0.tgz#03b65b513f9099bbda6ecf94d673a2952f8c6c70"
-"@gitlab-org/gitlab-ui@^1.5.1":
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-ui/-/gitlab-ui-1.5.1.tgz#82bc8583e24edfbaab5f1b6e88bf1a8056d7b528"
+"@gitlab-org/gitlab-ui@^1.7.0":
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-ui/-/gitlab-ui-1.7.0.tgz#cf37b7262f90af42664d4d1600917271a8f8fb28"
dependencies:
"@gitlab-org/gitlab-svgs" "^1.23.0"
bootstrap-vue "^2.0.0-rc.11"