diff options
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" |