diff options
30 files changed, 221 insertions, 252 deletions
diff --git a/.rubocop_todo/rspec/before_all_role_assignment.yml b/.rubocop_todo/rspec/before_all_role_assignment.yml index 755eb0126e7..35906845e95 100644 --- a/.rubocop_todo/rspec/before_all_role_assignment.yml +++ b/.rubocop_todo/rspec/before_all_role_assignment.yml @@ -73,6 +73,7 @@ RSpec/BeforeAllRoleAssignment: - 'ee/spec/features/boards/sidebar_spec.rb' - 'ee/spec/features/boards/swimlanes/epics_swimlanes_sidebar_spec.rb' - 'ee/spec/features/burnup_charts_spec.rb' + - 'ee/spec/features/ci/ci_catalog_spec.rb' - 'ee/spec/features/dashboards/todos_spec.rb' - 'ee/spec/features/epic_boards/epic_boards_sidebar_spec.rb' - 'ee/spec/features/epic_boards/epic_boards_spec.rb' @@ -317,6 +318,7 @@ RSpec/BeforeAllRoleAssignment: - 'ee/spec/lib/gitlab/elastic/project_search_results_spec.rb' - 'ee/spec/lib/gitlab/git_access_spec.rb' - 'ee/spec/lib/gitlab/git_access_wiki_spec.rb' + - 'ee/spec/lib/gitlab/llm/chain/agents/zero_shot/executor_spec.rb' - 'ee/spec/lib/gitlab/llm/chain/tools/gitlab_documentation/executor_spec.rb' - 'ee/spec/lib/gitlab/llm/chain/tools/issue_identifier/executor_spec.rb' - 'ee/spec/lib/gitlab/llm/chain/tools/summarize_comments/executor_spec.rb' @@ -568,6 +570,7 @@ RSpec/BeforeAllRoleAssignment: - 'ee/spec/requests/groups/protected_branches_controller_spec.rb' - 'ee/spec/requests/groups/security/compliance_framework_reports_controller_spec.rb' - 'ee/spec/requests/groups/security/credentials_controller_spec.rb' + - 'ee/spec/requests/groups/service_accounts_controller_spec.rb' - 'ee/spec/requests/groups/settings/merge_requests_controller_spec.rb' - 'ee/spec/requests/groups/two_factor_auths_controller_spec.rb' - 'ee/spec/requests/jwt_controller_spec.rb' @@ -792,6 +795,7 @@ RSpec/BeforeAllRoleAssignment: - 'spec/controllers/projects/analytics/cycle_analytics/value_streams_controller_spec.rb' - 'spec/controllers/projects/artifacts_controller_spec.rb' - 'spec/controllers/projects/blame_controller_spec.rb' + - 'spec/controllers/projects/blob_controller_spec.rb' - 'spec/controllers/projects/ci/lints_controller_spec.rb' - 'spec/controllers/projects/ci/pipeline_editor_controller_spec.rb' - 'spec/controllers/projects/clusters_controller_spec.rb' @@ -827,6 +831,7 @@ RSpec/BeforeAllRoleAssignment: - 'spec/controllers/projects/settings/slacks_controller_spec.rb' - 'spec/controllers/projects/snippets_controller_spec.rb' - 'spec/controllers/projects/terraform_controller_spec.rb' + - 'spec/controllers/projects/tree_controller_spec.rb' - 'spec/controllers/projects/usage_quotas_controller_spec.rb' - 'spec/controllers/projects/web_ide_schemas_controller_spec.rb' - 'spec/controllers/projects/work_items_controller_spec.rb' @@ -938,6 +943,7 @@ RSpec/BeforeAllRoleAssignment: - 'spec/features/projects/releases/user_views_releases_spec.rb' - 'spec/features/projects/settings/access_tokens_spec.rb' - 'spec/features/projects/settings/branch_names_settings_spec.rb' + - 'spec/features/projects/settings/service_desk_setting_spec.rb' - 'spec/features/projects/show/clone_button_spec.rb' - 'spec/features/projects/snippets/user_comments_on_snippet_spec.rb' - 'spec/features/projects/snippets/user_updates_snippet_spec.rb' @@ -1004,6 +1010,7 @@ RSpec/BeforeAllRoleAssignment: - 'spec/finders/user_group_notification_settings_finder_spec.rb' - 'spec/frontend/fixtures/autocomplete.rb' - 'spec/frontend/fixtures/autocomplete_sources.rb' + - 'spec/frontend/fixtures/groups.rb' - 'spec/frontend/fixtures/metrics_dashboard.rb' - 'spec/frontend/fixtures/milestones.rb' - 'spec/frontend/fixtures/pipelines.rb' @@ -1378,7 +1385,6 @@ RSpec/BeforeAllRoleAssignment: - 'spec/requests/api/rubygem_packages_spec.rb' - 'spec/requests/api/search_spec.rb' - 'spec/requests/api/terraform/modules/v1/packages_spec.rb' - - 'spec/requests/api/users_spec.rb' - 'spec/requests/api/v3/github_spec.rb' - 'spec/requests/api/wikis_spec.rb' - 'spec/requests/concerns/planning_hierarchy_spec.rb' @@ -1466,7 +1472,6 @@ RSpec/BeforeAllRoleAssignment: - 'spec/services/groups/merge_requests_count_service_spec.rb' - 'spec/services/groups/open_issues_count_service_spec.rb' - 'spec/services/groups/transfer_service_spec.rb' - - 'spec/services/groups/update_shared_runners_service_spec.rb' - 'spec/services/ide/base_config_service_spec.rb' - 'spec/services/ide/schemas_config_service_spec.rb' - 'spec/services/ide/terminal_config_service_spec.rb' diff --git a/app/assets/javascripts/content_editor/components/formatting_toolbar.vue b/app/assets/javascripts/content_editor/components/formatting_toolbar.vue index 8bf71a481bd..b6c16e9f306 100644 --- a/app/assets/javascripts/content_editor/components/formatting_toolbar.vue +++ b/app/assets/javascripts/content_editor/components/formatting_toolbar.vue @@ -1,4 +1,5 @@ <script> +import CommentTemplatesDropdown from '~/vue_shared/components/markdown/comment_templates_dropdown.vue'; import trackUIControl from '../services/track_ui_control'; import ToolbarButton from './toolbar_button.vue'; import ToolbarAttachmentButton from './toolbar_attachment_button.vue'; @@ -13,6 +14,11 @@ export default { ToolbarTableButton, ToolbarAttachmentButton, ToolbarMoreDropdown, + CommentTemplatesDropdown, + }, + inject: { + newCommentTemplatePath: { default: null }, + tiptapEditor: { default: null }, }, props: { supportsQuickActions: { @@ -30,6 +36,9 @@ export default { trackToolbarControlExecution({ contentType, value }) { trackUIControl({ property: contentType, value }); }, + insertSavedReply(savedReply) { + this.tiptapEditor.chain().focus().pasteContent(savedReply).run(); + }, }, }; </script> @@ -131,11 +140,16 @@ export default { data-testid="quick-actions" content-type="quickAction" icon-name="quick-actions" - class="gl-display-none gl-sm-display-inline gl-mr-1!" + class="gl-display-none gl-sm-display-inline" editor-command="insertQuickAction" :label="__('Add a quick action')" @execute="trackToolbarControlExecution" /> + <comment-templates-dropdown + v-if="newCommentTemplatePath" + :new-comment-template-path="newCommentTemplatePath" + @select="insertSavedReply" + /> <toolbar-more-dropdown data-testid="more" @execute="trackToolbarControlExecution" /> </div> </div> diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js index c113bb24fb5..575cd05ceb8 100644 --- a/app/assets/javascripts/diffs/constants.js +++ b/app/assets/javascripts/diffs/constants.js @@ -117,4 +117,3 @@ export const TRACKING_MULTIPLE_FILES_MODE = 'i_code_review_diff_multiple_files'; // UI export const ZERO_CHANGES_ALT_DISPLAY = '-'; -export const STICKY_FILE_HEADER_HEIGHT = 40; diff --git a/app/assets/javascripts/mr_more_dropdown.js b/app/assets/javascripts/mr_more_dropdown.js index 2a22547e763..ca50028a93a 100644 --- a/app/assets/javascripts/mr_more_dropdown.js +++ b/app/assets/javascripts/mr_more_dropdown.js @@ -35,6 +35,7 @@ export const initMrMoreDropdown = () => { el, provide: { reportAbusePath: el.dataset.reportAbusePath, + showSummaryNotesToggle: Boolean(document.querySelector('#js-summary-notes')), }, render: (createElement) => createElement(MrMoreDropdown, { diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue index babe005a1f3..50262e81f1c 100644 --- a/app/assets/javascripts/notes/components/noteable_note.vue +++ b/app/assets/javascripts/notes/components/noteable_note.vue @@ -5,7 +5,7 @@ import { escape, isEmpty } from 'lodash'; import { mapGetters, mapActions } from 'vuex'; import SafeHtml from '~/vue_shared/directives/safe_html'; import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'; -import { INLINE_DIFF_LINES_KEY, STICKY_FILE_HEADER_HEIGHT } from '~/diffs/constants'; +import { INLINE_DIFF_LINES_KEY } from '~/diffs/constants'; import { createAlert } from '~/alert'; import { HTTP_STATUS_GONE } from '~/lib/utils/http_status'; import { ignoreWhilePending } from '~/lib/utils/ignore_while_pending'; @@ -14,7 +14,6 @@ import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item import { __, s__, sprintf } from '~/locale'; import { renderGFM } from '~/behaviors/markdown/render_gfm'; import { containsSensitiveToken, confirmSensitiveAction } from '~/lib/utils/secret_detection'; -import { scrollToElement } from '~/lib/utils/common_utils'; import eventHub from '../event_hub'; import noteable from '../mixins/noteable'; import resolvable from '../mixins/resolvable'; @@ -235,13 +234,12 @@ export default { this.scrollToNoteIfNeeded($(this.$el)); } }); + }, - eventHub.$on('scrollToNote', () => { - if (this.isTarget && this.shouldScrollToNote) { - const offset = this.line ? STICKY_FILE_HEADER_HEIGHT * -1 : 0; - setTimeout(() => scrollToElement(this.$el, { offset, behavior: 'instant' }), 1500); - } - }); + mounted() { + if (this.isTarget && this.shouldScrollToNote) { + this.scrollToNoteIfNeeded($(this.$el)); + } }, methods: { @@ -422,10 +420,7 @@ export default { <template> <timeline-entry-item :id="noteAnchorId" - :class="{ - ...classNameBindings, - 'internal-note': note.internal, - }" + :class="{ ...classNameBindings, 'internal-note': note.internal }" :data-award-url="note.toggle_award_path" :data-note-id="note.id" class="note note-wrapper note-comment" diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue index dcafdbffec4..06c925002b6 100644 --- a/app/assets/javascripts/notes/components/notes_app.vue +++ b/app/assets/javascripts/notes/components/notes_app.vue @@ -165,7 +165,6 @@ export default { updated() { this.$nextTick(() => { highlightCurrentUser(this.$el.querySelectorAll('.gfm-project_member')); - eventHub.$emit('scrollToNote'); }); }, beforeDestroy() { diff --git a/app/assets/javascripts/pages/projects/merge_requests/diffs/index.js b/app/assets/javascripts/pages/projects/merge_requests/diffs/index.js index 77294c0fb9e..b15e9a14b6a 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/diffs/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/diffs/index.js @@ -1,5 +1,5 @@ import initDiffsApp from '~/diffs'; -import { initMrPage } from '../page'; +import { initMrPage } from 'ee_else_ce/pages/projects/merge_requests/page'; initMrPage(); initDiffsApp(); diff --git a/app/assets/javascripts/pages/projects/merge_requests/show/index.js b/app/assets/javascripts/pages/projects/merge_requests/show/index.js index 67dc3782a24..1cc7e892ca0 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/show/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/show/index.js @@ -1,7 +1,7 @@ import mountNotesApp from 'ee_else_ce/mr_notes/mount_app'; import { initReportAbuse } from '~/projects/report_abuse'; import { initMrMoreDropdown } from '~/mr_more_dropdown'; -import { initMrPage } from '../page'; +import { initMrPage } from 'ee_else_ce/pages/projects/merge_requests/page'; initMrPage(); mountNotesApp(); diff --git a/app/assets/javascripts/vue_shared/components/markdown/comment_templates_dropdown.vue b/app/assets/javascripts/vue_shared/components/markdown/comment_templates_dropdown.vue index cc9790279cd..966a5556d24 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/comment_templates_dropdown.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/comment_templates_dropdown.vue @@ -1,7 +1,6 @@ <script> import { GlCollapsibleListbox, GlTooltip, GlButton } from '@gitlab/ui'; import fuzzaldrinPlus from 'fuzzaldrin-plus'; -import { updateText } from '~/lib/utils/text_markdown'; import savedRepliesQuery from './saved_replies.query.graphql'; export default { @@ -54,20 +53,8 @@ export default { }, onSelect(id) { const savedReply = this.savedReplies.find((r) => r.id === id); - const textArea = this.$el.closest('.md-area')?.querySelector('textarea'); - - if (savedReply && textArea) { - updateText({ - textArea, - tag: savedReply.content, - cursorOffset: 0, - wrap: false, - }); - - // Wait for text to be added into textarea - requestAnimationFrame(() => { - textArea.focus(); - }); + if (savedReply) { + this.$emit('select', savedReply.content); } }, }, @@ -105,7 +92,7 @@ export default { </template> <template #footer> <div - class="gl-border-t-solid gl-border-t-1 gl-border-t-gray-100 gl-display-flex gl-justify-content-center gl-p-3" + class="gl-border-t-solid gl-border-t-1 gl-border-t-gray-200 gl-display-flex gl-justify-content-center gl-p-2" > <gl-button :href="newCommentTemplatePath" @@ -131,4 +118,8 @@ export default { .comment-template-dropdown .gl-new-dropdown-item-check-icon { display: none; } + +.comment-template-dropdown input { + border-radius: 0; +} </style> diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue index 758254b3cc0..c794ed951ef 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/header.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue @@ -195,10 +195,9 @@ export default { insertIntoTextarea(text) { const textArea = this.$el.closest('.md-area')?.querySelector('textarea'); if (textArea) { - const generatedByText = `${text}\n\n---\n\n_${__('This comment was generated by AI')}_`; updateText({ textArea, - tag: generatedByText, + tag: text, cursorOffset: 0, wrap: false, }); @@ -228,6 +227,12 @@ export default { this.showMarkdownPreview(); } }, + insertAIAction(text) { + this.insertIntoTextarea(`${text}\n\n---\n\n_${__('This comment was generated by AI')}_`); + }, + insertSavedReply(savedReply) { + this.insertIntoTextarea(savedReply); + }, }, shortcuts: { bold: keysFor(BOLD_TEXT), @@ -311,7 +316,7 @@ export default { <ai-actions-dropdown v-if="editorAiActions.length" :actions="editorAiActions" - @input="insertIntoTextarea" + @input="insertAIAction" @replace="replaceTextarea" /> <toolbar-button @@ -459,6 +464,7 @@ export default { <comment-templates-dropdown v-if="newCommentTemplatePath && glFeatures.savedReplies" :new-comment-template-path="newCommentTemplatePath" + @select="insertSavedReply" /> <div class="full-screen"> <gl-button diff --git a/app/assets/javascripts/vue_shared/components/mr_more_dropdown.vue b/app/assets/javascripts/vue_shared/components/mr_more_dropdown.vue index cfc998c57b1..a2a29241fb1 100644 --- a/app/assets/javascripts/vue_shared/components/mr_more_dropdown.vue +++ b/app/assets/javascripts/vue_shared/components/mr_more_dropdown.vue @@ -51,6 +51,8 @@ export default { SidebarSubscriptionsWidget, AbuseCategorySelector, NewHeaderActionsPopover, + SummaryNotesToggle: () => + import('ee_component/merge_requests/components/summary_notes_toggle.vue'), }, directives: { GlTooltip: GlTooltipDirective, @@ -60,6 +62,9 @@ export default { reportAbusePath: { default: '', }, + showSummaryNotesToggle: { + default: false, + }, }, props: { mr: { @@ -329,6 +334,8 @@ export default { {{ $options.i18n.copyReferenceText }} </template> </gl-disclosure-dropdown-item> + + <summary-notes-toggle v-if="showSummaryNotesToggle" @action="closeActionsDropdown" /> </gl-disclosure-dropdown-group> <gl-disclosure-dropdown-group diff --git a/config/feature_flags/development/ci_support_include_rules_when_never.yml b/config/feature_flags/development/ci_support_include_rules_when_never.yml deleted file mode 100644 index 594da30ec97..00000000000 --- a/config/feature_flags/development/ci_support_include_rules_when_never.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: ci_support_include_rules_when_never -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/122810 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/414517 -milestone: '16.1' -type: development -group: group::pipeline authoring -default_enabled: false diff --git a/doc/ci/yaml/includes.md b/doc/ci/yaml/includes.md index 69595b62de2..6a616854c0b 100644 --- a/doc/ci/yaml/includes.md +++ b/doc/ci/yaml/includes.md @@ -418,7 +418,8 @@ these keywords: ### `include` with `rules:if` -> Support for `when: never` and `when:always` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348146) in GitLab 16.1 [with a flag](../../administration/feature_flags.md) named `ci_support_include_rules_when_never`. Disabled by default. +> - Support for `when: never` and `when:always` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348146) in GitLab 16.1 [with a flag](../../administration/feature_flags.md) named `ci_support_include_rules_when_never`. Disabled by default. +> - Support for `when: never` and `when:always` [generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/414517) in GitLab 16.2. Feature flag `ci_support_include_rules_when_never` removed. Use [`rules:if`](index.md#rulesif) to conditionally include other configuration files based on the status of CI/CD variables. For example: @@ -447,7 +448,8 @@ test: ### `include` with `rules:exists` -> Support for `when: never` and `when:always` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348146) in GitLab 16.1 [with a flag](../../administration/feature_flags.md) named `ci_support_include_rules_when_never`. Disabled by default. +> - Support for `when: never` and `when:always` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348146) in GitLab 16.1 [with a flag](../../administration/feature_flags.md) named `ci_support_include_rules_when_never`. Disabled by default. +> - Support for `when: never` and `when:always` [generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/414517) in GitLab 16.2. Feature flag `ci_support_include_rules_when_never` removed. Use [`rules:exists`](index.md#rulesexists) to conditionally include other configuration files based on the existence of files. For example: diff --git a/doc/security/token_overview.md b/doc/security/token_overview.md index 80dbe8c03cc..8c8a3052eed 100644 --- a/doc/security/token_overview.md +++ b/doc/security/token_overview.md @@ -174,16 +174,25 @@ This table shows available scopes per token. Scopes can be limited further on to ## Security considerations -- Access tokens should be treated like passwords and kept secure. -- Adding access tokens to URLs is a security risk, especially when cloning or adding a remote because Git then writes the URL to its `.git/config` file in plain text. URLs are +1. Treat access tokens like passwords and keep them secure. +1. When creating a scoped token, consider using the most limited scope possible to reduce the impact of accidentally leaking the token. +1. When creating a token, consider setting a token that expires when your task is complete. For example, if performing a one-off import, set the + token to expire after a few hours or a day. This reduces the impact of a token that is accidentally leaked because it is useless when it expires. +1. If you are recording a video that might contain a sensitive secret like a personal access token (PAT), feed token, or trigger token, you must mask that secret before uploading the video to GitLab Unfiltered or any other video hosting service. As an additional defense-in-depth security measure, you must revoke those secrets before you share the video publicly. For more information, see [revoking a PAT](../user/profile/personal_access_tokens.md#revoke-a-personal-access-token). +1. Adding access tokens to URLs is a security risk, especially when cloning or adding a remote because Git then writes the URL to its `.git/config` file in plain text. URLs are also generally logged by proxies and application servers, which makes those credentials visible to system administrators. Instead, pass API calls an access token using headers like [the `Private-Token` header](../api/rest/index.md#personalprojectgroup-access-tokens). -- Tokens can also be stored using a [Git credential storage](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage). -- Tokens must not be committed to your source code. Instead, consider an approach such as [using external secrets in CI](../ci/secrets/index.md). -- When creating a scoped token, consider using the most limited scope possible to reduce the impact of accidentally leaking the token. -- When creating a token, consider setting a token that expires when your task is complete. For example, if performing a one-off import, set the - token to expire after a few hours or a day. This reduces the impact of a token that is accidentally leaked because it is useless when it expires. -- Be careful not to include tokens when pasting code, console commands, or log outputs into an issue or MR description or comment. -- Don’t log credentials in the console logs. Consider [protecting](../ci/variables/index.md#protect-a-cicd-variable) and +1. You can also store token using a [Git credential storage](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage). +1. Do not: + - Store tokens in plain text in your projects. + - Include tokens when pasting code, console commands, or log outputs into an issue, MR description, or comment. + Consider an approach such as [using external secrets in CI](../ci/secrets/index.md). +1. Do not log credentials in the console logs or artifacts. Consider [protecting](../ci/variables/index.md#protect-a-cicd-variable) and [masking](../ci/variables/index.md#mask-a-cicd-variable) your credentials. -- Review all currently active access tokens of all types on a regular basis and revoke any that are no longer needed. +1. If you have set up a demo environment to showcase a project you have been working on and you are recording a video or writing a blog post describing that project, make sure you are not leaking sensitive secrets during that process. If you are done with the demo, you must revoke all the secrets created during that demo. +1. Review all active access tokens of all types on a regular basis and revoke any that are no longer needed. This includes: + - Personal, project, and group access tokens. + - Feed tokens. + - Trigger tokens. + - Runner registration tokens. + - Any other sensitive secrets etc. diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md index 509029a3099..eeb3592cd24 100644 --- a/doc/user/project/import/github.md +++ b/doc/user/project/import/github.md @@ -35,7 +35,7 @@ When importing projects: - The organization the repository belongs to must not impose restrictions of a [third-party application access policy](https://docs.github.com/en/organizations/managing-oauth-access-to-your-organizations-data/about-oauth-app-access-restrictions) on the GitLab instance you import to. <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> -For an overview of the import process, see [Migrating from GitHub to GitLab](https://youtu.be/VYOXuOg9tQI). +For an overview of the import process, see [How to migrate from GitHub to GitLab including Actions](https://www.youtube.com/watch?v=0Id5oMl1Kqs). ## Prerequisites diff --git a/doc/user/project/merge_requests/approvals/rules.md b/doc/user/project/merge_requests/approvals/rules.md index bc5d4353ffc..2ea2b9469d5 100644 --- a/doc/user/project/merge_requests/approvals/rules.md +++ b/doc/user/project/merge_requests/approvals/rules.md @@ -42,7 +42,7 @@ To add a merge request approval rule: - To apply the rule to a specific branch, select it from the list. 1. Set the number of required approvals in **Approvals required**. A value of `0` makes [the rule optional](#configure-optional-approval-rules), and any number greater than `0` - creates a required rule. + creates a required rule. Maximum number of required approvals is `100`. 1. To add users or groups as approvers, search for users or groups that are [eligible to approve](#eligible-approvers), and select **Add**. GitLab suggests approvers based on previous authors of the files changed by the merge request. diff --git a/lib/api/concerns/packages/debian_package_endpoints.rb b/lib/api/concerns/packages/debian_package_endpoints.rb index 25c97932e31..45290cb3e44 100644 --- a/lib/api/concerns/packages/debian_package_endpoints.rb +++ b/lib/api/concerns/packages/debian_package_endpoints.rb @@ -13,7 +13,6 @@ module API component: ::Packages::Debian::COMPONENT_REGEX, architecture: ::Packages::Debian::ARCHITECTURE_REGEX }.freeze - LIST_PACKAGE = 'list_package' included do feature_category :package_registry @@ -41,8 +40,6 @@ module API package_file = distribution_from!(project).package_files.with_file_name(params[:file_name]).last! - track_debian_package_event 'pull_package' - present_package_file!(package_file) end @@ -73,22 +70,8 @@ module API no_content! # empty component files are not always persisted in DB end - track_debian_package_event LIST_PACKAGE - present_carrierwave_file!(component_file.file) end - - def track_debian_package_event(action) - if project_or_group.is_a?(Project) - project = project_or_group - namespace = project_or_group.namespace - else - project = nil - namespace = project_or_group - end - - track_package_event(action, :debian, project: project, namespace: namespace, user: current_user) - end end rescue_from ArgumentError do |e| @@ -146,7 +129,6 @@ module API get 'Release' do distribution = distribution_from!(project_or_group) - track_debian_package_event LIST_PACKAGE present_carrierwave_file!(distribution.file) end @@ -166,7 +148,6 @@ module API get 'InRelease' do distribution = distribution_from!(project_or_group) - track_debian_package_event LIST_PACKAGE present_carrierwave_file!(distribution.signed_file) end diff --git a/lib/api/debian_project_packages.rb b/lib/api/debian_project_packages.rb index e1531847b87..a2b2d781797 100644 --- a/lib/api/debian_project_packages.rb +++ b/lib/api/debian_project_packages.rb @@ -111,8 +111,6 @@ module API ::Packages::Debian::CreatePackageFileService.new(package: package, current_user: current_user, params: file_params).execute - track_debian_package_event 'push_package' - created! rescue ObjectStorage::RemoteStoreError => e Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: project_or_group.id }) diff --git a/lib/gitlab/ci/config/external/rules.rb b/lib/gitlab/ci/config/external/rules.rb index 134306332e6..59e666b8bb5 100644 --- a/lib/gitlab/ci/config/external/rules.rb +++ b/lib/gitlab/ci/config/external/rules.rb @@ -17,16 +17,12 @@ module Gitlab end def evaluate(context) - if Feature.enabled?(:ci_support_include_rules_when_never, context.project) - if @rule_list.nil? - Result.new('always') - elsif matched_rule = match_rule(context) - Result.new(matched_rule.attributes[:when]) - else - Result.new('never') - end + if @rule_list.nil? + Result.new('always') + elsif matched_rule = match_rule(context) + Result.new(matched_rule.attributes[:when]) else - LegacyResult.new(@rule_list.nil? || match_rule(context)) + Result.new('never') end end @@ -55,12 +51,6 @@ module Gitlab self.when != 'never' end end - - LegacyResult = Struct.new(:result) do - def pass? - !!result - end - end end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a7fcca6d652..06ed3e29b88 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -28564,6 +28564,9 @@ msgstr "" msgid "Merge request approvals" msgstr "" +msgid "Merge request change summary" +msgstr "" + msgid "Merge request commits" msgstr "" @@ -28582,6 +28585,9 @@ msgstr "" msgid "Merge request status" msgstr "" +msgid "Merge request summaries" +msgstr "" + msgid "Merge request summary" msgstr "" @@ -28822,6 +28828,12 @@ msgstr "" msgid "MergeRequest|Search (e.g. *.vue) (%{MODIFIER_KEY}P)" msgstr "" +msgid "MergeRequest|Summaries are written by AI" +msgstr "" + +msgid "MergeRequest|Summary notes" +msgstr "" + msgid "MergeRequest|This description was generated for revision %{revision} using AI" msgstr "" @@ -44844,6 +44856,9 @@ msgstr "" msgid "Summary generated by AI" msgstr "" +msgid "Summary will be generated with the next push to this merge request and will appear here." +msgstr "" + msgid "Sun" msgstr "" @@ -50550,6 +50565,9 @@ msgstr "" msgid "View seat usage" msgstr "" +msgid "View summary notes" +msgstr "" + msgid "View supported languages and frameworks" msgstr "" diff --git a/qa/Gemfile b/qa/Gemfile index a0136c5dd97..b8414896829 100644 --- a/qa/Gemfile +++ b/qa/Gemfile @@ -3,7 +3,7 @@ source 'https://rubygems.org' gem 'gitlab-qa', '~> 12', require: 'gitlab/qa' -gem 'gitlab_quality-test_tooling', '~> 0.8.2', require: false +gem 'gitlab_quality-test_tooling', '~> 0.8.3', require: false gem 'gitlab-utils', path: '../gems/gitlab-utils' gem 'activesupport', '~> 6.1.7.2' # This should stay in sync with the root's Gemfile gem 'allure-rspec', '~> 2.20.0' diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock index b84b51ad83b..00cc24d9c11 100644 --- a/qa/Gemfile.lock +++ b/qa/Gemfile.lock @@ -129,7 +129,7 @@ GEM rainbow (>= 3, < 4) table_print (= 1.5.7) zeitwerk (>= 2, < 3) - gitlab_quality-test_tooling (0.8.2) + gitlab_quality-test_tooling (0.8.3) activesupport (>= 6.1, < 7.1) gitlab (~> 4.19) http (~> 5.0) @@ -351,7 +351,7 @@ DEPENDENCIES fog-google (~> 1.19) gitlab-qa (~> 12) gitlab-utils! - gitlab_quality-test_tooling (~> 0.8.2) + gitlab_quality-test_tooling (~> 0.8.3) influxdb-client (~> 2.9) knapsack (~> 4.0) nokogiri (~> 1.15, >= 1.15.2) diff --git a/spec/frontend/clusters/agents/components/create_token_modal_spec.js b/spec/frontend/clusters/agents/components/create_token_modal_spec.js index f0fded7b7b2..40cb3b8292f 100644 --- a/spec/frontend/clusters/agents/components/create_token_modal_spec.js +++ b/spec/frontend/clusters/agents/components/create_token_modal_spec.js @@ -3,6 +3,7 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; +import { stubComponent, RENDER_ALL_SLOTS_TEMPLATE } from 'helpers/stub_component'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { mockTracking } from 'helpers/tracking_helper'; import { @@ -37,6 +38,7 @@ describe('CreateTokenModal', () => { }; const agentName = 'cluster-agent'; const projectPath = 'path/to/project'; + const hideModalMock = jest.fn(); const provide = { agentName, @@ -91,10 +93,12 @@ describe('CreateTokenModal', () => { provide, propsData, stubs: { - GlModal, + GlModal: stubComponent(GlModal, { + methods: { hide: hideModalMock }, + template: RENDER_ALL_SLOTS_TEMPLATE, + }), }, }); - wrapper.vm.$refs.modal.hide = jest.fn(); trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); }; @@ -138,6 +142,11 @@ describe('CreateTokenModal', () => { expectDisabledAttribute(findCancelButton(), false); }); + it('cancel button should hide the modal', () => { + findCancelButton().vm.$emit('click'); + expect(hideModalMock).toHaveBeenCalled(); + }); + it('renders a disabled next button', () => { expect(findActionButton().text()).toBe('Create token'); expectDisabledAttribute(findActionButton(), true); diff --git a/spec/frontend/content_editor/components/formatting_toolbar_spec.js b/spec/frontend/content_editor/components/formatting_toolbar_spec.js index 0f0198a6425..0e9dc8d6184 100644 --- a/spec/frontend/content_editor/components/formatting_toolbar_spec.js +++ b/spec/frontend/content_editor/components/formatting_toolbar_spec.js @@ -2,22 +2,25 @@ import { GlTabs, GlTab } from '@gitlab/ui'; import { mockTracking } from 'helpers/tracking_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import FormattingToolbar from '~/content_editor/components/formatting_toolbar.vue'; +import CommentTemplatesDropdown from '~/vue_shared/components/markdown/comment_templates_dropdown.vue'; import { TOOLBAR_CONTROL_TRACKING_ACTION, CONTENT_EDITOR_TRACKING_LABEL, } from '~/content_editor/constants'; +import { createTestEditor, mockChainedCommands } from '../test_utils'; describe('content_editor/components/formatting_toolbar', () => { let wrapper; let trackingSpy; - const buildWrapper = (props) => { + const buildWrapper = ({ props = {}, provide = {} } = {}) => { wrapper = shallowMountExtended(FormattingToolbar, { stubs: { GlTabs, GlTab, }, propsData: props, + provide, }); }; @@ -69,9 +72,37 @@ describe('content_editor/components/formatting_toolbar', () => { describe('when attachment button is hidden', () => { it('does not show the attachment button', () => { - buildWrapper({ hideAttachmentButton: true }); + buildWrapper({ props: { hideAttachmentButton: true } }); expect(wrapper.findByTestId('attachment').exists()).toBe(false); }); }); + + describe('when selecting a saved reply from the comment templates dropdown', () => { + it('updates the rich text editor with the saved comment', async () => { + const tiptapEditor = createTestEditor(); + + buildWrapper({ + provide: { + tiptapEditor, + newCommentTemplatePath: 'some/path', + }, + }); + + const commands = mockChainedCommands(tiptapEditor, ['focus', 'pasteContent', 'run']); + await wrapper + .findComponent(CommentTemplatesDropdown) + .vm.$emit('select', 'Some saved comment'); + + expect(commands.focus).toHaveBeenCalled(); + expect(commands.pasteContent).toHaveBeenCalledWith('Some saved comment'); + expect(commands.run).toHaveBeenCalled(); + }); + + it('does not show the saved replies icon if newCommentTemplatePath is not provided', () => { + buildWrapper(); + + expect(wrapper.findComponent(CommentTemplatesDropdown).exists()).toBe(false); + }); + }); }); diff --git a/spec/frontend/vue_shared/components/markdown/comment_templates_dropdown_spec.js b/spec/frontend/vue_shared/components/markdown/comment_templates_dropdown_spec.js index aea25abb324..2bef6dd15df 100644 --- a/spec/frontend/vue_shared/components/markdown/comment_templates_dropdown_spec.js +++ b/spec/frontend/vue_shared/components/markdown/comment_templates_dropdown_spec.js @@ -4,13 +4,9 @@ import savedRepliesResponse from 'test_fixtures/graphql/comment_templates/saved_ import { mountExtended } from 'helpers/vue_test_utils_helper'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; -import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; -import { updateText } from '~/lib/utils/text_markdown'; import CommentTemplatesDropdown from '~/vue_shared/components/markdown/comment_templates_dropdown.vue'; import savedRepliesQuery from '~/vue_shared/components/markdown/saved_replies.query.graphql'; -jest.mock('~/lib/utils/text_markdown'); - let wrapper; let savedRepliesResp; @@ -28,7 +24,6 @@ function createComponent(options = {}) { const { mockApollo } = options; return mountExtended(CommentTemplatesDropdown, { - attachTo: '#root', propsData: { newCommentTemplatePath: '/new', }, @@ -37,14 +32,6 @@ function createComponent(options = {}) { } describe('Comment templates dropdown', () => { - beforeEach(() => { - setHTMLFixture('<div class="md-area"><textarea></textarea><div id="root"></div></div>'); - }); - - afterEach(() => { - resetHTMLFixture(); - }); - it('fetches data when dropdown gets opened', async () => { const mockApollo = createMockApolloProvider(savedRepliesResponse); wrapper = createComponent({ mockApollo }); @@ -56,7 +43,7 @@ describe('Comment templates dropdown', () => { expect(savedRepliesResp).toHaveBeenCalled(); }); - it('adds content to textarea', async () => { + it('adds emits a select event on selecting a comment', async () => { const mockApollo = createMockApolloProvider(savedRepliesResponse); wrapper = createComponent({ mockApollo }); @@ -66,11 +53,6 @@ describe('Comment templates dropdown', () => { wrapper.find('.gl-new-dropdown-item').trigger('click'); - expect(updateText).toHaveBeenCalledWith({ - textArea: document.querySelector('textarea'), - tag: savedRepliesResponse.data.currentUser.savedReplies.nodes[0].content, - cursorOffset: 0, - wrap: false, - }); + expect(wrapper.emitted().select[0]).toEqual(['Saved Reply Content']); }); }); diff --git a/spec/frontend/vue_shared/components/markdown/header_spec.js b/spec/frontend/vue_shared/components/markdown/header_spec.js index 0d973bb9afc..4738fd44128 100644 --- a/spec/frontend/vue_shared/components/markdown/header_spec.js +++ b/spec/frontend/vue_shared/components/markdown/header_spec.js @@ -2,20 +2,27 @@ import $ from 'jquery'; import { nextTick } from 'vue'; import { GlToggle, GlButton } from '@gitlab/ui'; import HeaderComponent from '~/vue_shared/components/markdown/header.vue'; +import CommentTemplatesDropdown from '~/vue_shared/components/markdown/comment_templates_dropdown.vue'; import ToolbarButton from '~/vue_shared/components/markdown/toolbar_button.vue'; import DrawioToolbarButton from '~/vue_shared/components/markdown/drawio_toolbar_button.vue'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { updateText } from '~/lib/utils/text_markdown'; +import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; + +jest.mock('~/lib/utils/text_markdown'); describe('Markdown field header component', () => { let wrapper; - const createWrapper = (props) => { + const createWrapper = ({ props = {}, provide = {}, attachTo = document.body } = {}) => { wrapper = shallowMountExtended(HeaderComponent, { + attachTo, propsData: { previewMarkdown: false, ...props, }, stubs: { GlToggle }, + provide, }); }; @@ -27,6 +34,7 @@ describe('Markdown field header component', () => { .filter((button) => button.props(prop) === value) .at(0); const findDrawioToolbarButton = () => wrapper.findComponent(DrawioToolbarButton); + const findCommentTemplatesDropdown = () => wrapper.findComponent(CommentTemplatesDropdown); beforeEach(() => { window.gl = { @@ -91,13 +99,13 @@ describe('Markdown field header component', () => { }); it('shows markdown preview when previewMarkdown is true', () => { - createWrapper({ previewMarkdown: true }); + createWrapper({ props: { previewMarkdown: true } }); expect(findPreviewToggle().text()).toBe('Continue editing'); }); it('hides toolbar in preview mode', () => { - createWrapper({ previewMarkdown: true }); + createWrapper({ props: { previewMarkdown: true } }); // only one button is rendered in preview mode expect(findToolbar().findAllComponents(GlButton)).toHaveLength(1); @@ -150,7 +158,9 @@ describe('Markdown field header component', () => { it('does not render suggestion button if `canSuggest` is set to false', () => { createWrapper({ - canSuggest: false, + props: { + canSuggest: false, + }, }); expect(wrapper.find('.js-suggestion-btn').exists()).toBe(false); @@ -158,7 +168,9 @@ describe('Markdown field header component', () => { it('hides markdown preview when previewMarkdown property is false', () => { createWrapper({ - enablePreview: false, + props: { + enablePreview: false, + }, }); expect(wrapper.findByTestId('preview-toggle').exists()).toBe(false); @@ -173,7 +185,9 @@ describe('Markdown field header component', () => { it('restricts items as per input', () => { createWrapper({ - restrictedToolBarItems: ['quote'], + props: { + restrictedToolBarItems: ['quote'], + }, }); expect(findToolbarButtons().length).toBe(defaultCount - 1); @@ -192,9 +206,11 @@ describe('Markdown field header component', () => { beforeEach(() => { createWrapper({ - drawioEnabled: true, - uploadsPath, - markdownPreviewPath, + props: { + drawioEnabled: true, + uploadsPath, + markdownPreviewPath, + }, }); }); @@ -205,4 +221,47 @@ describe('Markdown field header component', () => { }); }); }); + + describe('when selecting a saved reply from the comment templates dropdown', () => { + beforeEach(() => { + setHTMLFixture('<div class="md-area"><textarea></textarea><div id="root"></div></div>'); + }); + + afterEach(() => { + resetHTMLFixture(); + }); + + it('updates the textarea with the saved comment', async () => { + createWrapper({ + attachTo: '#root', + provide: { + newCommentTemplatePath: 'some/path', + glFeatures: { + savedReplies: true, + }, + }, + }); + + await findCommentTemplatesDropdown().vm.$emit('select', 'Some saved comment'); + + expect(updateText).toHaveBeenCalledWith({ + textArea: document.querySelector('textarea'), + tag: 'Some saved comment', + cursorOffset: 0, + wrap: false, + }); + }); + + it('does not show the saved replies button if newCommentTemplatePath is not defined', () => { + createWrapper({ + provide: { + glFeatures: { + savedReplies: true, + }, + }, + }); + + expect(findCommentTemplatesDropdown().exists()).toBe(false); + }); + }); }); diff --git a/spec/lib/gitlab/ci/config/external/mapper/filter_spec.rb b/spec/lib/gitlab/ci/config/external/mapper/filter_spec.rb index 4da3e7e51a7..1a2a6c5beeb 100644 --- a/spec/lib/gitlab/ci/config/external/mapper/filter_spec.rb +++ b/spec/lib/gitlab/ci/config/external/mapper/filter_spec.rb @@ -29,18 +29,5 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper::Filter, feature_category: : [{ local: 'config/.gitlab-ci.yml', rules: [{ if: '$VARIABLE1' }] }] ) end - - context 'when FF `ci_support_include_rules_when_never` is disabled' do - before do - stub_feature_flags(ci_support_include_rules_when_never: false) - end - - it 'filters locations according to rules ignoring when:' do - is_expected.to eq( - [{ local: 'config/.gitlab-ci.yml', rules: [{ if: '$VARIABLE1' }] }, - { remote: 'https://testing.com/.gitlab-ci.yml', rules: [{ if: '$VARIABLE1', when: 'never' }] }] - ) - end - end end end diff --git a/spec/lib/gitlab/ci/config/external/rules_spec.rb b/spec/lib/gitlab/ci/config/external/rules_spec.rb index 1ba5caa1d4b..25b7998ef5e 100644 --- a/spec/lib/gitlab/ci/config/external/rules_spec.rb +++ b/spec/lib/gitlab/ci/config/external/rules_spec.rb @@ -3,8 +3,7 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Config::External::Rules, feature_category: :pipeline_composition do - # Remove `project` property when FF `ci_support_include_rules_when_never` is removed - let(:context) { double(variables_hash: {}, project: nil) } + let(:context) { double(variables_hash: {}) } let(:rule_hashes) { [{ if: '$MY_VAR == "hello"' }] } subject(:rules) { described_class.new(rule_hashes) } @@ -19,11 +18,6 @@ RSpec.describe Gitlab::Ci::Config::External::Rules, feature_category: :pipeline_ end shared_examples 'when there is a rule with if' do |rule_matched_result = true, rule_not_matched_result = false| - # Remove this `before` block when FF `ci_support_include_rules_when_never` is removed - before do - allow(context).to receive(:project).and_return(nil) - end - context 'when the rule matches' do let(:context) { double(variables_hash: { 'MY_VAR' => 'hello' }) } @@ -70,28 +64,12 @@ RSpec.describe Gitlab::Ci::Config::External::Rules, feature_category: :pipeline_ let(:rule_hashes) { [{ if: '$MY_VAR == "hello"', when: 'never' }] } it_behaves_like 'when there is a rule with if', false, false - - context 'when FF `ci_support_include_rules_when_never` is disabled' do - before do - stub_feature_flags(ci_support_include_rules_when_never: false) - end - - it_behaves_like 'when there is a rule with if' - end end context 'with when: always' do let(:rule_hashes) { [{ if: '$MY_VAR == "hello"', when: 'always' }] } it_behaves_like 'when there is a rule with if' - - context 'when FF `ci_support_include_rules_when_never` is disabled' do - before do - stub_feature_flags(ci_support_include_rules_when_never: false) - end - - it_behaves_like 'when there is a rule with if' - end end context 'with when: <invalid string>' do @@ -115,28 +93,12 @@ RSpec.describe Gitlab::Ci::Config::External::Rules, feature_category: :pipeline_ let(:rule_hashes) { [{ exists: 'Dockerfile', when: 'never' }] } it_behaves_like 'when there is a rule with exists', false, false - - context 'when FF `ci_support_include_rules_when_never` is disabled' do - before do - stub_feature_flags(ci_support_include_rules_when_never: false) - end - - it_behaves_like 'when there is a rule with exists' - end end context 'with when: always' do let(:rule_hashes) { [{ exists: 'Dockerfile', when: 'always' }] } it_behaves_like 'when there is a rule with exists' - - context 'when FF `ci_support_include_rules_when_never` is disabled' do - before do - stub_feature_flags(ci_support_include_rules_when_never: false) - end - - it_behaves_like 'when there is a rule with exists' - end end context 'with when: <invalid string>' do diff --git a/spec/requests/api/debian_group_packages_spec.rb b/spec/requests/api/debian_group_packages_spec.rb index 9c726e5a5f7..25b99862100 100644 --- a/spec/requests/api/debian_group_packages_spec.rb +++ b/spec/requests/api/debian_group_packages_spec.rb @@ -6,48 +6,28 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do include WorkhorseHelpers include_context 'Debian repository shared context', :group, false do - shared_examples 'a Debian package tracking event' do |action| - include_context 'Debian repository access', :public, :developer, :basic do - let(:snowplow_gitlab_standard_context) do - { project: nil, namespace: container, user: user, property: 'i_package_debian_user' } - end - - it_behaves_like 'a package tracking event', described_class.name, action - end - end - - shared_examples 'not a Debian package tracking event' do - include_context 'Debian repository access', :public, :developer, :basic do - it_behaves_like 'not a package tracking event', described_class.name, /.*/ - end - end - context 'with invalid parameter' do let(:url) { "/groups/1/-/packages/debian/dists/with+space/InRelease" } it_behaves_like 'Debian packages GET request', :bad_request, /^distribution is invalid$/ - it_behaves_like 'not a Debian package tracking event' end describe 'GET groups/:id/-/packages/debian/dists/*distribution/Release.gpg' do let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/Release.gpg" } it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^-----BEGIN PGP SIGNATURE-----/ - it_behaves_like 'not a Debian package tracking event' end describe 'GET groups/:id/-/packages/debian/dists/*distribution/Release' do let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/Release" } it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Codename: fixture-distribution\n$/ - it_behaves_like 'a Debian package tracking event', 'list_package' end describe 'GET groups/:id/-/packages/debian/dists/*distribution/InRelease' do let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/InRelease" } it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^-----BEGIN PGP SIGNED MESSAGE-----/ - it_behaves_like 'a Debian package tracking event', 'list_package' end describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do @@ -56,14 +36,12 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{target_component_name}/binary-#{architecture.name}/Packages" } it_behaves_like 'Debian packages index endpoint', /Description: This is an incomplete Packages file/ - it_behaves_like 'a Debian package tracking event', 'list_package' end describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/Packages.gz' do let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/Packages.gz" } it_behaves_like 'Debian packages read endpoint', 'GET', :not_found, /Format gz is not supported/ - it_behaves_like 'not a Debian package tracking event' end describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/by-hash/SHA256/:file_sha256' do @@ -73,7 +51,6 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{target_component_name}/binary-#{architecture.name}/by-hash/SHA256/#{target_sha256}" } it_behaves_like 'Debian packages index sha256 endpoint', /^Other SHA256$/ - it_behaves_like 'a Debian package tracking event', 'list_package' end describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/source/Sources' do @@ -82,7 +59,6 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{target_component_name}/source/Sources" } it_behaves_like 'Debian packages index endpoint', /^Description: This is an incomplete Sources file$/ - it_behaves_like 'a Debian package tracking event', 'list_package' end describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/source/by-hash/SHA256/:file_sha256' do @@ -92,7 +68,6 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{target_component_name}/source/by-hash/SHA256/#{target_sha256}" } it_behaves_like 'Debian packages index sha256 endpoint', /^Other SHA256$/ - it_behaves_like 'a Debian package tracking event', 'list_package' end describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/Packages' do @@ -101,14 +76,12 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{target_component_name}/debian-installer/binary-#{architecture.name}/Packages" } it_behaves_like 'Debian packages index endpoint', /Description: This is an incomplete D-I Packages file/ - it_behaves_like 'a Debian package tracking event', 'list_package' end describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/Packages.gz' do let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/Packages.gz" } it_behaves_like 'Debian packages read endpoint', 'GET', :not_found, /Format gz is not supported/ - it_behaves_like 'not a Debian package tracking event' end describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/by-hash/SHA256/:file_sha256' do @@ -118,7 +91,6 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{target_component_name}/debian-installer/binary-#{architecture.name}/by-hash/SHA256/#{target_sha256}" } it_behaves_like 'Debian packages index sha256 endpoint', /^Other SHA256$/ - it_behaves_like 'a Debian package tracking event', 'list_package' end describe 'GET groups/:id/-/packages/debian/pool/:codename/:project_id/:letter/:package_name/:package_version/:file_name' do @@ -139,7 +111,6 @@ RSpec.describe API::DebianGroupPackages, feature_category: :package_registry do with_them do it_behaves_like 'Debian packages read endpoint', 'GET', :success, params[:success_body] - it_behaves_like 'a Debian package tracking event', 'pull_package' context 'for bumping last downloaded at' do include_context 'Debian repository access', :public, :developer, :basic do diff --git a/spec/requests/api/debian_project_packages_spec.rb b/spec/requests/api/debian_project_packages_spec.rb index b1566860ffc..7f3f633a35c 100644 --- a/spec/requests/api/debian_project_packages_spec.rb +++ b/spec/requests/api/debian_project_packages_spec.rb @@ -7,22 +7,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d include WorkhorseHelpers include_context 'Debian repository shared context', :project, false do - shared_examples 'a Debian package tracking event' do |action| - include_context 'Debian repository access', :public, :developer, :basic do - let(:snowplow_gitlab_standard_context) do - { project: container, namespace: container.namespace, user: user, property: 'i_package_debian_user' } - end - - it_behaves_like 'a package tracking event', described_class.name, action - end - end - - shared_examples 'not a Debian package tracking event' do - include_context 'Debian repository access', :public, :developer, :basic do - it_behaves_like 'not a package tracking event', described_class.name, /.*/ - end - end - shared_examples 'accept GET request on private project with access to package registry for everyone' do include_context 'Debian repository access', :private, :anonymous, :basic do before do @@ -37,14 +21,12 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d let(:url) { "/projects/1/packages/debian/dists/with+space/InRelease" } it_behaves_like 'Debian packages GET request', :bad_request, /^distribution is invalid$/ - it_behaves_like 'not a Debian package tracking event' end describe 'GET projects/:id/packages/debian/dists/*distribution/Release.gpg' do let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/Release.gpg" } it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^-----BEGIN PGP SIGNATURE-----/ - it_behaves_like 'not a Debian package tracking event' it_behaves_like 'accept GET request on private project with access to package registry for everyone' end @@ -52,7 +34,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/Release" } it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Codename: fixture-distribution\n$/ - it_behaves_like 'a Debian package tracking event', 'list_package' it_behaves_like 'accept GET request on private project with access to package registry for everyone' end @@ -60,7 +41,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/InRelease" } it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^-----BEGIN PGP SIGNED MESSAGE-----/ - it_behaves_like 'a Debian package tracking event', 'list_package' it_behaves_like 'accept GET request on private project with access to package registry for everyone' end @@ -70,7 +50,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{target_component_name}/binary-#{architecture.name}/Packages" } it_behaves_like 'Debian packages index endpoint', /Description: This is an incomplete Packages file/ - it_behaves_like 'a Debian package tracking event', 'list_package' it_behaves_like 'accept GET request on private project with access to package registry for everyone' end @@ -78,7 +57,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/Packages.gz" } it_behaves_like 'Debian packages read endpoint', 'GET', :not_found, /Format gz is not supported/ - it_behaves_like 'not a Debian package tracking event' end describe 'GET projects/:id/packages/debian/dists/*distribution/:component/binary-:architecture/by-hash/SHA256/:file_sha256' do @@ -88,7 +66,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{target_component_name}/binary-#{architecture.name}/by-hash/SHA256/#{target_sha256}" } it_behaves_like 'Debian packages index sha256 endpoint', /^Other SHA256$/ - it_behaves_like 'a Debian package tracking event', 'list_package' it_behaves_like 'accept GET request on private project with access to package registry for everyone' end @@ -98,7 +75,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{target_component_name}/source/Sources" } it_behaves_like 'Debian packages index endpoint', /^Description: This is an incomplete Sources file$/ - it_behaves_like 'a Debian package tracking event', 'list_package' it_behaves_like 'accept GET request on private project with access to package registry for everyone' end @@ -109,7 +85,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{target_component_name}/source/by-hash/SHA256/#{target_sha256}" } it_behaves_like 'Debian packages index sha256 endpoint', /^Other SHA256$/ - it_behaves_like 'a Debian package tracking event', 'list_package' it_behaves_like 'accept GET request on private project with access to package registry for everyone' end @@ -119,7 +94,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{target_component_name}/debian-installer/binary-#{architecture.name}/Packages" } it_behaves_like 'Debian packages index endpoint', /Description: This is an incomplete D-I Packages file/ - it_behaves_like 'a Debian package tracking event', 'list_package' it_behaves_like 'accept GET request on private project with access to package registry for everyone' end @@ -127,7 +101,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/Packages.gz" } it_behaves_like 'Debian packages read endpoint', 'GET', :not_found, /Format gz is not supported/ - it_behaves_like 'not a Debian package tracking event' end describe 'GET projects/:id/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/by-hash/SHA256/:file_sha256' do @@ -137,7 +110,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{target_component_name}/debian-installer/binary-#{architecture.name}/by-hash/SHA256/#{target_sha256}" } it_behaves_like 'Debian packages index sha256 endpoint', /^Other SHA256$/ - it_behaves_like 'a Debian package tracking event', 'list_package' it_behaves_like 'accept GET request on private project with access to package registry for everyone' end @@ -159,7 +131,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d with_them do it_behaves_like 'Debian packages read endpoint', 'GET', :success, params[:success_body] - it_behaves_like 'a Debian package tracking event', 'pull_package' context 'for bumping last downloaded at' do include_context 'Debian repository access', :public, :developer, :basic do @@ -182,13 +153,11 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d it_behaves_like 'Debian packages write endpoint', 'upload', :created, nil it_behaves_like 'Debian packages endpoint catching ObjectStorage::RemoteStoreError' - it_behaves_like 'a Debian package tracking event', 'push_package' context 'with codename and component' do let(:extra_params) { { distribution: distribution.codename, component: 'main' } } it_behaves_like 'Debian packages write endpoint', 'upload', :created, nil - it_behaves_like 'a Debian package tracking event', 'push_package' end context 'with codename and without component' do @@ -197,8 +166,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d include_context 'Debian repository access', :public, :developer, :basic do it_behaves_like 'Debian packages GET request', :bad_request, /component is missing/ end - - it_behaves_like 'not a Debian package tracking event' end end @@ -209,8 +176,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d it_behaves_like "Debian packages upload request", :created, nil end - it_behaves_like 'a Debian package tracking event', 'push_package' - context 'with codename and component' do let(:extra_params) { { distribution: distribution.codename, component: 'main' } } @@ -218,8 +183,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d it_behaves_like "Debian packages upload request", :bad_request, /^file_name Only debs, udebs and ddebs can be directly added to a distribution$/ end - - it_behaves_like 'not a Debian package tracking event' end end @@ -227,7 +190,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d let(:file_name) { 'sample_1.2.3~alpha2_amd64.changes' } it_behaves_like 'Debian packages write endpoint', 'upload', :created, nil - it_behaves_like 'a Debian package tracking event', 'push_package' end end @@ -237,7 +199,6 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d let(:url) { "/projects/#{container.id}/packages/debian/#{file_name}/authorize" } it_behaves_like 'Debian packages write endpoint', 'upload authorize', :created, nil - it_behaves_like 'not a Debian package tracking event' end end end |