diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-14 15:13:33 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-12-14 15:13:33 +0300 |
commit | 79a7da2537e94c34aba92ad4b9a5dc1da431b2c4 (patch) | |
tree | 83b6a53a15a9266ca2e2aca061dd3f33d5282b1f /app | |
parent | a58667ab4b4fc4b0d1e47e95d91c27ccadd0acd3 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
35 files changed, 235 insertions, 145 deletions
diff --git a/app/assets/javascripts/import_entities/import_groups/components/import_table.vue b/app/assets/javascripts/import_entities/import_groups/components/import_table.vue index 298771a4d12..f2837a7bb44 100644 --- a/app/assets/javascripts/import_entities/import_groups/components/import_table.vue +++ b/app/assets/javascripts/import_entities/import_groups/components/import_table.vue @@ -314,9 +314,8 @@ export default { variables: { importRequests }, }); } catch (error) { - const message = error?.networkError?.response?.data?.error ?? i18n.ERROR_IMPORT; createFlash({ - message, + message: i18n.ERROR_IMPORT, captureError: true, error, }); diff --git a/app/assets/javascripts/import_entities/import_groups/components/import_target_cell.vue b/app/assets/javascripts/import_entities/import_groups/components/import_target_cell.vue index ca9ae9447d0..344a6e45370 100644 --- a/app/assets/javascripts/import_entities/import_groups/components/import_target_cell.vue +++ b/app/assets/javascripts/import_entities/import_groups/components/import_target_cell.vue @@ -32,72 +32,84 @@ export default { fullPath() { return this.group.importTarget.targetNamespace.fullPath || s__('BulkImport|No parent'); }, - invalidNameValidationMessage() { - return getInvalidNameValidationMessage(this.group.importTarget); + validationMessage() { + return ( + this.group.progress?.message || getInvalidNameValidationMessage(this.group.importTarget) + ); + }, + validNameState() { + // bootstrap-vue requires null for "indifferent" state, if we return true + // this will highlight field in green like "passed validation" + return this.group.flags.isInvalid && this.group.flags.isAvailableForImport ? false : null; }, }, }; </script> <template> - <div class="gl-display-flex gl-align-items-stretch"> - <import-group-dropdown - #default="{ namespaces }" - :text="fullPath" - :disabled="!group.flags.isAvailableForImport" - :namespaces="availableNamespaces" - toggle-class="gl-rounded-top-right-none! gl-rounded-bottom-right-none!" - class="gl-h-7 gl-flex-grow-1" - data-qa-selector="target_namespace_selector_dropdown" - > - <gl-dropdown-item @click="$emit('update-target-namespace', { fullPath: '', id: null })">{{ - s__('BulkImport|No parent') - }}</gl-dropdown-item> - <template v-if="namespaces.length"> - <gl-dropdown-divider /> - <gl-dropdown-section-header> - {{ s__('BulkImport|Existing groups') }} - </gl-dropdown-section-header> - <gl-dropdown-item - v-for="ns in namespaces" - :key="ns.fullPath" - data-qa-selector="target_group_dropdown_item" - :data-qa-group-name="ns.fullPath" - @click="$emit('update-target-namespace', ns)" - > - {{ ns.fullPath }} - </gl-dropdown-item> - </template> - </import-group-dropdown> - <div - class="gl-h-7 gl-px-3 gl-display-flex gl-align-items-center gl-border-solid gl-border-0 gl-border-t-1 gl-border-b-1 gl-bg-gray-10" - :class="{ - 'gl-text-gray-400 gl-border-gray-100': !group.flags.isAvailableForImport, - 'gl-border-gray-200': group.flags.isAvailableForImport, - }" - > - / - </div> - <div class="gl-flex-grow-1"> - <gl-form-input - class="gl-rounded-top-left-none gl-rounded-bottom-left-none" + <div> + <div class="gl-display-flex gl-align-items-stretch"> + <import-group-dropdown + #default="{ namespaces }" + :text="fullPath" + :disabled="!group.flags.isAvailableForImport" + :namespaces="availableNamespaces" + toggle-class="gl-rounded-top-right-none! gl-rounded-bottom-right-none!" + class="gl-h-7 gl-flex-grow-1" + data-qa-selector="target_namespace_selector_dropdown" + > + <gl-dropdown-item @click="$emit('update-target-namespace', { fullPath: '', id: null })">{{ + s__('BulkImport|No parent') + }}</gl-dropdown-item> + <template v-if="namespaces.length"> + <gl-dropdown-divider /> + <gl-dropdown-section-header> + {{ s__('BulkImport|Existing groups') }} + </gl-dropdown-section-header> + <gl-dropdown-item + v-for="ns in namespaces" + :key="ns.fullPath" + data-qa-selector="target_group_dropdown_item" + :data-qa-group-name="ns.fullPath" + @click="$emit('update-target-namespace', ns)" + > + {{ ns.fullPath }} + </gl-dropdown-item> + </template> + </import-group-dropdown> + <div + class="gl-h-7 gl-px-3 gl-display-flex gl-align-items-center gl-border-solid gl-border-0 gl-border-t-1 gl-border-b-1 gl-bg-gray-10" :class="{ - 'gl-inset-border-1-gray-200!': group.flags.isAvailableForImport, - 'gl-inset-border-1-gray-100!': !group.flags.isAvailableForImport, - 'is-invalid': group.flags.isInvalid && group.flags.isAvailableForImport, + 'gl-text-gray-400 gl-border-gray-100': !group.flags.isAvailableForImport, + 'gl-border-gray-200': group.flags.isAvailableForImport, }" - debounce="500" - :disabled="!group.flags.isAvailableForImport" - :value="group.importTarget.newName" - :aria-label="__('New name')" - @input="$emit('update-new-name', $event)" - /> - <p - v-if="group.flags.isAvailableForImport && group.flags.isInvalid" - class="gl-text-red-500 gl-m-0 gl-mt-2" > - {{ invalidNameValidationMessage }} - </p> + / + </div> + <div class="gl-flex-grow-1"> + <gl-form-input + class="gl-rounded-top-left-none gl-rounded-bottom-left-none" + :class="{ + 'gl-inset-border-1-gray-200!': + group.flags.isAvailableForImport && !group.flags.isInvalid, + 'gl-inset-border-1-gray-100!': + !group.flags.isAvailableForImport && !group.flags.isInvalid, + }" + debounce="500" + :disabled="!group.flags.isAvailableForImport" + :value="group.importTarget.newName" + :aria-label="__('New name')" + :state="validNameState" + @input="$emit('update-new-name', $event)" + /> + </div> + </div> + <div + v-if="group.flags.isAvailableForImport && (group.flags.isInvalid || validationMessage)" + class="gl-text-red-500 gl-m-0 gl-mt-2" + role="alert" + > + {{ validationMessage }} </div> </div> </template> diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js b/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js index bce6e7bcb1f..31352bbc8bb 100644 --- a/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js +++ b/app/assets/javascripts/import_entities/import_groups/graphql/client_factory.js @@ -142,9 +142,7 @@ export function createResolvers({ endpoints }) { }; }); - const { - data: { id: jobId }, - } = await axios.post(endpoints.createBulkImport, { + const { data: originalResponse } = await axios.post(endpoints.createBulkImport, { bulk_import: importOperations.map((op) => ({ source_type: 'group_entity', source_full_path: op.group.fullPath, @@ -153,15 +151,21 @@ export function createResolvers({ endpoints }) { })), }); - return importOperations.map((op) => { + const responses = Array.isArray(originalResponse) + ? originalResponse + : [{ success: true, id: originalResponse.id }]; + + return importOperations.map((op, idx) => { + const response = responses[idx]; const lastImportTarget = { targetNamespace: op.targetNamespace, newName: op.newName, }; const progress = { - id: jobId, - status: STATUSES.CREATED, + id: response.id || `local-${Date.now()}-${idx}`, + status: response.success ? STATUSES.CREATED : STATUSES.FAILED, + message: response.message || null, }; localStorageCache.set(op.group.webUrl, { progress, lastImportTarget }); diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/fragments/bulk_import_source_group_progress.fragment.graphql b/app/assets/javascripts/import_entities/import_groups/graphql/fragments/bulk_import_source_group_progress.fragment.graphql index 2d60bf82d65..33c564f36a8 100644 --- a/app/assets/javascripts/import_entities/import_groups/graphql/fragments/bulk_import_source_group_progress.fragment.graphql +++ b/app/assets/javascripts/import_entities/import_groups/graphql/fragments/bulk_import_source_group_progress.fragment.graphql @@ -1,4 +1,5 @@ fragment BulkImportSourceGroupProgress on ClientBulkImportProgress { id status + message } diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/mutations/import_groups.mutation.graphql b/app/assets/javascripts/import_entities/import_groups/graphql/mutations/import_groups.mutation.graphql index 75215471d0f..39289887b75 100644 --- a/app/assets/javascripts/import_entities/import_groups/graphql/mutations/import_groups.mutation.graphql +++ b/app/assets/javascripts/import_entities/import_groups/graphql/mutations/import_groups.mutation.graphql @@ -9,6 +9,7 @@ mutation importGroups($importRequests: [ImportGroupInput!]!) { progress { id status + message } } } diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/services/local_storage_cache.js b/app/assets/javascripts/import_entities/import_groups/graphql/services/local_storage_cache.js index 09bc7b33692..1aad22f0f3f 100644 --- a/app/assets/javascripts/import_entities/import_groups/graphql/services/local_storage_cache.js +++ b/app/assets/javascripts/import_entities/import_groups/graphql/services/local_storage_cache.js @@ -22,7 +22,14 @@ export class LocalStorageCache { loadCacheFromStorage() { try { - return JSON.parse(this.storage.getItem(KEY)) ?? {}; + const storage = JSON.parse(this.storage.getItem(KEY)) ?? {}; + Object.values(storage).forEach((entry) => { + if (entry.progress && !('message' in entry.progress)) { + // eslint-disable-next-line no-param-reassign + entry.progress.message = ''; + } + }); + return storage; } catch { return {}; } diff --git a/app/assets/javascripts/import_entities/import_groups/graphql/typedefs.graphql b/app/assets/javascripts/import_entities/import_groups/graphql/typedefs.graphql index b8dd79a5000..1639ce16758 100644 --- a/app/assets/javascripts/import_entities/import_groups/graphql/typedefs.graphql +++ b/app/assets/javascripts/import_entities/import_groups/graphql/typedefs.graphql @@ -16,6 +16,7 @@ type ClientBulkImportSourceGroupConnection { type ClientBulkImportProgress { id: ID! status: String! + message: String } type ClientBulkImportValidationError { diff --git a/app/assets/javascripts/packages_and_registries/package_registry/constants.js b/app/assets/javascripts/packages_and_registries/package_registry/constants.js index d75c195be4c..ab6541e4264 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/constants.js +++ b/app/assets/javascripts/packages_and_registries/package_registry/constants.js @@ -1,4 +1,5 @@ import { s__, __ } from '~/locale'; +import { helpPagePath } from '~/helpers/help_page_helper'; export { DELETE_PACKAGE_TRACKING_ACTION, @@ -136,3 +137,8 @@ export const PACKAGE_TYPES = [ s__('PackageRegistry|Debian'), s__('PackageRegistry|Helm'), ]; + +// links + +export const EMPTY_LIST_HELP_URL = helpPagePath('user/packages/package_registry/index'); +export const PACKAGE_HELP_URL = helpPagePath('user/packages/index'); diff --git a/app/assets/javascripts/packages_and_registries/package_registry/index.js b/app/assets/javascripts/packages_and_registries/package_registry/index.js new file mode 100644 index 00000000000..7ec931ff9a0 --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/package_registry/index.js @@ -0,0 +1,30 @@ +import Vue from 'vue'; +import Translate from '~/vue_shared/translate'; +import { apolloProvider } from '~/packages_and_registries/package_registry/graphql/index'; +import PackageRegistry from '~/packages_and_registries/package_registry/pages/index.vue'; +import createRouter from './router'; + +Vue.use(Translate); + +export default () => { + const el = document.getElementById('js-vue-packages-list'); + const { endpoint, resourceId, fullPath, pageType, emptyListIllustration } = el.dataset; + const router = createRouter(endpoint); + + const isGroupPage = pageType === 'groups'; + + return new Vue({ + el, + router, + apolloProvider, + provide: { + resourceId, + fullPath, + emptyListIllustration, + isGroupPage, + }, + render(createElement) { + return createElement(PackageRegistry); + }, + }); +}; diff --git a/app/assets/javascripts/packages_and_registries/package_registry/pages/index.vue b/app/assets/javascripts/packages_and_registries/package_registry/pages/index.vue new file mode 100644 index 00000000000..a14d0c32cbe --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/package_registry/pages/index.vue @@ -0,0 +1,5 @@ +<template> + <div> + <router-view /> + </div> +</template> diff --git a/app/assets/javascripts/packages_and_registries/package_registry/pages/list.js b/app/assets/javascripts/packages_and_registries/package_registry/pages/list.js deleted file mode 100644 index d797a0a5327..00000000000 --- a/app/assets/javascripts/packages_and_registries/package_registry/pages/list.js +++ /dev/null @@ -1,24 +0,0 @@ -import Vue from 'vue'; -import Translate from '~/vue_shared/translate'; -import { apolloProvider } from '~/packages_and_registries/package_registry/graphql/index'; -import PackagesListApp from '../components/list/app.vue'; - -Vue.use(Translate); - -export default () => { - const el = document.getElementById('js-vue-packages-list'); - - const isGroupPage = el.dataset.pageType === 'groups'; - - return new Vue({ - el, - apolloProvider, - provide: { - ...el.dataset, - isGroupPage, - }, - render(createElement) { - return createElement(PackagesListApp); - }, - }); -}; diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/list/app.vue b/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue index d65711d061d..38df701157a 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/components/list/app.vue +++ b/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue @@ -9,13 +9,15 @@ import { GROUP_RESOURCE_TYPE, GRAPHQL_PAGE_SIZE, DELETE_PACKAGE_SUCCESS_MESSAGE, + EMPTY_LIST_HELP_URL, + PACKAGE_HELP_URL, } from '~/packages_and_registries/package_registry/constants'; import getPackagesQuery from '~/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql'; import DeletePackage from '~/packages_and_registries/package_registry/components/functional/delete_package.vue'; -import PackageTitle from './package_title.vue'; -import PackageSearch from './package_search.vue'; -import PackageList from './packages_list.vue'; +import PackageTitle from '~/packages_and_registries/package_registry/components/list/package_title.vue'; +import PackageSearch from '~/packages_and_registries/package_registry/components/list/package_search.vue'; +import PackageList from '~/packages_and_registries/package_registry/components/list/packages_list.vue'; export default { components: { @@ -27,13 +29,7 @@ export default { PackageSearch, DeletePackage, }, - inject: [ - 'packageHelpUrl', - 'emptyListIllustration', - 'emptyListHelpUrl', - 'isGroupPage', - 'fullPath', - ], + inject: ['emptyListIllustration', 'isGroupPage', 'fullPath'], data() { return { packages: {}, @@ -156,12 +152,16 @@ export default { 'PackageRegistry|Learn how to %{noPackagesLinkStart}publish and share your packages%{noPackagesLinkEnd} with GitLab.', ), }, + links: { + EMPTY_LIST_HELP_URL, + PACKAGE_HELP_URL, + }, }; </script> <template> <div> - <package-title :help-url="packageHelpUrl" :count="packagesCount" /> + <package-title :help-url="$options.links.PACKAGE_HELP_URL" :count="packagesCount" /> <package-search @update="handleSearchUpdate" /> <delete-package @@ -185,7 +185,9 @@ export default { <gl-sprintf v-if="hasFilters" :message="$options.i18n.widenFilters" /> <gl-sprintf v-else :message="$options.i18n.noResultsText"> <template #noPackagesLink="{ content }"> - <gl-link :href="emptyListHelpUrl" target="_blank">{{ content }}</gl-link> + <gl-link :href="$options.links.EMPTY_LIST_HELP_URL" target="_blank">{{ + content + }}</gl-link> </template> </gl-sprintf> </template> diff --git a/app/assets/javascripts/packages_and_registries/package_registry/router.js b/app/assets/javascripts/packages_and_registries/package_registry/router.js new file mode 100644 index 00000000000..ea5b740e879 --- /dev/null +++ b/app/assets/javascripts/packages_and_registries/package_registry/router.js @@ -0,0 +1,21 @@ +import Vue from 'vue'; +import VueRouter from 'vue-router'; +import List from '~/packages_and_registries/package_registry/pages/list.vue'; + +Vue.use(VueRouter); + +export default function createRouter(base) { + const router = new VueRouter({ + base, + mode: 'history', + routes: [ + { + name: 'list', + path: '/', + component: List, + }, + ], + }); + + return router; +} diff --git a/app/assets/javascripts/pages/groups/packages/index/index.js b/app/assets/javascripts/pages/groups/packages/index/index.js index f9eecff4ac4..174973a9fad 100644 --- a/app/assets/javascripts/pages/groups/packages/index/index.js +++ b/app/assets/javascripts/pages/groups/packages/index/index.js @@ -1,3 +1,3 @@ -import packageList from '~/packages_and_registries/package_registry/pages/list'; +import packageApp from '~/packages_and_registries/package_registry/index'; -packageList(); +packageApp(); diff --git a/app/assets/javascripts/pages/projects/packages/packages/index/index.js b/app/assets/javascripts/pages/projects/packages/packages/index/index.js index f9eecff4ac4..174973a9fad 100644 --- a/app/assets/javascripts/pages/projects/packages/packages/index/index.js +++ b/app/assets/javascripts/pages/projects/packages/packages/index/index.js @@ -1,3 +1,3 @@ -import packageList from '~/packages_and_registries/package_registry/pages/list'; +import packageApp from '~/packages_and_registries/package_registry/index'; -packageList(); +packageApp(); diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue index e36cfb3b275..2f6776f835e 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue @@ -165,6 +165,6 @@ export default { <template> <div> <div class="flash-container js-suggestions-flash"></div> - <div v-show="isRendered" ref="container" v-safe-html="noteHtml" class="md"></div> + <div v-show="isRendered" ref="container" v-safe-html="noteHtml" class="md suggestions"></div> </div> </template> diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss index 9b04b9a2612..c6e52c13e83 100644 --- a/app/assets/stylesheets/framework/markdown_area.scss +++ b/app/assets/stylesheets/framework/markdown_area.scss @@ -139,6 +139,10 @@ font-family: $monospace-font !important; } +.suggestions.md > .markdown-code-block { + @include gl-static; +} + .md-suggestion-header { height: $suggestion-header-height; display: flex; diff --git a/app/controllers/import/bulk_imports_controller.rb b/app/controllers/import/bulk_imports_controller.rb index bec26cb547d..f26c06b7e37 100644 --- a/app/controllers/import/bulk_imports_controller.rb +++ b/app/controllers/import/bulk_imports_controller.rb @@ -40,13 +40,9 @@ class Import::BulkImportsController < ApplicationController end def create - response = ::BulkImports::CreateService.new(current_user, create_params, credentials).execute + responses = create_params.map { |entry| ::BulkImports::CreateService.new(current_user, entry, credentials).execute } - if response.success? - render json: response.payload.to_json(only: [:id]) - else - render json: { error: response.message }, status: response.http_status - end + render json: responses.map { |response| { success: response.success?, id: response.payload[:id], message: response.message } } end def realtime_changes diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 2b42dcb66ad..b30ef7506aa 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -99,7 +99,7 @@ class Projects::BlobController < Projects::ApplicationController @content = params[:content] @blob.load_all_data! diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3', include_diff_info: true) - diff_lines = diffy.diff.scan(/.*\n/)[2..-1] + diff_lines = diffy.diff.scan(/.*\n/)[2..] diff_lines = Gitlab::Diff::Parser.new.parse(diff_lines).to_a @diff_lines = Gitlab::Diff::Highlight.new(diff_lines, repository: @repository).highlight diff --git a/app/graphql/resolvers/ci/jobs_resolver.rb b/app/graphql/resolvers/ci/jobs_resolver.rb index 5ae9e721cc8..df138a15538 100644 --- a/app/graphql/resolvers/ci/jobs_resolver.rb +++ b/app/graphql/resolvers/ci/jobs_resolver.rb @@ -29,7 +29,7 @@ module Resolvers job_types: security_report_types ).execute else - pipeline.statuses + pipeline.statuses_order_id_desc end end end diff --git a/app/graphql/resolvers/project_pipelines_resolver.rb b/app/graphql/resolvers/project_pipelines_resolver.rb index 23423b9274a..47a8b028d4d 100644 --- a/app/graphql/resolvers/project_pipelines_resolver.rb +++ b/app/graphql/resolvers/project_pipelines_resolver.rb @@ -18,7 +18,7 @@ module Resolvers def preloads { - jobs: { statuses: [:needs] }, + jobs: { statuses_order_id_desc: [:needs] }, upstream: [:triggered_by_pipeline], downstream: [:triggered_pipelines] } diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index ca5fe38576e..2b5f726dad1 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -283,7 +283,7 @@ module DiffHelper return path unless path.size > max && max > 3 - "...#{path[-(max - 3)..-1]}" + "...#{path[-(max - 3)..]}" end def code_navigation_path(diffs) diff --git a/app/helpers/packages_helper.rb b/app/helpers/packages_helper.rb index c69d9eb1326..66f80e7eeb8 100644 --- a/app/helpers/packages_helper.rb +++ b/app/helpers/packages_helper.rb @@ -38,17 +38,6 @@ module PackagesHelper "#{Gitlab.config.gitlab.host}/#{group_id}" end - def packages_list_data(type, resource) - { - resource_id: resource.id, - full_path: resource.full_path, - page_type: type, - empty_list_help_url: help_page_path('user/packages/package_registry/index'), - empty_list_illustration: image_path('illustrations/no-packages.svg'), - package_help_url: help_page_path('user/packages/index') - } - end - def track_package_event(event_name, scope, **args) ::Packages::CreateEventService.new(nil, current_user, event_name: event_name, scope: scope).execute category = args.delete(:category) || self.class.name diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 2a746784a01..d0db3e134ed 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -63,6 +63,7 @@ module Ci has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline has_many :latest_statuses_ordered_by_stage, -> { latest.order(:stage_idx, :stage) }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline has_many :latest_statuses, -> { latest }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline + has_many :statuses_order_id_desc, -> { order_id_desc }, class_name: 'CommitStatus', foreign_key: :commit_id has_many :processables, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline has_many :bridges, class_name: 'Ci::Bridge', foreign_key: :commit_id, inverse_of: :pipeline has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline diff --git a/app/models/concerns/token_authenticatable_strategies/encryption_helper.rb b/app/models/concerns/token_authenticatable_strategies/encryption_helper.rb index 3be82ed72d3..447521ad8c1 100644 --- a/app/models/concerns/token_authenticatable_strategies/encryption_helper.rb +++ b/app/models/concerns/token_authenticatable_strategies/encryption_helper.rb @@ -11,7 +11,7 @@ module TokenAuthenticatableStrategies # The pattern of the token is "#{DYNAMIC_NONCE_IDENTIFIER}#{token}#{iv_of_12_characters}" if token.start_with?(DYNAMIC_NONCE_IDENTIFIER) && token.size > NONCE_SIZE + DYNAMIC_NONCE_IDENTIFIER.size token_to_decrypt = token[1...-NONCE_SIZE] - iv = token[-NONCE_SIZE..-1] + iv = token[-NONCE_SIZE..] Gitlab::CryptoHelper.aes256_gcm_decrypt(token_to_decrypt, nonce: iv) else diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb index d1584a62bfb..16b95d2a2b9 100644 --- a/app/models/hooks/project_hook.rb +++ b/app/models/hooks/project_hook.rb @@ -31,10 +31,6 @@ class ProjectHook < WebHook _('Webhooks') end - def web_hooks_disable_failed? - Feature.enabled?(:web_hooks_disable_failed, project) - end - override :rate_limit def rate_limit project.actual_limits.limit_for(:web_hook_calls) @@ -44,6 +40,13 @@ class ProjectHook < WebHook def application_context super.merge(project: project) end + + private + + override :web_hooks_disable_failed? + def web_hooks_disable_failed? + Feature.enabled?(:web_hooks_disable_failed, project) + end end ProjectHook.prepend_mod_with('ProjectHook') diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index cb5c1ac48cd..e8a55abfc8f 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -34,9 +34,19 @@ class WebHook < ApplicationRecord end def executable? - return true unless web_hooks_disable_failed? + !temporarily_disabled? && !permanently_disabled? + end + + def temporarily_disabled? + return false unless web_hooks_disable_failed? + + disabled_until.present? && disabled_until >= Time.current + end + + def permanently_disabled? + return false unless web_hooks_disable_failed? - recent_failures <= FAILURE_THRESHOLD && (disabled_until.nil? || disabled_until < Time.current) + recent_failures > FAILURE_THRESHOLD end # rubocop: disable CodeReuse/ServiceClass @@ -69,6 +79,8 @@ class WebHook < ApplicationRecord end def disable! + return if permanently_disabled? + update_attribute(:recent_failures, FAILURE_THRESHOLD + 1) end @@ -80,7 +92,7 @@ class WebHook < ApplicationRecord end def backoff! - return if backoff_count >= MAX_FAILURES && disabled_until && disabled_until > Time.current + return if permanently_disabled? || (backoff_count >= MAX_FAILURES && temporarily_disabled?) assign_attributes(disabled_until: next_backoff.from_now, backoff_count: backoff_count.succ.clamp(0, MAX_FAILURES)) save(validate: false) @@ -93,7 +105,19 @@ class WebHook < ApplicationRecord save(validate: false) end - # Overridden in ProjectHook and GroupHook, other webhooks are not rate-limited. + # @return [Boolean] Whether or not the WebHook is currently throttled. + def rate_limited? + return false unless rate_limit + + Gitlab::ApplicationRateLimiter.peek( + :web_hook_calls, + scope: [self], + threshold: rate_limit + ) + end + + # Threshold for the rate-limit. + # Overridden in ProjectHook and GroupHook, other WebHooks are not rate-limited. def rate_limit nil end diff --git a/app/models/namespaces/traversal/linear.rb b/app/models/namespaces/traversal/linear.rb index 36b45dde6b4..5a5f2a5d063 100644 --- a/app/models/namespaces/traversal/linear.rb +++ b/app/models/namespaces/traversal/linear.rb @@ -204,7 +204,7 @@ module Namespaces end if bottom - skope = skope.where(id: bottom.traversal_ids[0..-1]) + skope = skope.where(id: bottom.traversal_ids) end # The original `with_depth` attribute in ObjectHierarchy increments as you diff --git a/app/models/serverless/domain.rb b/app/models/serverless/domain.rb index 2fef3b66b08..164f93afa9a 100644 --- a/app/models/serverless/domain.rb +++ b/app/models/serverless/domain.rb @@ -37,7 +37,7 @@ module Serverless 'a1', serverless_domain_cluster.uuid[2..-3], 'f2', - serverless_domain_cluster.uuid[-2..-1] + serverless_domain_cluster.uuid[-2..] ].join end end diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 25438581f2f..3dbbbcdfe23 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -338,7 +338,7 @@ class WikiPage current_dirname = File.dirname(title) if persisted? - return title[1..-1] if current_dirname == '/' + return title[1..] if current_dirname == '/' return File.join([directory.presence, title].compact) if current_dirname == '.' end diff --git a/app/services/bulk_imports/create_service.rb b/app/services/bulk_imports/create_service.rb index c1becbb5609..cbf2b34b33c 100644 --- a/app/services/bulk_imports/create_service.rb +++ b/app/services/bulk_imports/create_service.rb @@ -59,7 +59,7 @@ module BulkImports ) bulk_import.create_configuration!(credentials.slice(:url, :access_token)) - params.each do |entity| + Array.wrap(params).each do |entity| BulkImports::Entity.create!( bulk_import: bulk_import, source_type: entity[:source_type], diff --git a/app/services/cohorts_service.rb b/app/services/cohorts_service.rb index 7bc3b267a12..1b1598b301c 100644 --- a/app/services/cohorts_service.rb +++ b/app/services/cohorts_service.rb @@ -38,7 +38,7 @@ class CohortsService { registration_month: registration_month, - activity_months: activity_months[1..-1], + activity_months: activity_months[1..], total: activity_months.first[:total], inactive: inactive } diff --git a/app/uploaders/lfs_object_uploader.rb b/app/uploaders/lfs_object_uploader.rb index 0a966f3d44f..027857500f4 100644 --- a/app/uploaders/lfs_object_uploader.rb +++ b/app/uploaders/lfs_object_uploader.rb @@ -9,7 +9,7 @@ class LfsObjectUploader < GitlabUploader alias_method :upload, :model def filename - model.oid[4..-1] + model.oid[4..] end def store_dir diff --git a/app/views/groups/packages/index.html.haml b/app/views/groups/packages/index.html.haml index 7910217c939..d56a806f082 100644 --- a/app/views/groups/packages/index.html.haml +++ b/app/views/groups/packages/index.html.haml @@ -3,4 +3,8 @@ .row .col-12 - #js-vue-packages-list{ data: packages_list_data('groups', @group) } + #js-vue-packages-list{ data: { resource_id: @group.id, + full_path: @group.full_path, + endpoint: group_packages_path(@group), + page_type: 'groups', + empty_list_illustration: image_path('illustrations/no-packages.svg'), } } diff --git a/app/views/projects/packages/packages/index.html.haml b/app/views/projects/packages/packages/index.html.haml index 0d5350ab62b..c67b06218e2 100644 --- a/app/views/projects/packages/packages/index.html.haml +++ b/app/views/projects/packages/packages/index.html.haml @@ -3,4 +3,8 @@ .row .col-12 - #js-vue-packages-list{ data: packages_list_data('projects', @project) } + #js-vue-packages-list{ data: { resource_id: @project.id, + full_path: @project.full_path, + endpoint: project_packages_path(@project), + page_type: 'projects', + empty_list_illustration: image_path('illustrations/no-packages.svg'), } } |