diff options
Diffstat (limited to 'app')
48 files changed, 210 insertions, 94 deletions
diff --git a/app/assets/javascripts/alerts_settings/components/alert_mapping_builder.vue b/app/assets/javascripts/alerts_settings/components/alert_mapping_builder.vue index 1a586bd1e91..bc4df04cb30 100644 --- a/app/assets/javascripts/alerts_settings/components/alert_mapping_builder.vue +++ b/app/assets/javascripts/alerts_settings/components/alert_mapping_builder.vue @@ -159,8 +159,10 @@ export default { </div> <div class="gl-display-table-cell gl-pr-3 gl-vertical-align-middle"> - <div class="right-arrow"> - <i class="right-arrow-head"></i> + <div class="right-arrow gl-relative gl-w-full gl-bg-gray-400"> + <i + class="right-arrow-head gl-absolute gl-border-solid gl-border-gray-400 gl-display-inline-block gl-p-2" + ></i> </div> </div> diff --git a/app/assets/javascripts/content_editor/components/content_editor.vue b/app/assets/javascripts/content_editor/components/content_editor.vue index beb3497e250..92f3c3fb8fa 100644 --- a/app/assets/javascripts/content_editor/components/content_editor.vue +++ b/app/assets/javascripts/content_editor/components/content_editor.vue @@ -90,6 +90,11 @@ export default { required: false, default: () => ({}), }, + disableAttachments: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { @@ -223,7 +228,11 @@ export default { class="md-area gl-border-none! gl-shadow-none!" :class="{ 'is-focused': focused }" > - <formatting-toolbar ref="toolbar" @enableMarkdownEditor="$emit('enableMarkdownEditor')" /> + <formatting-toolbar + ref="toolbar" + :hide-attachment-button="disableAttachments" + @enableMarkdownEditor="$emit('enableMarkdownEditor')" + /> <div v-if="showPlaceholder" class="gl-absolute gl-text-gray-400 gl-px-5 gl-pt-4"> {{ placeholder }} </div> diff --git a/app/assets/javascripts/content_editor/components/formatting_toolbar.vue b/app/assets/javascripts/content_editor/components/formatting_toolbar.vue index fac259cf6a1..89d7a2c958c 100644 --- a/app/assets/javascripts/content_editor/components/formatting_toolbar.vue +++ b/app/assets/javascripts/content_editor/components/formatting_toolbar.vue @@ -16,6 +16,13 @@ export default { ToolbarMoreDropdown, EditorModeSwitcher, }, + props: { + hideAttachmentButton: { + type: Boolean, + default: false, + required: false, + }, + }, methods: { trackToolbarControlExecution({ contentType, value }) { trackUIControl({ property: contentType, value }); @@ -114,6 +121,7 @@ export default { /> <toolbar-table-button data-testid="table" @execute="trackToolbarControlExecution" /> <toolbar-attachment-button + v-if="!hideAttachmentButton" data-testid="attachment" @execute="trackToolbarControlExecution" /> diff --git a/app/assets/javascripts/pages/admin/topics/edit/index.js b/app/assets/javascripts/pages/admin/topics/edit/index.js index b2cbd52fb27..901fd9193a5 100644 --- a/app/assets/javascripts/pages/admin/topics/edit/index.js +++ b/app/assets/javascripts/pages/admin/topics/edit/index.js @@ -1,11 +1,10 @@ -import $ from 'jquery'; -import GLForm from '~/gl_form'; import initFilePickers from '~/file_pickers'; import ZenMode from '~/zen_mode'; import { initRemoveAvatar } from '~/admin/topics'; +import { mountMarkdownEditor } from '~/vue_shared/components/markdown/mount_markdown_editor'; -new GLForm($('.js-project-topic-form')); // eslint-disable-line no-new initFilePickers(); new ZenMode(); // eslint-disable-line no-new initRemoveAvatar(); +mountMarkdownEditor(); diff --git a/app/assets/javascripts/pages/admin/topics/new/index.js b/app/assets/javascripts/pages/admin/topics/new/index.js index c4e05bbd092..fc9ca4fd4e6 100644 --- a/app/assets/javascripts/pages/admin/topics/new/index.js +++ b/app/assets/javascripts/pages/admin/topics/new/index.js @@ -1,8 +1,7 @@ -import $ from 'jquery'; -import GLForm from '~/gl_form'; import initFilePickers from '~/file_pickers'; import ZenMode from '~/zen_mode'; +import { mountMarkdownEditor } from '~/vue_shared/components/markdown/mount_markdown_editor'; -new GLForm($('.js-project-topic-form')); // eslint-disable-line no-new initFilePickers(); new ZenMode(); // eslint-disable-line no-new +mountMarkdownEditor(); diff --git a/app/assets/javascripts/projects/commit_box/info/components/commit_refs.vue b/app/assets/javascripts/projects/commit_box/info/components/commit_refs.vue index 936938f3032..4258332ed6e 100644 --- a/app/assets/javascripts/projects/commit_box/info/components/commit_refs.vue +++ b/app/assets/javascripts/projects/commit_box/info/components/commit_refs.vue @@ -1,5 +1,6 @@ <script> import { createAlert } from '~/alert'; +import { joinPaths } from '~/lib/utils/url_utility'; import commitReferencesQuery from '../graphql/queries/commit_references.query.graphql'; import containingBranchesQuery from '../graphql/queries/commit_containing_branches.query.graphql'; import containingTagsQuery from '../graphql/queries/commit_containing_tags.query.graphql'; @@ -64,6 +65,10 @@ export default { commitSha: this.commitSha, }; }, + commitsUrlPart() { + const urlPart = joinPaths(gon.relative_url_root || '', `/${this.fullPath}`, `/-/commits/`); + return urlPart; + }, }, methods: { async fetchContainingRefs({ query, namespace }) { @@ -106,6 +111,7 @@ export default { :tipping-refs="tippingBranches" :containing-refs="containingBranches" :namespace="$options.i18n.branches" + :url-part="commitsUrlPart" @[$options.fetchContainingRefsEvent]="fetchContainingBranches" /> <refs-list @@ -115,6 +121,7 @@ export default { :tipping-refs="tippingTags" :containing-refs="containingTags" :namespace="$options.i18n.tags" + :url-part="commitsUrlPart" @[$options.fetchContainingRefsEvent]="fetchContainingTags" /> </div> diff --git a/app/assets/javascripts/projects/commit_box/info/components/refs_list.vue b/app/assets/javascripts/projects/commit_box/info/components/refs_list.vue index 602fa26efa7..7e21040a3b1 100644 --- a/app/assets/javascripts/projects/commit_box/info/components/refs_list.vue +++ b/app/assets/javascripts/projects/commit_box/info/components/refs_list.vue @@ -12,6 +12,10 @@ export default { GlIcon, }, props: { + urlPart: { + type: String, + required: true, + }, containingRefs: { type: Array, required: false, @@ -66,9 +70,14 @@ export default { <template> <div class="gl-pt-4"> <span data-testid="title" class="gl-mr-2">{{ namespace }}</span> - <gl-badge v-for="ref in tippingRefs" :key="ref" class="gl-mt-2 gl-mr-2" size="sm">{{ - ref - }}</gl-badge> + <gl-badge + v-for="ref in tippingRefs" + :key="ref" + :href="`${urlPart}${ref}`" + class="gl-mt-2 gl-mr-2" + size="sm" + >{{ ref }}</gl-badge + > <gl-button v-if="hasContainingRefs" class="gl-mr-2 gl-font-sm!" @@ -82,9 +91,14 @@ export default { <gl-collapse :visible="isContainingRefsVisible"> <gl-skeleton-loader v-if="isLoadingRefs" :lines="1" /> <template v-else> - <gl-badge v-for="ref in containingRefs" :key="ref" class="gl-mt-3 gl-mr-2" size="sm">{{ - ref - }}</gl-badge> + <gl-badge + v-for="ref in containingRefs" + :key="ref" + :href="`${urlPart}${ref}`" + class="gl-mt-3 gl-mr-2" + size="sm" + >{{ ref }}</gl-badge + > </template> </gl-collapse> </div> diff --git a/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue b/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue index d9d4056e997..9fd606d775d 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue @@ -95,6 +95,11 @@ export default { required: false, default: false, }, + disableAttachments: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { @@ -111,6 +116,9 @@ export default { // Match textarea focus behavior return this.autofocus && !this.autofocused ? 'end' : false; }, + markdownFieldRestrictedToolBarItems() { + return this.disableAttachments ? ['attach-file'] : []; + }, }, watch: { value(val) { @@ -231,7 +239,7 @@ export default { v-bind="$attrs" data-testid="markdown-field" :markdown-preview-path="renderMarkdownPath" - can-attach-file + :can-attach-file="!disableAttachments" :textarea-value="markdown" :uploads-path="uploadsPath" :enable-autocomplete="enableAutocomplete" @@ -240,6 +248,7 @@ export default { :quick-actions-docs-path="quickActionsDocsPath" :show-content-editor-switcher="enableContentEditor" :drawio-enabled="drawioEnabled" + :restricted-tool-bar-items="markdownFieldRestrictedToolBarItems" :remove-border="true" @enableContentEditor="onEditingModeChange('contentEditor')" @handleSuggestDismissed="() => $emit('handleSuggestDismissed')" @@ -256,8 +265,7 @@ export default { :disabled="disabled" @input="updateMarkdownFromMarkdownField" @keydown="$emit('keydown', $event)" - > - </textarea> + ></textarea> </template> </markdown-field> <div v-else> @@ -273,6 +281,7 @@ export default { :enable-autocomplete="enableAutocomplete" :autocomplete-data-sources="autocompleteDataSources" :editable="!disabled" + :disable-attachments="disableAttachments" @initialized="setEditorAsAutofocused" @change="updateMarkdownFromContentEditor" @keydown="$emit('keydown', $event)" diff --git a/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js b/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js index ac4f06a665d..8ff14220eab 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js +++ b/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js @@ -1,5 +1,6 @@ import Vue from 'vue'; import { queryToObject, objectToQuery } from '~/lib/utils/url_utility'; +import { parseBoolean } from '~/lib/utils/common_utils'; import { CLEAR_AUTOSAVE_ENTRY_EVENT } from '../../constants'; import MarkdownEditor from './markdown_editor.vue'; import eventHub from './eventhub'; @@ -67,6 +68,9 @@ export function mountMarkdownEditor() { newIssuePath, } = el.dataset; + const supportsQuickActions = parseBoolean(el.dataset.supportsQuickActions ?? true); + const enableAutocomplete = parseBoolean(el.dataset.enableAutocomplete ?? true); + const disableAttachments = parseBoolean(el.dataset.disableAttachments ?? false); const hiddenInput = el.querySelector('input[type="hidden"]'); const formFieldName = hiddenInput.getAttribute('name'); const formFieldId = hiddenInput.getAttribute('id'); @@ -102,9 +106,11 @@ export function mountMarkdownEditor() { 'data-qa-selector': qaSelector, }, autosaveKey, - enableAutocomplete: true, + enableAutocomplete, autocompleteDataSources: gl.GfmAutoComplete?.dataSources, - supportsQuickActions: true, + supportsQuickActions, + disableAttachments, + autofocus: true, }, }); }, diff --git a/app/assets/stylesheets/page_bundles/alert_management_settings.scss b/app/assets/stylesheets/page_bundles/alert_management_settings.scss index ed2707ffbcd..7abde7c1a11 100644 --- a/app/assets/stylesheets/page_bundles/alert_management_settings.scss +++ b/app/assets/stylesheets/page_bundles/alert_management_settings.scss @@ -3,21 +3,13 @@ $stroke-size: 1px; .right-arrow { - @include gl-relative; - @include gl-w-full; height: $stroke-size; - background-color: var(--gray-400, $gray-400); min-width: $gl-spacing-scale-5; &-head { - @include gl-absolute; top: -$gl-spacing-scale-2; left: calc(100% - #{$gl-spacing-scale-3} - #{2 * $stroke-size}); - border-color: var(--gray-400, $gray-400); - @include gl-border-solid; border-width: 0 $stroke-size $stroke-size 0; - @include gl-display-inline-block; - @include gl-p-2; transform: rotate(-45deg); } } diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 0f9ecc60648..001f5242138 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -5,7 +5,7 @@ class Admin::GroupsController < Admin::ApplicationController before_action :group, only: [:edit, :update, :destroy, :project_update, :members_update] - feature_category :subgroups, [:create, :destroy, :edit, :index, :members_update, :new, :show, :update] + feature_category :groups_and_projects, [:create, :destroy, :edit, :index, :members_update, :new, :show, :update] def index @groups = groups.sort_by_attribute(@sort = params[:sort]) diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 84eb90ce334..e79a899cee7 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -6,7 +6,7 @@ class Admin::ProjectsController < Admin::ApplicationController before_action :project, only: [:show, :transfer, :repository_check, :destroy, :edit, :update] before_action :group, only: [:show, :transfer] - feature_category :projects, [:index, :show, :transfer, :destroy, :edit, :update] + feature_category :groups_and_projects, [:index, :show, :transfer, :destroy, :edit, :update] feature_category :source_code_management, [:repository_check] def index diff --git a/app/controllers/admin/topics/avatars_controller.rb b/app/controllers/admin/topics/avatars_controller.rb index 7acdec424b4..ee0a7e68bb3 100644 --- a/app/controllers/admin/topics/avatars_controller.rb +++ b/app/controllers/admin/topics/avatars_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Admin::Topics::AvatarsController < Admin::ApplicationController - feature_category :projects + feature_category :groups_and_projects def destroy @topic = Projects::Topic.find(params[:topic_id]) diff --git a/app/controllers/admin/topics_controller.rb b/app/controllers/admin/topics_controller.rb index 94d084932ad..1f0664362a0 100644 --- a/app/controllers/admin/topics_controller.rb +++ b/app/controllers/admin/topics_controller.rb @@ -6,7 +6,11 @@ class Admin::TopicsController < Admin::ApplicationController before_action :topic, only: [:edit, :update, :destroy] - feature_category :projects + feature_category :groups_and_projects + + before_action do + push_frontend_feature_flag(:content_editor_on_issues, current_user) + end def index @topics = Projects::TopicsFinder.new(params: params.permit(:search)).execute.page(params[:page]).without_count diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index 01cc1ef21c6..c9cb1ca14e2 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -7,7 +7,7 @@ class AutocompleteController < ApplicationController before_action :check_search_rate_limit!, only: [:users, :projects] feature_category :user_profile, [:users, :user] - feature_category :projects, [:projects] + feature_category :groups_and_projects, [:projects] feature_category :team_planning, [:award_emojis] feature_category :code_review_workflow, [:merge_request_target_branches] feature_category :continuous_delivery, [:deploy_keys_with_owners] diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb index 552d74686d6..39bee37ee05 100644 --- a/app/controllers/dashboard/groups_controller.rb +++ b/app/controllers/dashboard/groups_controller.rb @@ -5,7 +5,7 @@ class Dashboard::GroupsController < Dashboard::ApplicationController skip_cross_project_access_check :index - feature_category :subgroups + feature_category :groups_and_projects urgency :low, [:index] diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb index e26ac083622..eee172995cf 100644 --- a/app/controllers/dashboard/projects_controller.rb +++ b/app/controllers/dashboard/projects_controller.rb @@ -14,7 +14,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController before_action :projects, only: [:index] skip_cross_project_access_check :index, :starred - feature_category :projects + feature_category :groups_and_projects urgency :low, [:starred, :index] def index diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb index 96a7b5b144d..6cb0736d7ef 100644 --- a/app/controllers/explore/groups_controller.rb +++ b/app/controllers/explore/groups_controller.rb @@ -3,7 +3,7 @@ class Explore::GroupsController < Explore::ApplicationController include GroupTree - feature_category :subgroups + feature_category :groups_and_projects urgency :low def index diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index eebcbe88ebf..d065fff8a55 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -23,7 +23,7 @@ class Explore::ProjectsController < Explore::ApplicationController rescue_from PageOutOfBoundsError, with: :page_out_of_bounds - feature_category :projects + feature_category :groups_and_projects # TODO: Set higher urgency after addressing https://gitlab.com/gitlab-org/gitlab/-/issues/357913 # and https://gitlab.com/gitlab-org/gitlab/-/issues/358945 urgency :low, [:index, :topics, :trending, :starred, :topic] diff --git a/app/controllers/groups/autocomplete_sources_controller.rb b/app/controllers/groups/autocomplete_sources_controller.rb index 3cad9e1fbad..414461d9e93 100644 --- a/app/controllers/groups/autocomplete_sources_controller.rb +++ b/app/controllers/groups/autocomplete_sources_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Groups::AutocompleteSourcesController < Groups::ApplicationController - feature_category :subgroups, [:members] + feature_category :groups_and_projects, [:members] feature_category :team_planning, [:issues, :labels, :milestones, :commands] feature_category :code_review_workflow, [:merge_requests] diff --git a/app/controllers/groups/avatars_controller.rb b/app/controllers/groups/avatars_controller.rb index 1f13be449a9..3b34a1947ae 100644 --- a/app/controllers/groups/avatars_controller.rb +++ b/app/controllers/groups/avatars_controller.rb @@ -5,7 +5,7 @@ class Groups::AvatarsController < Groups::ApplicationController skip_cross_project_access_check :destroy - feature_category :subgroups + feature_category :groups_and_projects def destroy @group.remove_avatar! diff --git a/app/controllers/groups/children_controller.rb b/app/controllers/groups/children_controller.rb index ca3be1542aa..98d7487b21a 100644 --- a/app/controllers/groups/children_controller.rb +++ b/app/controllers/groups/children_controller.rb @@ -9,7 +9,7 @@ module Groups skip_cross_project_access_check :index - feature_category :subgroups + feature_category :groups_and_projects # TODO: Set to higher urgency after resolving https://gitlab.com/gitlab-org/gitlab/-/issues/331494 urgency :low, [:index] diff --git a/app/controllers/groups/group_links_controller.rb b/app/controllers/groups/group_links_controller.rb index c74c48a960d..a874c7d164d 100644 --- a/app/controllers/groups/group_links_controller.rb +++ b/app/controllers/groups/group_links_controller.rb @@ -4,7 +4,7 @@ class Groups::GroupLinksController < Groups::ApplicationController before_action :authorize_admin_group! before_action :group_link, only: [:update, :destroy] - feature_category :subgroups + feature_category :groups_and_projects def update Groups::GroupLinks::UpdateService.new(@group_link, current_user).execute(group_link_params) diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index d614cc1cb24..de47c5fb5e3 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -24,7 +24,7 @@ class Groups::GroupMembersController < Groups::ApplicationController skip_cross_project_access_check :index, :update, :destroy, :request_access, :approve_access_request, :leave, :resend_invite, :override - feature_category :subgroups + feature_category :groups_and_projects urgency :low def index diff --git a/app/controllers/groups/shared_projects_controller.rb b/app/controllers/groups/shared_projects_controller.rb index 2d2664c02e8..73f951d6ce4 100644 --- a/app/controllers/groups/shared_projects_controller.rb +++ b/app/controllers/groups/shared_projects_controller.rb @@ -6,7 +6,7 @@ module Groups before_action :group skip_cross_project_access_check :index - feature_category :subgroups + feature_category :groups_and_projects urgency :low, [:index] def index diff --git a/app/controllers/groups/uploads_controller.rb b/app/controllers/groups/uploads_controller.rb index 22e6549aa04..cd1ebc39411 100644 --- a/app/controllers/groups/uploads_controller.rb +++ b/app/controllers/groups/uploads_controller.rb @@ -9,7 +9,7 @@ class Groups::UploadsController < Groups::ApplicationController before_action :authorize_upload_file!, only: [:create, :authorize] before_action :verify_workhorse_api!, only: [:authorize] - feature_category :subgroups + feature_category :groups_and_projects urgency :low, [:show] private diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 182d7aee060..ec16be8f85e 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -52,14 +52,12 @@ class GroupsController < Groups::ApplicationController layout :determine_layout - feature_category :subgroups, [ + feature_category :groups_and_projects, [ :index, :new, :create, :show, :edit, :update, - :destroy, :details, :transfer, :activity + :destroy, :details, :transfer, :activity, :projects ] - feature_category :team_planning, [:issues, :issues_calendar, :preview_markdown] feature_category :code_review_workflow, [:merge_requests, :unfoldered_environment_names] - feature_category :projects, [:projects] feature_category :importers, [:export, :download_export] urgency :low, [:export, :download_export] diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb index 5db7609e07a..3728406afd3 100644 --- a/app/controllers/projects/avatars_controller.rb +++ b/app/controllers/projects/avatars_controller.rb @@ -5,7 +5,7 @@ class Projects::AvatarsController < Projects::ApplicationController before_action :authorize_admin_project!, only: [:destroy] - feature_category :projects + feature_category :groups_and_projects urgency :low, [:show] diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb index bb1b8760c42..f621adbebc7 100644 --- a/app/controllers/projects/blame_controller.rb +++ b/app/controllers/projects/blame_controller.rb @@ -9,6 +9,7 @@ class Projects::BlameController < Projects::ApplicationController before_action :assign_ref_vars before_action :authorize_read_code! before_action :load_blob + before_action :require_non_binary_blob feature_category :source_code_management urgency :low, [:show] @@ -40,6 +41,10 @@ class Projects::BlameController < Projects::ApplicationController redirect_to_tree_root_for_missing_path(@project, @ref, @path) end + def require_non_binary_blob + redirect_to project_blob_path(@project, File.join(@ref, @path)), notice: _('Blame for binary files is not supported.') if @blob.binary? + end + def load_environment environment_params = @repository.branch_exists?(@ref) ? { ref: @ref } : { commit: @commit } environment_params[:find_latest] = true diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb index 451f1d1363b..60300f78bbb 100644 --- a/app/controllers/projects/group_links_controller.rb +++ b/app/controllers/projects/group_links_controller.rb @@ -6,7 +6,7 @@ class Projects::GroupLinksController < Projects::ApplicationController before_action :authorize_admin_project_group_link!, only: [:destroy] before_action :authorize_admin_project_member!, only: [:update] - feature_category :subgroups + feature_category :groups_and_projects def update Projects::GroupLinks::UpdateService.new(group_link, current_user).execute(group_link_params) diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index f4b96177b0f..5390df449e8 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -8,7 +8,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController # Authorize before_action :authorize_admin_project_member!, except: [:index, :leave, :request_access] - feature_category :projects + feature_category :groups_and_projects urgency :low def index diff --git a/app/controllers/projects/redirect_controller.rb b/app/controllers/projects/redirect_controller.rb index 6bcbe87ee42..c66a99be02b 100644 --- a/app/controllers/projects/redirect_controller.rb +++ b/app/controllers/projects/redirect_controller.rb @@ -6,7 +6,7 @@ class Projects::RedirectController < ::ApplicationController skip_before_action :authenticate_user! - feature_category :projects + feature_category :groups_and_projects def redirect_from_id project = Project.find(params[:id]) diff --git a/app/controllers/projects/starrers_controller.rb b/app/controllers/projects/starrers_controller.rb index 06996e8e5fc..f9f71ce72d3 100644 --- a/app/controllers/projects/starrers_controller.rb +++ b/app/controllers/projects/starrers_controller.rb @@ -3,7 +3,7 @@ class Projects::StarrersController < Projects::ApplicationController include SortingHelper - feature_category :projects + feature_category :groups_and_projects urgency :low, [:index] diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index a612c1e89a5..4fa7faa9094 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -50,7 +50,7 @@ class ProjectsController < Projects::ApplicationController layout :determine_layout - feature_category :projects, [ + feature_category :groups_and_projects, [ :index, :show, :new, :create, :edit, :update, :transfer, :destroy, :archive, :unarchive, :toggle_star, :activity ] diff --git a/app/graphql/types/ci/job_artifact_type.rb b/app/graphql/types/ci/job_artifact_type.rb index 6346d50de3a..2280fcd1370 100644 --- a/app/graphql/types/ci/job_artifact_type.rb +++ b/app/graphql/types/ci/job_artifact_type.rb @@ -19,7 +19,7 @@ module Types description: 'File name of the artifact.', method: :filename - field :size, GraphQL::Types::Int, null: false, + field :size, GraphQL::Types::BigInt, null: false, description: 'Size of the artifact in bytes.' field :expire_at, Types::TimeType, null: true, diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb index a715277a787..5d51b2562c7 100644 --- a/app/models/packages/package.rb +++ b/app/models/packages/package.rb @@ -225,6 +225,10 @@ class Packages::Package < ApplicationRecord find_by!(name: name, version: version) end + def self.debian_incoming_package! + find_by!(name: Packages::Debian::INCOMING_PACKAGE_NAME, version: nil, package_type: :debian, status: :default) + end + def self.existing_debian_packages_with(name:, version:) debian.with_name(name) .with_version(version) diff --git a/app/models/plan_limits.rb b/app/models/plan_limits.rb index 2ce02f9410d..16a2d6df6aa 100644 --- a/app/models/plan_limits.rb +++ b/app/models/plan_limits.rb @@ -10,6 +10,7 @@ class PlanLimits < ApplicationRecord belongs_to :plan validates :notification_limit, numericality: { only_integer: true } + validates :enforcement_limit, numericality: { only_integer: true } def exceeded?(limit_name, subject, alternate_limit: 0) limit = limit_for(limit_name, alternate_limit: alternate_limit) diff --git a/app/services/packages/debian/create_package_file_service.rb b/app/services/packages/debian/create_package_file_service.rb index 24e40b5c986..2e9299a847c 100644 --- a/app/services/packages/debian/create_package_file_service.rb +++ b/app/services/packages/debian/create_package_file_service.rb @@ -14,7 +14,7 @@ module Packages raise ArgumentError, "Invalid user" unless current_user.present? # Debian package file are first uploaded to incoming with empty metadata, - # and are moved later by Packages::Debian::ProcessChangesService + # and are moved later by Packages::Debian::ProcessPackageFileService package_file = package.package_files.create!( file: params[:file], size: params[:file]&.size, @@ -29,14 +29,12 @@ module Packages } ) - if params[:distribution].present? && params[:component].present? + if end_of_new_upload? ::Packages::Debian::ProcessPackageFileWorker.perform_async( package_file.id, params[:distribution], params[:component] ) - elsif params[:file_name].end_with? '.changes' - ::Packages::Debian::ProcessChangesWorker.perform_async(package_file.id, current_user.id) end package_file @@ -45,6 +43,10 @@ module Packages private attr_reader :package, :current_user, :params + + def end_of_new_upload? + params[:distribution].present? || params[:file_name].end_with?('.changes') + end end end end diff --git a/app/services/packages/debian/extract_changes_metadata_service.rb b/app/services/packages/debian/extract_changes_metadata_service.rb index 43a4db5bdfc..fdca8c88fdc 100644 --- a/app/services/packages/debian/extract_changes_metadata_service.rb +++ b/app/services/packages/debian/extract_changes_metadata_service.rb @@ -101,12 +101,17 @@ module Packages def entries_from_package_files @entries.each do |filename, entry| - entry.package_file = ::Packages::PackageFileFinder.new(@package_file.package, filename).execute! + entry.package_file = ::Packages::PackageFileFinder.new(incoming, filename).execute! entry.validate! rescue ActiveRecord::RecordNotFound raise ExtractionError, "#{filename} is listed in Files but was not uploaded" end end + + def incoming + @package_file.package.project.packages.debian_incoming_package! + end + strong_memoize_attr(:incoming) end end end diff --git a/app/services/packages/debian/process_package_file_service.rb b/app/services/packages/debian/process_package_file_service.rb index f4fcd3a563c..684192f6006 100644 --- a/app/services/packages/debian/process_package_file_service.rb +++ b/app/services/packages/debian/process_package_file_service.rb @@ -10,6 +10,8 @@ module Packages # used by ExclusiveLeaseGuard DEFAULT_LEASE_TIMEOUT = 1.hour.to_i.freeze + SIMPLE_DEB_FILE_TYPES = %i[deb udeb ddeb].freeze + def initialize(package_file, distribution_name, component_name) @package_file = package_file @distribution_name = distribution_name @@ -22,9 +24,10 @@ module Packages validate! try_obtain_lease do - package.transaction do + distribution.transaction do rename_package_and_set_version update_package + update_files_metadata if changes_file? update_file_metadata cleanup_temp_package end @@ -36,28 +39,61 @@ module Packages private def validate! - raise ArgumentError, 'missing distribution name' unless @distribution_name.present? - raise ArgumentError, 'missing component name' unless @component_name.present? raise ArgumentError, 'package file without Debian metadata' unless @package_file.debian_file_metadatum raise ArgumentError, 'already processed package file' unless @package_file.debian_file_metadatum.unknown? - if file_metadata[:file_type] == :deb || file_metadata[:file_type] == :udeb || file_metadata[:file_type] == :ddeb - return - end + changes_file? ? validate_changes_file! : validate_package_file! + end + + def changes_file? + @package_file.file_name.end_with?('.changes') + end + + def validate_changes_file! + raise ArgumentError, 'unwanted distribution name' unless @distribution_name.nil? + raise ArgumentError, 'unwanted component name' unless @component_name.nil? + raise ArgumentError, 'missing Source field' unless file_metadata.dig(:fields, 'Source').present? + raise ArgumentError, 'missing Version field' unless file_metadata.dig(:fields, 'Version').present? + raise ArgumentError, 'missing Distribution field' unless file_metadata.dig(:fields, 'Distribution').present? + end + + def validate_package_file! + raise ArgumentError, 'missing distribution name' unless @distribution_name.present? + raise ArgumentError, 'missing component name' unless @component_name.present? + + return if SIMPLE_DEB_FILE_TYPES.include?(file_metadata[:file_type]) raise ArgumentError, "invalid package file type: #{file_metadata[:file_type]}" end def file_metadata - ::Packages::Debian::ExtractMetadataService.new(@package_file).execute + metadata_service_class.new(@package_file).execute end strong_memoize_attr :file_metadata + def metadata_service_class + changes_file? ? ::Packages::Debian::ExtractChangesMetadataService : ::Packages::Debian::ExtractMetadataService + end + + def distribution + Packages::Debian::DistributionsFinder.new( + @package_file.package.project, + codename_or_suite: package_distribution + ).execute.last! + end + strong_memoize_attr :distribution + + def package_distribution + return file_metadata[:fields]['Distribution'] if changes_file? + + @distribution_name + end + def package packages = temp_package.project .packages .existing_debian_packages_with(name: package_name, version: package_version) - package = packages.with_debian_codename_or_suite(@distribution_name) + package = packages.with_debian_codename_or_suite(package_distribution) .first unless package @@ -79,10 +115,14 @@ module Packages strong_memoize_attr :temp_package def package_name + return file_metadata[:fields]['Source'] if changes_file? + package_name_and_version[0] end def package_version + return file_metadata[:fields]['Version'] if changes_file? + package_name_and_version[1] end @@ -121,13 +161,24 @@ module Packages package.id == temp_package.id end - def distribution - Packages::Debian::DistributionsFinder.new( - @package_file.package.project, - codename_or_suite: @distribution_name - ).execute.last! + def update_files_metadata + file_metadata[:files].each do |_, entry| + file_metadata = ::Packages::Debian::ExtractMetadataService.new(entry.package_file).execute + + ::Packages::UpdatePackageFileService.new(entry.package_file, package_id: package.id) + .execute + + # Force reload from database, as package has changed + entry.package_file.reload_package + + entry.package_file.debian_file_metadatum.update!( + file_type: file_metadata[:file_type], + component: entry.component, + architecture: file_metadata[:architecture], + fields: file_metadata[:fields] + ) + end end - strong_memoize_attr :distribution def update_file_metadata ::Packages::UpdatePackageFileService.new(@package_file, package_id: package.id) @@ -150,7 +201,7 @@ module Packages # used by ExclusiveLeaseGuard def lease_key - "packages:debian:process_package_file_service:package_file:#{@package_file.id}" + "packages:debian:process_package_file_service:#{temp_package.project_id}_#{package_name}_#{package_version}" end # used by ExclusiveLeaseGuard diff --git a/app/views/admin/topics/_form.html.haml b/app/views/admin/topics/_form.html.haml index 544310e312c..c3b5161d617 100644 --- a/app/views/admin/topics/_form.html.haml +++ b/app/views/admin/topics/_form.html.haml @@ -18,14 +18,15 @@ .form-group = f.label :description, _("Description") - = render layout: 'shared/md_preview', locals: { url: preview_markdown_admin_topics_path, referenced_users: false } do - = render 'shared/zen', f: f, attr: :description, - classes: 'note-textarea', - placeholder: _('Write a description…'), - supports_quick_actions: false, - supports_autocomplete: false, - qa_selector: 'topic_form_description' - = render 'shared/notes/hints', supports_file_upload: false + .js-markdown-editor{ data: { render_markdown_path: preview_markdown_admin_topics_path, + markdown_docs_path: help_page_path('user/markdown'), + qa_selector: 'topic_form_description', + form_field_placeholder: _('Write a description…'), + supports_quick_actions: 'false', + enable_autocomplete: 'false', + disable_attachments: 'true', + form_field_classes: 'note-textarea js-gfm-input markdown-area' } } + = f.hidden_field :description .form-group.gl-mt-3.gl-mb-3 = f.label :avatar, _('Topic avatar'), class: 'gl-display-block' diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index 056c76f6326..e6ad18181cd 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -554,7 +554,7 @@ :tags: [] - :name: cronjob:member_invitation_reminder_emails :worker_name: MemberInvitationReminderEmailsWorker - :feature_category: :subgroups + :feature_category: :groups_and_projects :has_external_dependencies: false :urgency: :low :resource_boundary: :unknown @@ -2642,7 +2642,7 @@ :tags: [] - :name: disallow_two_factor_for_group :worker_name: DisallowTwoFactorForGroupWorker - :feature_category: :subgroups + :feature_category: :groups_and_projects :has_external_dependencies: false :urgency: :low :resource_boundary: :unknown @@ -2651,7 +2651,7 @@ :tags: [] - :name: disallow_two_factor_for_subgroups :worker_name: DisallowTwoFactorForSubgroupsWorker - :feature_category: :subgroups + :feature_category: :groups_and_projects :has_external_dependencies: false :urgency: :low :resource_boundary: :unknown @@ -2786,7 +2786,7 @@ :tags: [] - :name: group_destroy :worker_name: GroupDestroyWorker - :feature_category: :subgroups + :feature_category: :groups_and_projects :has_external_dependencies: false :urgency: :low :resource_boundary: :unknown @@ -3398,7 +3398,7 @@ :tags: [] - :name: projects_record_target_platforms :worker_name: Projects::RecordTargetPlatformsWorker - :feature_category: :projects + :feature_category: :groups_and_projects :has_external_dependencies: false :urgency: :low :resource_boundary: :unknown diff --git a/app/workers/disallow_two_factor_for_group_worker.rb b/app/workers/disallow_two_factor_for_group_worker.rb index 5b958f9f31f..1ee2585a718 100644 --- a/app/workers/disallow_two_factor_for_group_worker.rb +++ b/app/workers/disallow_two_factor_for_group_worker.rb @@ -8,7 +8,7 @@ class DisallowTwoFactorForGroupWorker sidekiq_options retry: 3 include ExceptionBacktrace - feature_category :subgroups + feature_category :groups_and_projects idempotent! def perform(group_id) diff --git a/app/workers/disallow_two_factor_for_subgroups_worker.rb b/app/workers/disallow_two_factor_for_subgroups_worker.rb index 500c13deed2..02dceee2488 100644 --- a/app/workers/disallow_two_factor_for_subgroups_worker.rb +++ b/app/workers/disallow_two_factor_for_subgroups_worker.rb @@ -10,7 +10,7 @@ class DisallowTwoFactorForSubgroupsWorker INTERVAL = 2.seconds.to_i - feature_category :subgroups + feature_category :groups_and_projects idempotent! def perform(group_id) diff --git a/app/workers/group_destroy_worker.rb b/app/workers/group_destroy_worker.rb index a116944feb9..d8dd0b6d7b3 100644 --- a/app/workers/group_destroy_worker.rb +++ b/app/workers/group_destroy_worker.rb @@ -8,7 +8,7 @@ class GroupDestroyWorker sidekiq_options retry: 3 include ExceptionBacktrace - feature_category :subgroups + feature_category :groups_and_projects idempotent! deduplicate :until_executed, ttl: 2.hours diff --git a/app/workers/member_invitation_reminder_emails_worker.rb b/app/workers/member_invitation_reminder_emails_worker.rb index a7614db30f6..d36ec2611d5 100644 --- a/app/workers/member_invitation_reminder_emails_worker.rb +++ b/app/workers/member_invitation_reminder_emails_worker.rb @@ -7,7 +7,7 @@ class MemberInvitationReminderEmailsWorker # rubocop:disable Scalability/Idempot include CronjobQueue # rubocop:disable Scalability/CronWorkerContext - feature_category :subgroups + feature_category :groups_and_projects urgency :low def perform diff --git a/app/workers/packages/debian/process_package_file_worker.rb b/app/workers/packages/debian/process_package_file_worker.rb index 8e7f0b3b987..0e21e98d182 100644 --- a/app/workers/packages/debian/process_package_file_worker.rb +++ b/app/workers/packages/debian/process_package_file_worker.rb @@ -19,7 +19,7 @@ module Packages @distribution_name = distribution_name @component_name = component_name - return unless package_file && distribution_name && component_name + return unless package_file # return if file has already been processed return unless package_file.debian_file_metadatum&.unknown? diff --git a/app/workers/projects/record_target_platforms_worker.rb b/app/workers/projects/record_target_platforms_worker.rb index 2e523ccc992..9ebc52f77d3 100644 --- a/app/workers/projects/record_target_platforms_worker.rb +++ b/app/workers/projects/record_target_platforms_worker.rb @@ -8,7 +8,7 @@ module Projects LEASE_TIMEOUT = 1.hour.to_i APPLE_PLATFORM_LANGUAGES = %w(swift objective-c).freeze - feature_category :projects + feature_category :groups_and_projects data_consistency :always deduplicate :until_executed urgency :low |