diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-03 15:11:31 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-03 15:11:31 +0000 |
commit | e6fa9529b4922a4c552e8908a2929ff995e8b53d (patch) | |
tree | 9ccfea4d253faa8c202f753631961a794af44ad6 | |
parent | 720cf698151643831bf36e3bd4ccd1c8e9246184 (diff) | |
download | gitlab-ce-e6fa9529b4922a4c552e8908a2929ff995e8b53d.tar.gz |
Add latest changes from gitlab-org/gitlab@master
123 files changed, 1127 insertions, 564 deletions
diff --git a/.gitlab/ci/as-if-jh.gitlab-ci.yml b/.gitlab/ci/as-if-jh.gitlab-ci.yml new file mode 100644 index 00000000000..6019c8a9649 --- /dev/null +++ b/.gitlab/ci/as-if-jh.gitlab-ci.yml @@ -0,0 +1,63 @@ +.shared-as-if-jh: + variables: + SANDBOX_PROJECT: "gitlab-org-sandbox/gitlab-jh-validation" + SANDBOX_REPOSITORY: "https://dummy:${AS_IF_JH_TOKEN}@gitlab.com/${SANDBOX_PROJECT}.git" + GITLAB_JH_MIRROR_PROJECT: "33019816" + AS_IF_JH_BRANCH: "as-if-jh/${CI_COMMIT_REF_NAME}" + JH_FILES_TO_COMMIT: "jh package.json yarn.lock" + +add-jh-files: + extends: + - .shared-as-if-jh + - .as-if-jh:rules:prepare-as-if-jh + image: ${GITLAB_DEPENDENCY_PROXY}ruby:${RUBY_VERSION} + stage: prepare + before_script: + - source ./scripts/utils.sh + - source ./scripts/setup/as-if-jh.sh + - install_gitlab_gem + script: + - prepare_jh_branch + - download_jh_path ${JH_FILES_TO_COMMIT} + - echoinfo "Changes after downloading JiHu files:" + - git diff + - git status + artifacts: + expire_in: 2d + paths: + # This should match JH_FILES_TO_COMMIT + - jh/ + - package.json + - yarn.lock + +prepare-as-if-jh-branch: + extends: + - .shared-as-if-jh + - .as-if-jh:rules:prepare-as-if-jh + stage: prepare + needs: + - add-jh-files + script: + - git checkout -b "${AS_IF_JH_BRANCH}" + - git add ${JH_FILES_TO_COMMIT} + - git commit -m 'Add JH files' # TODO: Mark which SHA we add + # Fetch for the history of the branch so it does not cause the following error: + # ! [remote rejected] ref -> ref (shallow update not allowed) + - git fetch --unshallow --filter=tree:0 origin "${CI_COMMIT_REF_NAME}" + - git push -f "${SANDBOX_REPOSITORY}" "${AS_IF_JH_BRANCH}" + +start-as-if-jh: + extends: + - .shared-as-if-jh + - .as-if-jh:rules:start-as-if-jh + stage: prepare + needs: ["prepare-as-if-jh-branch"] + inherit: + variables: false + variables: + AS_IF_EDITION: "jh" + FORCE_GITLAB_CI: "true" # TODO: Trigger a merge request pipeline + trigger: + project: gitlab-org-sandbox/gitlab-jh-validation # ${SANDBOX_PROJECT} does not work here + branch: as-if-jh/${CI_COMMIT_REF_NAME} # ${AS_IF_JH_BRANCH} does not work here + strategy: depend diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index f3f40256538..c8b1b374b24 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -5,13 +5,17 @@ if: '$CI_PROJECT_NAMESPACE !~ /^gitlab(-org)?($|\/)/' .if-not-ee: &if-not-ee - if: '$CI_PROJECT_NAME !~ /^gitlab(-ee)?$/' + # Only consider FOSS not EE + if: '$CI_PROJECT_NAME !~ /^gitlab(-ee)?$/ && $CI_PROJECT_NAME !~ /^gitlab-jh/' .if-not-foss: &if-not-foss if: '$CI_PROJECT_NAME != "gitlab-foss" && $CI_PROJECT_NAME != "gitlab-ce" && $CI_PROJECT_NAME != "gitlabhq"' .if-jh: &if-jh - if: '$CI_PROJECT_PATH =~ /^gitlab-(jh|cn)\/.*/' + # Example of these projects: + # https://jihulab.com/gitlab-cn/gitlab + # https://gitlab.com/gitlab-org-sandbox/gitlab-jh-validation + if: '$CI_PROJECT_PATH =~ /^gitlab-(jh|cn)\/.*/ || $CI_PROJECT_NAME =~ /^gitlab-jh/' .if-force-ci: &if-force-ci if: '$FORCE_GITLAB_CI' @@ -560,6 +564,13 @@ - <<: *if-jh when: never +.as-if-jh-default-exclusion-rules: + rules: + - <<: *if-security-merge-request + when: never + - <<: *if-merge-request-targeting-stable-branch + when: never + .rails:rules:minimal-default-rules: rules: - <<: *if-merge-request-approved @@ -2024,3 +2035,21 @@ rules: - <<: *if-default-refs changes: *lint-metrics-yaml-patterns + +################## +# as-if-jh rules # +################## +.as-if-jh:rules:prepare-as-if-jh: + rules: + - !reference [".strict-ee-only-rules", rules] + - !reference [".as-if-jh-default-exclusion-rules", rules] + - <<: *if-merge-request-labels-as-if-jh + +# This rule should share the same logic with .as-if-jh:rules:prepare-as-if-jh +# Because the jobs using this need jobs using the preparation rules +.as-if-jh:rules:start-as-if-jh: + rules: + - !reference [".strict-ee-only-rules", rules] + - !reference [".as-if-jh-default-exclusion-rules", rules] + - <<: *if-merge-request-labels-as-if-jh + allow_failure: true # See https://gitlab.com/gitlab-org/gitlab/-/issues/351136 diff --git a/.rubocop.yml b/.rubocop.yml index 8224f104674..3ae3ce89be0 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -590,6 +590,12 @@ Graphql/Descriptions: - 'app/graphql/**/*' - 'ee/app/graphql/**/*' +Graphql/EnumNames: + Enabled: true + Include: + - 'app/graphql/**/*' + - 'ee/app/graphql/**/*' + # Cops for upgrade to gitlab-styles 3.1.0 RSpec/ImplicitSubject: Enabled: false diff --git a/.rubocop_todo/graphql/enum_names.yml b/.rubocop_todo/graphql/enum_names.yml new file mode 100644 index 00000000000..18f485b6759 --- /dev/null +++ b/.rubocop_todo/graphql/enum_names.yml @@ -0,0 +1,37 @@ +--- +Graphql/EnumNames: + Details: grace period + Exclude: + - 'app/graphql/types/access_level_enum.rb' + - 'app/graphql/types/availability_enum.rb' + - 'app/graphql/types/ci/pipeline_config_source_enum.rb' + - 'app/graphql/types/ci/pipeline_scope_enum.rb' + - 'app/graphql/types/ci/pipeline_status_enum.rb' + - 'app/graphql/types/container_expiration_policy_cadence_enum.rb' + - 'app/graphql/types/container_expiration_policy_keep_enum.rb' + - 'app/graphql/types/container_expiration_policy_older_than_enum.rb' + - 'app/graphql/types/data_visualization_palette/color_enum.rb' + - 'app/graphql/types/data_visualization_palette/weight_enum.rb' + - 'app/graphql/types/merge_strategy_enum.rb' + - 'app/graphql/types/milestone_state_enum.rb' + - 'app/graphql/types/packages/cleanup/keep_duplicated_package_files_enum.rb' + - 'app/graphql/types/packages/conan/metadatum_file_type_enum.rb' + - 'app/graphql/types/packages/package_type_enum.rb' + - 'app/graphql/types/security/report_type_enum.rb' + - 'app/graphql/types/snippets/blob_action_enum.rb' + - 'app/graphql/types/snippets/type_enum.rb' + - 'app/graphql/types/snippets/visibility_scopes_enum.rb' + - 'app/graphql/types/sort_direction_enum.rb' + - 'app/graphql/types/todo_action_enum.rb' + - 'app/graphql/types/todo_state_enum.rb' + - 'app/graphql/types/todo_target_enum.rb' + - 'app/graphql/types/user_callout_feature_name_enum.rb' + - 'app/graphql/types/visibility_levels_enum.rb' + - 'ee/app/graphql/types/dast_scan_type_enum.rb' + - 'ee/app/graphql/types/dast_site_profile_validation_status_enum.rb' + - 'ee/app/graphql/types/dast_site_validation_status_enum.rb' + - 'ee/app/graphql/types/dast_site_validation_strategy_enum.rb' + - 'ee/app/graphql/types/dast_target_type_enum.rb' + - 'ee/app/graphql/types/geo/replication_state_enum.rb' + - 'ee/app/graphql/types/geo/verification_state_enum.rb' + - 'ee/app/graphql/types/incident_management/oncall_rotation_length_unit_enum.rb' diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index a119dcb32a8..8b4dcdffca0 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -165e1fbd149fd4baec8ce3957e728d3ce3412a15 +b55578ec476e8bc8ecd9775ee7e9960b52e0f6e0 diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue index d99afa8455d..0d2e49c966f 100644 --- a/app/assets/javascripts/boards/components/board_content.vue +++ b/app/assets/javascripts/boards/components/board_content.vue @@ -1,6 +1,6 @@ <script> import { GlAlert } from '@gitlab/ui'; -import { sortBy } from 'lodash'; +import { sortBy, throttle } from 'lodash'; import Draggable from 'vuedraggable'; import { mapState, mapGetters, mapActions } from 'vuex'; import BoardAddNewColumn from 'ee_else_ce/boards/components/board_add_new_column.vue'; @@ -26,6 +26,11 @@ export default { required: true, }, }, + data() { + return { + boardHeight: null, + }; + }, computed: { ...mapState(['boardLists', 'error', 'addColumnForm']), ...mapGetters(['isSwimlanesOn', 'isEpicBoard', 'isIssueBoard']), @@ -55,12 +60,28 @@ export default { return this.canDragColumns ? options : {}; }, }, + mounted() { + this.setBoardHeight(); + + this.resizeObserver = new ResizeObserver( + throttle(() => { + this.setBoardHeight(); + }, 150), + ); + this.resizeObserver.observe(document.body); + }, + unmounted() { + this.resizeObserver.disconnect(); + }, methods: { ...mapActions(['moveList', 'unsetError']), afterFormEnters() { const el = this.canDragColumns ? this.$refs.list.$el : this.$refs.list; el.scrollTo({ left: el.scrollWidth, behavior: 'smooth' }); }, + setBoardHeight() { + this.boardHeight = `${window.innerHeight - this.$el.getBoundingClientRect().top}px`; + }, }, }; </script> @@ -76,6 +97,7 @@ export default { ref="list" v-bind="draggableOptions" class="boards-list gl-w-full gl-py-5 gl-pr-3 gl-white-space-nowrap gl-overflow-x-scroll" + :style="{ height: boardHeight }" @end="moveList" > <board-column @@ -99,6 +121,7 @@ export default { :lists="boardListsToUse" :can-admin-list="canAdminList" :disabled="disabled" + :style="{ height: boardHeight }" /> <board-content-sidebar v-if="isIssueBoard" data-testid="issue-boards-sidebar" /> diff --git a/app/assets/javascripts/reports/codequality_report/grouped_codequality_reports_app.vue b/app/assets/javascripts/reports/codequality_report/grouped_codequality_reports_app.vue deleted file mode 100644 index 599e8d35708..00000000000 --- a/app/assets/javascripts/reports/codequality_report/grouped_codequality_reports_app.vue +++ /dev/null @@ -1,84 +0,0 @@ -<script> -import { mapState, mapActions, mapGetters } from 'vuex'; -import { s__, sprintf } from '~/locale'; -import { componentNames } from '~/reports/components/issue_body'; -import ReportSection from '~/reports/components/report_section.vue'; -import createStore from './store'; - -export default { - name: 'GroupedCodequalityReportsApp', - store: createStore(), - components: { - ReportSection, - }, - props: { - headBlobPath: { - type: String, - required: true, - }, - baseBlobPath: { - type: String, - required: false, - default: null, - }, - codequalityReportsPath: { - type: String, - required: false, - default: '', - }, - codequalityHelpPath: { - type: String, - required: true, - }, - }, - componentNames, - computed: { - ...mapState(['newIssues', 'resolvedIssues', 'hasError', 'statusReason']), - ...mapGetters([ - 'hasCodequalityIssues', - 'codequalityStatus', - 'codequalityText', - 'codequalityPopover', - ]), - }, - created() { - this.setPaths({ - baseBlobPath: this.baseBlobPath, - headBlobPath: this.headBlobPath, - reportsPath: this.codequalityReportsPath, - helpPath: this.codequalityHelpPath, - }); - - this.fetchReports(); - }, - methods: { - ...mapActions(['fetchReports', 'setPaths']), - }, - loadingText: sprintf(s__('ciReport|Loading %{reportName} report'), { - // eslint-disable-next-line @gitlab/require-i18n-strings - reportName: 'Code quality', - }), - errorText: sprintf(s__('ciReport|Failed to load %{reportName} report'), { - // eslint-disable-next-line @gitlab/require-i18n-strings - reportName: 'Code quality', - }), -}; -</script> -<template> - <report-section - :status="codequalityStatus" - :loading-text="$options.loadingText" - :error-text="$options.errorText" - :success-text="codequalityText" - :unresolved-issues="newIssues" - :resolved-issues="resolvedIssues" - :has-issues="hasCodequalityIssues" - :component="$options.componentNames.CodequalityIssueBody" - :popover-options="codequalityPopover" - :show-report-section-status-icon="false" - track-action="users_expanding_testing_code_quality_report" - class="js-codequality-widget mr-widget-border-top mr-report" - > - <template v-if="hasError" #sub-heading>{{ statusReason }}</template> - </report-section> -</template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue index 94359d7d6ac..5111e2609e8 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue @@ -6,6 +6,7 @@ import { sprintf, __ } from '~/locale'; import Poll from '~/lib/utils/poll'; import ActionButtons from '../action_buttons.vue'; import { EXTENSION_ICONS } from '../../constants'; +import { createTelemetryHub } from '../extensions/telemetry'; import ContentRow from './widget_content_row.vue'; import DynamicContent from './dynamic_content.vue'; import StatusIcon from './status_icon.vue'; @@ -89,6 +90,11 @@ export default { type: String, required: true, }, + telemetry: { + type: Boolean, + required: false, + default: true, + }, }, data() { return { @@ -98,6 +104,7 @@ export default { isLoadingExpandedContent: false, summaryError: null, contentError: null, + telemetryHub: null, }; }, computed: { @@ -113,8 +120,14 @@ export default { this.$emit('is-loading', newValue); }, }, + created() { + if (this.telemetry) { + this.telemetryHub = createTelemetryHub(this.widgetName); + } + }, async mounted() { this.isLoading = true; + this.telemetryHub?.viewed(); try { await this.fetch(this.fetchCollapsedData, FETCH_TYPE_COLLAPSED); @@ -125,12 +138,21 @@ export default { this.isLoading = false; }, methods: { + onActionClick(action) { + if (action.fullReport) { + this.telemetryHub?.fullReportClicked(); + } + }, toggleCollapsed() { this.isCollapsed = !this.isCollapsed; - if (this.isExpandedForTheFirstTime && typeof this.fetchExpandedData === 'function') { - this.isExpandedForTheFirstTime = false; - this.fetchExpandedContent(); + if (this.isExpandedForTheFirstTime) { + this.telemetryHub?.expanded({ type: this.summaryStatusIcon }); + + if (typeof this.fetchExpandedData === 'function') { + this.isExpandedForTheFirstTime = false; + this.fetchExpandedContent(); + } } }, async fetchExpandedContent() { @@ -208,6 +230,7 @@ export default { v-if="actionButtons.length > 0" :widget="widgetName" :tertiary-buttons="actionButtons" + @clickedAction="onActionClick" /> <div v-if="isCollapsible" diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index 62ad22c637b..af0328edb23 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -82,8 +82,6 @@ export default { MrWidgetAutoMergeFailed: AutoMergeFailed, MrWidgetRebase: RebaseState, SourceBranchRemovalStatus, - GroupedCodequalityReportsApp: () => - import('../reports/codequality_report/grouped_codequality_reports_app.vue'), GroupedTestReportsApp: () => import('../reports/grouped_test_report/grouped_test_reports_app.vue'), MrWidgetApprovals, @@ -218,9 +216,6 @@ export default { shouldShowSecurityExtension() { return window.gon?.features?.refactorSecurityExtension; }, - shouldShowCodeQualityExtension() { - return window.gon?.features?.refactorCodeQualityExtension; - }, shouldShowMergeDetails() { if (this.mr.state === 'readyToMerge') return true; @@ -519,7 +514,7 @@ export default { } }, registerCodeQualityExtension() { - if (this.shouldRenderCodeQuality && this.shouldShowCodeQualityExtension) { + if (this.shouldRenderCodeQuality) { registerExtension(codeQualityExtension); } }, @@ -592,14 +587,6 @@ export default { <widget-container v-if="mr" :mr="mr" /> - <grouped-codequality-reports-app - v-if="shouldRenderCodeQuality && !shouldShowCodeQualityExtension" - :head-blob-path="mr.headBlobPath" - :base-blob-path="mr.baseBlobPath" - :codequality-reports-path="mr.codequalityReportsPath" - :codequality-help-path="mr.codequalityHelpPath" - /> - <security-reports-app v-if="shouldRenderSecurityReport && !shouldShowSecurityExtension" :pipeline-id="mr.pipeline.id" diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index 731d3886f61..9221b87014b 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -299,7 +299,6 @@ export default class MergeRequestStore { this.headBlobPath = blobPath.head_path || ''; this.baseBlobPath = blobPath.base_path || ''; this.codequalityReportsPath = data.codequality_reports_path; - this.codequalityHelpPath = data.codequality_help_path; // Security reports this.sastComparisonPath = data.sast_comparison_path; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 95e03abbb48..0f2a818d582 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -674,18 +674,7 @@ $ci-skipped-color: #888; */ $issue-boards-font-size: 14px; $issue-boards-card-shadow: rgba(0, 0, 0, 0.1); -/* - The following heights are used in boards.scss and are used for calculation of the board height. - They probably should be derived in a smarter way. -*/ $issue-boards-filter-height: 68px; -$issue-boards-filter-height-md: 110px; -$issue-boards-filter-height-sm: 299px; -$issue-boards-breadcrumbs-height-xs: 63px; -$issue-board-list-difference-xs: calc(#{$header-height} + #{$issue-boards-breadcrumbs-height-xs}); -$issue-board-list-difference-sm: calc(#{$header-height} + #{$breadcrumb-min-height}); -$issue-board-list-difference-md: calc(#{$issue-board-list-difference-sm} + #{$issue-boards-filter-height-md}); -$issue-board-list-difference-lg: calc(#{$issue-board-list-difference-sm} + #{$issue-boards-filter-height}); /* The following heights are used in environment_logs.scss and are used for calculation of the log viewer height. */ diff --git a/app/assets/stylesheets/page_bundles/boards.scss b/app/assets/stylesheets/page_bundles/boards.scss index d45bc865da5..0cc1fb40e4a 100644 --- a/app/assets/stylesheets/page_bundles/boards.scss +++ b/app/assets/stylesheets/page_bundles/boards.scss @@ -18,38 +18,9 @@ .boards-list, .board-swimlanes { - height: calc(100vh - #{$issue-board-list-difference-xs}); overflow-x: scroll; min-height: 200px; border-left: 8px solid var(--gray-10, $white); - - @include media-breakpoint-only(sm) { - height: calc(100vh - #{$issue-board-list-difference-sm}); - } - - @include media-breakpoint-up(md) { - height: calc(100vh - #{$issue-board-list-difference-md}); - } - - @include media-breakpoint-up(lg) { - height: calc(100vh - #{$issue-board-list-difference-lg}); - } - - .with-performance-bar & { - height: calc(100vh - #{$issue-board-list-difference-xs} - #{$performance-bar-height}); - - @include media-breakpoint-only(sm) { - height: calc(100vh - #{$issue-board-list-difference-sm} - #{$performance-bar-height}); - } - - @include media-breakpoint-up(md) { - height: calc(100vh - #{$issue-board-list-difference-md} - #{$performance-bar-height}); - } - - @include media-breakpoint-up(lg) { - height: calc(100vh - #{$issue-board-list-difference-lg} - #{$performance-bar-height}); - } - } } .board { diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 3251b1483c3..cfc565a8b56 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -34,7 +34,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo before_action only: [:show] do push_frontend_feature_flag(:merge_request_widget_graphql, project) push_frontend_feature_flag(:core_security_mr_widget_counts, project) - push_frontend_feature_flag(:refactor_code_quality_extension, project) push_frontend_feature_flag(:refactor_mr_widget_test_summary, project) push_frontend_feature_flag(:issue_assignees_widget, @project) push_frontend_feature_flag(:realtime_labels, project) diff --git a/app/models/concerns/pg_full_text_searchable.rb b/app/models/concerns/pg_full_text_searchable.rb index f533791c2ee..562c8cf23f3 100644 --- a/app/models/concerns/pg_full_text_searchable.rb +++ b/app/models/concerns/pg_full_text_searchable.rb @@ -25,7 +25,7 @@ module PgFullTextSearchable TSVECTOR_MAX_LENGTH = 1.megabyte.freeze TEXT_SEARCH_DICTIONARY = 'english' URL_SCHEME_REGEX = %r{(?<=\A|\W)\w+://(?=\w+)}.freeze - TSQUERY_DISALLOWED_CHARACTERS_REGEX = %r{[^a-zA-Z0-9 .@/\-"]}.freeze + TSQUERY_DISALLOWED_CHARACTERS_REGEX = %r{[^a-zA-Z0-9 .@/\-_"]}.freeze def update_search_data! tsvector_sql_nodes = self.class.pg_full_text_searchable_columns.map do |column, weight| diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb index 2135892a95a..925a2acbb58 100644 --- a/app/services/groups/update_service.rb +++ b/app/services/groups/update_service.rb @@ -10,6 +10,8 @@ module Groups reject_parent_id! remove_unallowed_params + before_assignment_hook(group, params) + if renaming_group_with_container_registry_images? group.errors.add(:base, container_images_error) return false @@ -25,8 +27,6 @@ module Groups handle_changes - before_assignment_hook(group, params) - handle_namespace_settings group.assign_attributes(params) diff --git a/app/services/incident_management/timeline_events/create_service.rb b/app/services/incident_management/timeline_events/create_service.rb index c625b0d15bd..ef6f48bd741 100644 --- a/app/services/incident_management/timeline_events/create_service.rb +++ b/app/services/incident_management/timeline_events/create_service.rb @@ -52,7 +52,8 @@ module IncidentManagement def change_severity(incident, user) return if Feature.disabled?(:incident_timeline_events_for_severity, incident.project) - note = "@#{user.username} changed the incident severity to **#{incident.severity.humanize}**" + severity_label = IssuableSeverity::SEVERITY_LABELS[incident.severity.to_sym] + note = "@#{user.username} changed the incident severity to **#{severity_label}**" occurred_at = incident.updated_at action = 'severity' diff --git a/config/feature_flags/development/lazy_load_commits.yml b/config/feature_flags/development/lazy_load_commits.yml index d4764907211..6140b88c3c2 100644 --- a/config/feature_flags/development/lazy_load_commits.yml +++ b/config/feature_flags/development/lazy_load_commits.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342497 milestone: '14.4' type: development group: group::source code -default_enabled: false +default_enabled: true diff --git a/config/feature_flags/development/refactor_code_quality_extension.yml b/config/feature_flags/development/refactor_code_quality_extension.yml deleted file mode 100644 index c6eb7c19a6e..00000000000 --- a/config/feature_flags/development/refactor_code_quality_extension.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: refactor_code_quality_extension -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88865 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/363845 -milestone: '15.1' -type: development -group: group::secure -default_enabled: false diff --git a/config/open_api.yml b/config/open_api.yml index 331a90a8710..a3e9bd44a16 100644 --- a/config/open_api.yml +++ b/config/open_api.yml @@ -29,6 +29,8 @@ metadata: description: Operations related to environments - name: feature_flags_user_lists description: Operations related to accessing GitLab feature flag user lists + - name: feature_flags + description: Operations related to feature flags - name: features description: Operations related to managing Flipper-based feature flags - name: freeze_periods @@ -39,5 +41,7 @@ metadata: description: Operations related to metadata of the GitLab instance - name: release_links description: Operations related to release assets (links) + - name: releases + description: Operations related to releases - name: suggestions - description: Operations related to suggestions
\ No newline at end of file + description: Operations related to suggestions diff --git a/doc/api/feature_flags.md b/doc/api/feature_flags.md index 55eb62f9a0d..1aec1006610 100644 --- a/doc/api/feature_flags.md +++ b/doc/api/feature_flags.md @@ -144,7 +144,7 @@ POST /projects/:id/feature_flags | ------------------- | ---------------- | ---------- | ---------------------------------------------------------------------------------------| | `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). | | `name` | string | yes | The name of the feature flag. | -| `version` | string | yes | The version of the feature flag. Must be `new_version_flag`. Omit or set to `legacy_flag` to create a Legacy feature flag. | +| `version` | string | yes | The version of the feature flag. Must be `new_version_flag`. Omit to create a Legacy feature flag. | | `description` | string | no | The description of the feature flag. | | `active` | boolean | no | The active state of the flag. Defaults to true. [Supported](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38350) in GitLab 13.3 and later. | | `strategies` | JSON | no | The feature flag [strategies](../operations/feature_flags.md#feature-flag-strategies). | diff --git a/doc/api/releases/index.md b/doc/api/releases/index.md index e3b1d9230f6..64af547e6d2 100644 --- a/doc/api/releases/index.md +++ b/doc/api/releases/index.md @@ -28,7 +28,7 @@ For authentication, the Releases API accepts either: > [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72448) to allow for `JOB-TOKEN` in GitLab 14.5. -Paginated list of Releases, sorted by `released_at`. +Returns a paginated list of releases, sorted by `released_at`. ```plaintext GET /projects/:id/releases @@ -235,7 +235,7 @@ Example response: > [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72448) to allow for `JOB-TOKEN` in GitLab 14.5. -Get a Release for the given tag. +Gets a release for the given tag. ```plaintext GET /projects/:id/releases/:tag_name @@ -366,7 +366,7 @@ Example response: ## Create a release -Create a release. Developer level access to the project is required to create a release. +Creates a release. Developer level access to the project is required to create a release. ```plaintext POST /projects/:id/releases @@ -516,7 +516,7 @@ adding milestones for ancestor groups raises an error. > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/199065) in GitLab 12.10. > - [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72448) to allow for `JOB-TOKEN` in GitLab 14.5. -Create Evidence for an existing Release. +Creates an evidence for an existing release. ```plaintext POST /projects/:id/releases/:tag_name/evidence @@ -543,7 +543,7 @@ Example response: > [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72448) to allow for `JOB-TOKEN` in GitLab 14.5. -Update a release. Developer level access to the project is required to update a release. +Updates a release. Developer level access to the project is required to update a release. ```plaintext PUT /projects/:id/releases/:tag_name @@ -652,7 +652,7 @@ Example response: > [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72448) to allow for `JOB-TOKEN` in GitLab 14.5. -Delete a release. Deleting a release doesn't delete the associated tag. Maintainer level access to the project is required to delete a release. +Deletes a release. Deleting a release doesn't delete the associated tag. Maintainer level access to the project is required to delete a release. ```plaintext DELETE /projects/:id/releases/:tag_name diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md index 9b17b694194..162b313b531 100644 --- a/doc/development/ee_features.md +++ b/doc/development/ee_features.md @@ -693,7 +693,7 @@ module EE prepended do params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: ::API::API::NAMESPACE_OR_PROJECT_REQUIREMENTS do # ... diff --git a/doc/development/pipelines/index.md b/doc/development/pipelines/index.md index 56a36395afd..26b3c7558cf 100644 --- a/doc/development/pipelines/index.md +++ b/doc/development/pipelines/index.md @@ -257,27 +257,31 @@ set and get the `ee/` folder removed before the tests start running. The intent is to ensure that a change doesn't introduce a failure after `gitlab-org/gitlab` is synced to `gitlab-org/gitlab-foss`. -## As-if-JH jobs +## As-if-JH cross project downstream pipeline -NOTE: -This is disabled for now. - -The `* as-if-jh` jobs run the GitLab test suite "as if JiHu", meaning as if the jobs would run in the context -of [GitLab JH](../jh_features_review.md). These jobs are only created in the following cases: +The `start-as-if-jh` job triggers a cross project downstream pipeline which +runs the GitLab test suite "as if JiHu", meaning as if the pipeline would run +in the context of [GitLab JH](../jh_features_review.md). These jobs are only +created in the following cases: - when the `pipeline:run-as-if-jh` label is set on the merge request -- when the `pipeline:run-all-rspec` label is set on the merge request -- when any code or backstage file is changed -- when any startup CSS file is changed -The `* as-if-jh` jobs are run in addition to the regular EE-context jobs. The `jh/` folder is added before the tests start running. +This pipeline runs under the context of a generated branch in the +[GitLab JH validation](https://gitlab.com/gitlab-org-sandbox/gitlab-jh-validation) +project, which is a mirror of the +[GitLab JH mirror](https://gitlab.com/gitlab-org/gitlab-jh-mirrors/gitlab). -The intent is to ensure that a change doesn't introduce a failure after `gitlab-org/gitlab` is synced to [GitLab JH](https://jihulab.com/gitlab-cn/gitlab). +The generated branch name is prefixed with `as-if-jh/` along with the branch +name in the merge request. This generated branch is based on the merge request +branch, additionally adding changes downloaded from the +[corresponding JH branch](#corresponding-jh-branch) on top to turn the whole +pipeline as if JiHu. -### When to consider applying `pipeline:run-as-if-jh` label +The intent is to ensure that a change doesn't introduce a failure after +[GitLab](https://gitlab.com/gitlab-org/gitlab) is synchronized to +[GitLab JH](https://jihulab.com/gitlab-cn/gitlab). -NOTE: -This is disabled for now. +### When to consider applying `pipeline:run-as-if-jh` label If a Ruby file is renamed and there's a corresponding [`prepend_mod` line](../jh_features_review.md#jh-features-based-on-ce-or-ee-features), it's likely that GitLab JH is relying on it and requires a corresponding @@ -285,17 +289,109 @@ change to rename the module or class it's prepending. ### Corresponding JH branch -NOTE: -This is disabled for now. - You can create a corresponding JH branch on [GitLab JH](https://jihulab.com/gitlab-cn/gitlab) by appending `-jh` to the branch name. If a corresponding JH branch is found, -`* as-if-jh` jobs grab the `jh` folder from the respective branch, -rather than from the default branch `main-jh`. +as-if-jh pipeline grabs files from the respective branch, rather than from the +default branch `main-jh`. NOTE: For now, CI will try to fetch the branch on the [GitLab JH mirror](https://gitlab.com/gitlab-org/gitlab-jh-mirrors/gitlab), so it might take some time for the new JH branch to propagate to the mirror. +NOTE: +While [GitLab JH validation](https://gitlab.com/gitlab-org-sandbox/gitlab-jh-validation) is a mirror of +[GitLab JH mirror](https://gitlab.com/gitlab-org/gitlab-jh-mirrors/gitlab), +it does not include any corresponding JH branch beside the default `main-jh`. +This is why when we want to fetch corresponding JH branch we should fetch it +from the main mirror, rather than the validation project. + +### How as-if-JH pipeline was configured + +The whole process looks like this: + +```mermaid +flowchart TD + subgraph "JiHuLab.com" + JH["gitlab-cn/gitlab"] + end + + subgraph "GitLab.com" + Mirror["gitlab-org/gitlab-jh-mirrors/gitlab"] + Validation["gitlab-org-sandbox/gitlab-jh-validation"] + + subgraph MR["gitlab-org/gitlab merge request"] + Add["add-jh-files job"] + Prepare["prepare-as-if-jh-branch job"] + Add --"download artifacts"--> Prepare + end + + Mirror --"pull mirror with master and main-jh"--> Validation + Mirror --"download JiHu files with ADD_JH_FILES_TOKEN"--> Add + Prepare --"push as-if-jh branches with AS_IF_JH_TOKEN"--> Validation + Validation --> Pipeline["as-if-jh pipeline"] + end + + JH --"pull mirror with corresponding JH branches"--> Mirror +``` + +#### Tokens set in the project variables + +- `ADD_JH_FILES_TOKEN`: This is a [GitLab JH mirror](https://gitlab.com/gitlab-org/gitlab-jh-mirrors/gitlab) + project token with `read_api` permission, to be able to download JiHu files. +- `AS_IF_JH_TOKEN`: This is a [GitLab JH validation](https://gitlab.com/gitlab-org-sandbox/gitlab-jh-validation) + project token with `write_repository` permission, to push generated `as-if-jh/*` branch. + +#### How we generate the as-if-JH branch + +First `add-jh-files` job will download the required JiHu files from the +corresponding JH branch, saving in artifacts. Next `prepare-as-if-jh-branch` +job will create a new branch from the merge request branch, commit the +changes, and finally push the branch to the +[validation project](https://gitlab.com/gitlab-org-sandbox/gitlab-jh-validation). + +#### How we trigger and run the as-if-JH pipeline + +After having the `as-if-jh/*` branch, `start-as-if-jh` job will trigger a pipeline +in the [validation project](https://gitlab.com/gitlab-org-sandbox/gitlab-jh-validation) +to run the cross-project downstream pipeline. + +#### How the GitLab JH mirror project is set up + +The [GitLab JH mirror](https://gitlab.com/gitlab-org/gitlab-jh-mirrors/gitlab) project is private and CI is disabled. + +It's a pull mirror pulling from [GitLab JH](https://jihulab.com/gitlab-cn/gitlab), +mirroring all branches, overriding divergent refs, triggering no pipelines +when mirror is updated. + +The pulling user is [`@gitlab-jh-bot`](https://gitlab.com/gitlab-jh-bot), who +is a maintainer in the project. The credentials can be found in the 1password +engineering vault. + +No password is used from mirroring because GitLab JH is a public project. + +#### How the GitLab JH validation project is set up + +This [GitLab JH validation](https://gitlab.com/gitlab-org-sandbox/gitlab-jh-validation) project is public and CI is enabled, without any project variables. + +It's a pull mirror pulling from [GitLab JH mirror](https://gitlab.com/gitlab-org/gitlab-jh-mirrors/gitlab), +mirroring only protected branches, `master` and `main-jh`, overriding +divergent refs, triggering no pipelines when mirror is updated. + +The pulling user is [`@gitlab-jh-validation-bot`](https://gitlab.com/gitlab-jh-validation-bot), who is a maintainer in the project, and also a +reporter in the +[GitLab JH mirror](https://gitlab.com/gitlab-org/gitlab-jh-mirrors/gitlab). +The credentials can be found in the 1password engineering vault. + +A personal access token from `@gitlab-jh-validation-bot` with +`write_repository` permission is used as the password to pull changes from +the GitLab JH mirror. Username is set with `gitlab-jh-validation-bot`. + +There is also a [pipeline schedule](https://gitlab.com/gitlab-org-sandbox/gitlab-jh-validation/-/pipeline_schedules) +to run maintenance pipelines with variable `SCHEDULE_TYPE` set to `maintenance` +running every day, updating cache. + +The default CI/CD configuration file is also set at `jh/.gitlab-ci.yml` so it +runs exactly like [GitLab JH](https://jihulab.com/gitlab-cn/gitlab/-/blob/main-jh/jh/.gitlab-ci.yml). + ## Ruby 3.0 jobs You can add the `pipeline:run-in-ruby3` label to the merge request to switch diff --git a/doc/development/reusing_abstractions.md b/doc/development/reusing_abstractions.md index 2701192137c..58d1e20394c 100644 --- a/doc/development/reusing_abstractions.md +++ b/doc/development/reusing_abstractions.md @@ -151,7 +151,8 @@ When implementing a service class, consider: developer's discretion, such as: `issue`, `project`, `merge_request`. 1. When service represents an action initiated by a user or executed in the context of a user, the initializer must have the `current_user:` keyword argument. - Services with `current_user:` argument run high-level business logic. + Services with the `current_user:` argument run high-level business logic + and must validate user authorization to perform their operations. 1. When service does not have a user context and it's not directly initiated by a user (like background service or side-effects), the `current_user:` argument is not needed. This describes low-level domain logic or instance-wide logic. diff --git a/doc/install/installation.md b/doc/install/installation.md index 6108ec8d0e0..5149ae9c1e1 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -46,12 +46,12 @@ If the highest number stable branch is unclear, check the [GitLab blog](https:// ## Software requirements -| Software | Minimum version | Notes | -| -------- | --------------- | ----- | -| [Ruby](#2-ruby) | `2.7` | From GitLab 13.6, Ruby 2.7 is required. Ruby 3.0 is not supported yet (see [the relevant epic](https://gitlab.com/groups/gitlab-org/-/epics/5149) for the current status). You must use the standard MRI implementation of Ruby. We love [JRuby](https://www.jruby.org/) and [Rubinius](https://github.com/rubinius/rubinius#the-rubinius-language-platform), but GitLab needs several Gems that have native extensions. | -| [Go](#3-go) | `1.17` | From GitLab 15.2, Go 1.17 or later is required. | -| [Git](#git) | `2.33.x` | From GitLab 14.4, Git 2.33.x and later is required. It's highly recommended that you use the [Git version provided by Gitaly](#git). | -| [Node.js](#4-node) | `14.15.0` | GitLab uses [webpack](https://webpack.js.org/) to compile frontend assets. Node.js 16.x is recommended, as it's faster. You can check which version you're running with `node -v`. You must update it to a newer version if needed. | +| Software | Minimum version | Notes | +| ------------------ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| [Ruby](#2-ruby) | `2.7` | From GitLab 13.6, Ruby 2.7 is required. Ruby 3.0 is not supported yet (see [the relevant epic](https://gitlab.com/groups/gitlab-org/-/epics/5149) for the current status). You must use the standard MRI implementation of Ruby. We love [JRuby](https://www.jruby.org/) and [Rubinius](https://github.com/rubinius/rubinius#the-rubinius-language-platform), but GitLab needs several Gems that have native extensions. | +| [Go](#3-go) | `1.17` | From GitLab 15.2, Go 1.17 or later is required. | +| [Git](#git) | `2.37.x` | From GitLab 15.6, Git 2.37.x and later is required. It's highly recommended that you use the [Git version provided by Gitaly](#git). | +| [Node.js](#4-node) | `14.15.0` | GitLab uses [webpack](https://webpack.js.org/) to compile frontend assets. Node.js 16.x is recommended, as it's faster. You can check which version you're running with `node -v`. You must update it to a newer version if needed. | ## GitLab directory structure diff --git a/doc/operations/incident_management/img/timeline_event_for_severity_change_v15_6.png b/doc/operations/incident_management/img/timeline_event_for_severity_change_v15_6.png Binary files differindex 121cc4b23cb..ae9d446f8b5 100644 --- a/doc/operations/incident_management/img/timeline_event_for_severity_change_v15_6.png +++ b/doc/operations/incident_management/img/timeline_event_for_severity_change_v15_6.png diff --git a/doc/update/index.md b/doc/update/index.md index 38c0df68fc5..2d381d955d1 100644 --- a/doc/update/index.md +++ b/doc/update/index.md @@ -467,6 +467,10 @@ NOTE: Specific information that follow related to Ruby and Git versions do not apply to [Omnibus installations](https://docs.gitlab.com/omnibus/) and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with appropriate Ruby and Git versions and are not using system binaries for Ruby and Git. There is no need to install Ruby or Git when utilizing these two approaches. +### 15.6.0 + +- Git 2.37.0 and later is required by Gitaly. For installations from source, we recommend you use the [Git version provided by Gitaly](../install/installation.md#git). + ### 15.4.0 - GitLab 15.4.0 includes a [batched background migration](#batched-background-migrations) to [remove incorrect values from `expire_at` in `ci_job_artifacts` table](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89318). diff --git a/doc/user/public_access.md b/doc/user/public_access.md index fbab9d9bb51..24b3387e5c6 100644 --- a/doc/user/public_access.md +++ b/doc/user/public_access.md @@ -78,11 +78,12 @@ Prerequisite: ## Change group visibility -Prerequisite: +Prerequisites: - You must have the Owner role for a group. - Subgroups and projects must already have visibility settings that are at least as - restrictive as the new setting of the parent group. + restrictive as the new setting of the parent group. For example, you cannot set a group + to private if a subgroup or project in that group is public. 1. On the top bar, select **Main menu > Groups** and find your project. 1. On the left sidebar, select **Settings > General**. diff --git a/lib/api/alert_management_alerts.rb b/lib/api/alert_management_alerts.rb index f03f133f6f7..f57b7d00c81 100644 --- a/lib/api/alert_management_alerts.rb +++ b/lib/api/alert_management_alerts.rb @@ -6,7 +6,7 @@ module API urgency :low params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' requires :alert_iid, type: Integer, desc: 'The IID of the Alert' end diff --git a/lib/api/api.rb b/lib/api/api.rb index 3400e15394d..0f90ca7c845 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -182,11 +182,14 @@ module API mount ::API::Deployments mount ::API::Environments mount ::API::FeatureFlagsUserLists + mount ::API::FeatureFlags mount ::API::Features mount ::API::FreezePeriods - mount ::API::MergeRequestDiffs + mount ::API::Keys mount ::API::Metadata + mount ::API::MergeRequestDiffs mount ::API::ProjectRepositoryStorageMoves + mount ::API::Releases mount ::API::Release::Links mount ::API::ResourceAccessTokens mount ::API::ProtectedTags @@ -238,7 +241,6 @@ module API mount ::API::ErrorTracking::Collector mount ::API::ErrorTracking::ProjectSettings mount ::API::Events - mount ::API::FeatureFlags mount ::API::Files mount ::API::GenericPackages mount ::API::Geo @@ -263,7 +265,6 @@ module API mount ::API::Invitations mount ::API::IssueLinks mount ::API::Issues - mount ::API::Keys mount ::API::Labels mount ::API::Lint mount ::API::Markdown @@ -301,7 +302,6 @@ module API mount ::API::Projects mount ::API::ProtectedTags mount ::API::PypiPackages - mount ::API::Releases mount ::API::RemoteMirrors mount ::API::Repositories mount ::API::ResourceLabelEvents diff --git a/lib/api/boards.rb b/lib/api/boards.rb index 6e3005ce676..0e0f6441da7 100644 --- a/lib/api/boards.rb +++ b/lib/api/boards.rb @@ -19,7 +19,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do segment ':id/boards' do diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 7e6b0214c03..51c9a4e53df 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -29,7 +29,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Get a project repository branches' do diff --git a/lib/api/ci/job_artifacts.rb b/lib/api/ci/job_artifacts.rb index b3a0a9ef54a..352ad04c982 100644 --- a/lib/api/ci/job_artifacts.rb +++ b/lib/api/ci/job_artifacts.rb @@ -19,7 +19,7 @@ module API prepend_mod_with('API::Ci::JobArtifacts') # rubocop: disable Cop/InjectEnterpriseEditionModule params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Download the artifacts archive from a job' do diff --git a/lib/api/ci/jobs.rb b/lib/api/ci/jobs.rb index 0de915c22e0..770420f8f93 100644 --- a/lib/api/ci/jobs.rb +++ b/lib/api/ci/jobs.rb @@ -11,7 +11,7 @@ module API resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end helpers do diff --git a/lib/api/ci/pipeline_schedules.rb b/lib/api/ci/pipeline_schedules.rb index 886c3509c51..ab2a76a05d3 100644 --- a/lib/api/ci/pipeline_schedules.rb +++ b/lib/api/ci/pipeline_schedules.rb @@ -11,7 +11,7 @@ module API urgency :low params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: ::API::API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Get all pipeline schedules' do diff --git a/lib/api/ci/resource_groups.rb b/lib/api/ci/resource_groups.rb index ea6d3cc8fd4..3ec2d08158e 100644 --- a/lib/api/ci/resource_groups.rb +++ b/lib/api/ci/resource_groups.rb @@ -11,7 +11,7 @@ module API urgency :low params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: ::API::API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Get all resource groups for this project' do diff --git a/lib/api/ci/runners.rb b/lib/api/ci/runners.rb index 5250a9bca2c..988c3f4f566 100644 --- a/lib/api/ci/runners.rb +++ b/lib/api/ci/runners.rb @@ -256,7 +256,9 @@ module API end params do - requires :id, type: String, desc: 'The ID or URL-encoded path of the project owned by the authenticated user' + requires :id, + types: [String, Integer], + desc: 'The ID or URL-encoded path of the project owned by the authenticated user' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do before { authorize_admin_project } diff --git a/lib/api/ci/secure_files.rb b/lib/api/ci/secure_files.rb index 511b6e06cd3..e04718b72f0 100644 --- a/lib/api/ci/secure_files.rb +++ b/lib/api/ci/secure_files.rb @@ -16,7 +16,7 @@ module API default_format :json params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do diff --git a/lib/api/ci/triggers.rb b/lib/api/ci/triggers.rb index 0531b17cf97..2cda597430c 100644 --- a/lib/api/ci/triggers.rb +++ b/lib/api/ci/triggers.rb @@ -11,7 +11,7 @@ module API urgency :low params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Trigger a GitLab project pipeline' do diff --git a/lib/api/ci/variables.rb b/lib/api/ci/variables.rb index 25a06719783..b5eecc844d5 100644 --- a/lib/api/ci/variables.rb +++ b/lib/api/ci/variables.rb @@ -13,7 +13,7 @@ module API helpers ::API::Helpers::VariablesHelpers params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do diff --git a/lib/api/clusters/agent_tokens.rb b/lib/api/clusters/agent_tokens.rb index 02d3fcdf5ea..6ccd41c53a8 100644 --- a/lib/api/clusters/agent_tokens.rb +++ b/lib/api/clusters/agent_tokens.rb @@ -10,7 +10,7 @@ module API feature_category :kubernetes_management params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do params do diff --git a/lib/api/clusters/agents.rb b/lib/api/clusters/agents.rb index b93ad2a946b..62d4fb009c6 100644 --- a/lib/api/clusters/agents.rb +++ b/lib/api/clusters/agents.rb @@ -11,7 +11,7 @@ module API urgency :low params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'List the agents for a project' do diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index 7d8b58fd7b6..859cbd1ce60 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -8,7 +8,7 @@ module API urgency :low params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do include PaginationParams diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 50d0687ba75..b141e3edc71 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -27,7 +27,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS, urgency: :low do desc 'Get a project repository commits' do diff --git a/lib/api/container_repositories.rb b/lib/api/container_repositories.rb index d4fa6153a92..f2dd1fa21fd 100644 --- a/lib/api/container_repositories.rb +++ b/lib/api/container_repositories.rb @@ -14,7 +14,7 @@ module API namespace 'registry' do params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :repositories, requirements: { id: /[0-9]*/ } do desc 'Get a container repository' do diff --git a/lib/api/debian_project_packages.rb b/lib/api/debian_project_packages.rb index 03f0f97b805..1c7b29ab0b9 100644 --- a/lib/api/debian_project_packages.rb +++ b/lib/api/debian_project_packages.rb @@ -34,7 +34,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end namespace ':id/packages/debian' do diff --git a/lib/api/entities/basic_release_details.rb b/lib/api/entities/basic_release_details.rb index d13080f32f4..dba19b3abd7 100644 --- a/lib/api/entities/basic_release_details.rb +++ b/lib/api/entities/basic_release_details.rb @@ -5,12 +5,12 @@ module API class BasicReleaseDetails < Grape::Entity include ::API::Helpers::Presentable - expose :name - expose :tag, as: :tag_name - expose :description - expose :created_at - expose :released_at - expose :upcoming_release?, as: :upcoming_release + expose :name, documentation: { type: 'string', example: 'Release v1.0' } + expose :tag, documentation: { type: 'string', example: 'v1.0' }, as: :tag_name + expose :description, documentation: { type: 'string', example: 'Finally released v1.0' } + expose :created_at, documentation: { type: 'dateTime', example: '2019-01-03T01:56:19.539Z' } + expose :released_at, documentation: { type: 'dateTime', example: '2019-01-03T01:56:19.539Z' } + expose :upcoming_release?, documentation: { type: 'boolean' }, as: :upcoming_release end end end diff --git a/lib/api/entities/feature_flag.rb b/lib/api/entities/feature_flag.rb index 9dec3873504..273307357a2 100644 --- a/lib/api/entities/feature_flag.rb +++ b/lib/api/entities/feature_flag.rb @@ -3,12 +3,12 @@ module API module Entities class FeatureFlag < Grape::Entity - expose :name - expose :description - expose :active - expose :version - expose :created_at - expose :updated_at + expose :name, documentation: { type: 'string', example: 'merge_train' } + expose :description, documentation: { type: 'string', example: 'merge train feature flag' } + expose :active, documentation: { type: 'boolean' } + expose :version, documentation: { type: 'string', example: 'new_version_flag' } + expose :created_at, documentation: { type: 'dateTime', example: '2019-11-04T08:13:51.423Z' } + expose :updated_at, documentation: { type: 'dateTime', example: '2019-11-04T08:13:51.423Z' } expose :scopes do |_ff| [] end diff --git a/lib/api/entities/feature_flag/scope.rb b/lib/api/entities/feature_flag/scope.rb index 906fe718257..e29793c250a 100644 --- a/lib/api/entities/feature_flag/scope.rb +++ b/lib/api/entities/feature_flag/scope.rb @@ -4,8 +4,8 @@ module API module Entities class FeatureFlag < Grape::Entity class Scope < Grape::Entity - expose :id - expose :environment_scope + expose :id, documentation: { type: 'integer', example: 1 } + expose :environment_scope, documentation: { type: 'string', example: 'production' } end end end diff --git a/lib/api/entities/feature_flag/strategy.rb b/lib/api/entities/feature_flag/strategy.rb index 32699be0ee3..62178420370 100644 --- a/lib/api/entities/feature_flag/strategy.rb +++ b/lib/api/entities/feature_flag/strategy.rb @@ -4,9 +4,9 @@ module API module Entities class FeatureFlag < Grape::Entity class Strategy < Grape::Entity - expose :id - expose :name - expose :parameters + expose :id, documentation: { type: 'integer', example: 1 } + expose :name, documentation: { type: 'string', example: 'userWithId' } + expose :parameters, documentation: { type: 'string', example: '{"userIds": "user1"}' } expose :scopes, using: FeatureFlag::Scope end end diff --git a/lib/api/entities/release.rb b/lib/api/entities/release.rb index 2403c907f7f..5feb1edbff6 100644 --- a/lib/api/entities/release.rb +++ b/lib/api/entities/release.rb @@ -16,11 +16,13 @@ module API release.milestones.order_by_dates_and_title end - expose :commit_path, expose_nil: false - expose :tag_path, expose_nil: false + expose :commit_path, + documentation: { type: 'string', example: '/root/app/commit/588440f66559714280628a4f9799f0c4eb880a4a' }, + expose_nil: false + expose :tag_path, documentation: { type: 'string', example: '/root/app/-/tags/v1.0' }, expose_nil: false expose :assets do - expose :assets_count, as: :count + expose :assets_count, documentation: { type: 'integer', example: 2 }, as: :count expose :sources, using: Entities::Releases::Source, if: ->(_, _) { can_download_code? } expose :sorted_links, as: :links, using: Entities::Releases::Link end diff --git a/lib/api/entities/releases/evidence.rb b/lib/api/entities/releases/evidence.rb index 01603a71dbf..9d324309213 100644 --- a/lib/api/entities/releases/evidence.rb +++ b/lib/api/entities/releases/evidence.rb @@ -6,9 +6,9 @@ module API class Evidence < Grape::Entity include ::API::Helpers::Presentable - expose :sha - expose :filepath - expose :collected_at + expose :sha, documentation: { type: 'string', example: '760d6cdfb0879c3ffedec13af470e0f71cf52c6cde4d' } + expose :filepath, documentation: { type: 'string', example: 'https://gitlab.example.com/root/app/-/releases/v1.0/evidence.json' } + expose :collected_at, documentation: { type: 'dateTime', example: '2019-01-03T01:56:19.539Z' } end end end diff --git a/lib/api/entities/releases/link.rb b/lib/api/entities/releases/link.rb index 5157645af69..abf380e11d5 100644 --- a/lib/api/entities/releases/link.rb +++ b/lib/api/entities/releases/link.rb @@ -4,14 +4,22 @@ module API module Entities module Releases class Link < Grape::Entity - expose :id - expose :name - expose :url - expose :direct_asset_url do |link| + expose :id, documentation: { type: 'integer', example: 1 } + expose :name, documentation: { type: 'string', example: 'app-v1.0.dmg' } + expose :url, documentation: + { + type: 'string', + example: 'https://gitlab.example.com/root/app/-/jobs/688/artifacts/raw/bin/app-v1.0.dmg' + } + expose :direct_asset_url, documentation: + { + type: 'string', + example: 'https://gitlab.example.com/root/app/-/releases/v1.0/downloads/app-v1.0.dmg' + } do |link| ::Releases::LinkPresenter.new(link).direct_asset_url end - expose :external?, as: :external - expose :link_type + expose :external?, documentation: { type: 'boolean' }, as: :external + expose :link_type, documentation: { type: 'string', example: 'other' } end end end diff --git a/lib/api/entities/releases/source.rb b/lib/api/entities/releases/source.rb index 2b0c8038ddf..8c6750d6142 100644 --- a/lib/api/entities/releases/source.rb +++ b/lib/api/entities/releases/source.rb @@ -4,8 +4,8 @@ module API module Entities module Releases class Source < Grape::Entity - expose :format - expose :url + expose :format, documentation: { type: 'string', example: 'zip' } + expose :url, documentation: { type: 'string', example: 'https://gitlab.example.com/root/app/-/archive/v1.0/app-v1.0.zip' } end end end diff --git a/lib/api/entities/ssh_key.rb b/lib/api/entities/ssh_key.rb index e1554730cb6..3db10bb8ec2 100644 --- a/lib/api/entities/ssh_key.rb +++ b/lib/api/entities/ssh_key.rb @@ -3,8 +3,15 @@ module API module Entities class SSHKey < Grape::Entity - expose :id, :title, :created_at, :expires_at - expose :publishable_key, as: :key + expose :id, documentation: { type: 'integer', example: 1 } + expose :title, documentation: { type: 'string', example: 'Sample key 25' } + expose :created_at, documentation: { type: 'dateTime', example: '2015-09-03T07:24:44.627Z' } + expose :expires_at, documentation: { type: 'dateTime', example: '2020-09-03T07:24:44.627Z' } + expose :publishable_key, as: :key, documentation: + { type: 'string', + example: 'ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1256k6Yjz\ + GGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCdd\ + NaP0L+hM7zhFNzjFvpaMgJw0=' } end end end diff --git a/lib/api/entities/user_basic.rb b/lib/api/entities/user_basic.rb index b8ee4e5a6e0..9f20e0caa72 100644 --- a/lib/api/entities/user_basic.rb +++ b/lib/api/entities/user_basic.rb @@ -3,16 +3,16 @@ module API module Entities class UserBasic < UserSafe - expose :state + expose :state, documentation: { type: 'string', example: 'active' } - expose :avatar_url do |user, options| + expose :avatar_url, documentation: { type: 'string', example: 'https://gravatar.com/avatar/1' } do |user, options| user.avatar_url(only_path: false) end expose :avatar_path, if: ->(user, options) { options.fetch(:only_path, false) && user.avatar_path } expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes - expose :web_url do |user, options| + expose :web_url, documentation: { type: 'string', example: 'https://gitlab.example.com/root' } do |user, options| Gitlab::Routing.url_helpers.user_url(user) end end diff --git a/lib/api/entities/user_public.rb b/lib/api/entities/user_public.rb index 5d0e464abe1..9e21c5e12b6 100644 --- a/lib/api/entities/user_public.rb +++ b/lib/api/entities/user_public.rb @@ -3,17 +3,23 @@ module API module Entities class UserPublic < Entities::User - expose :last_sign_in_at - expose :confirmed_at - expose :last_activity_on - expose :email - expose :theme_id, :color_scheme_id, :projects_limit, :current_sign_in_at + expose :last_sign_in_at, documentation: { type: 'dateTime', example: '2015-09-03T07:24:01.670Z' } + expose :confirmed_at, documentation: { type: 'dateTime', example: '2015-09-03T07:24:01.670Z' } + expose :last_activity_on, documentation: { type: 'dateTime', example: '2015-09-03T07:24:01.670Z' } + expose :email, documentation: { type: 'string', example: 'john@example.com' } + expose :theme_id, documentation: { type: 'integer', example: 2 } + expose :color_scheme_id, documentation: { type: 'integer', example: 1 } + expose :projects_limit, documentation: { type: 'integer', example: 10 } + expose :current_sign_in_at, documentation: { type: 'dateTime', example: '2015-09-03T07:24:01.670Z' } expose :identities, using: Entities::Identity - expose :can_create_group?, as: :can_create_group - expose :can_create_project?, as: :can_create_project - expose :two_factor_enabled?, as: :two_factor_enabled - expose :external - expose :private_profile + expose :can_create_group?, as: :can_create_group, documentation: { type: 'boolean', example: true } + expose :can_create_project?, as: :can_create_project, documentation: { type: 'boolean', example: true } + + expose :two_factor_enabled?, as: :two_factor_enabled, documentation: { type: 'boolean', example: true } + + expose :external, documentation: { type: 'boolean', example: false } + + expose :private_profile, documentation: { type: 'boolean', example: :null } expose :commit_email_or_default, as: :commit_email end end diff --git a/lib/api/entities/user_safe.rb b/lib/api/entities/user_safe.rb index 127a8ef2160..0fbb10307cf 100644 --- a/lib/api/entities/user_safe.rb +++ b/lib/api/entities/user_safe.rb @@ -5,8 +5,9 @@ module API class UserSafe < Grape::Entity include RequestAwareEntity - expose :id, :username - expose :name do |user| + expose :id, documentation: { type: 'integer', example: 1 } + expose :username, documentation: { type: 'string', example: 'admin' } + expose :name, documentation: { type: 'string', example: 'Administrator' } do |user| current_user = request.respond_to?(:current_user) ? request.current_user : options.fetch(:current_user, nil) user.redacted_name(current_user) diff --git a/lib/api/error_tracking/client_keys.rb b/lib/api/error_tracking/client_keys.rb index c1c378111a7..b8104d4fc11 100644 --- a/lib/api/error_tracking/client_keys.rb +++ b/lib/api/error_tracking/client_keys.rb @@ -8,7 +8,7 @@ module API urgency :low params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do diff --git a/lib/api/error_tracking/collector.rb b/lib/api/error_tracking/collector.rb index eea0fd2bce9..e10125e02c6 100644 --- a/lib/api/error_tracking/collector.rb +++ b/lib/api/error_tracking/collector.rb @@ -67,7 +67,7 @@ module API detail 'This feature was introduced in GitLab 14.1.' end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end post 'error_tracking/collector/api/:id/envelope' do # There is a reason why we have such uncommon path. @@ -119,7 +119,7 @@ module API detail 'This feature was introduced in GitLab 14.1.' end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end post 'error_tracking/collector/api/:id/store' do # There is a reason why we have such uncommon path. diff --git a/lib/api/error_tracking/project_settings.rb b/lib/api/error_tracking/project_settings.rb index fefc2098137..01a55fbbd55 100644 --- a/lib/api/error_tracking/project_settings.rb +++ b/lib/api/error_tracking/project_settings.rb @@ -14,7 +14,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do diff --git a/lib/api/feature_flags.rb b/lib/api/feature_flags.rb index 67e96284449..b1599ab9b47 100644 --- a/lib/api/feature_flags.rb +++ b/lib/api/feature_flags.rb @@ -15,18 +15,20 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource 'projects/:id', requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do resource :feature_flags do - desc 'Get all feature flags of a project' do - detail 'This feature was introduced in GitLab 12.5' + desc 'List feature flags for a project' do + detail 'Gets all feature flags of the requested project. This feature was introduced in GitLab 12.5.' success ::API::Entities::FeatureFlag + is_array true + tags %w[feature_flags] end params do optional :scope, type: String, - desc: 'The scope of feature flags', + desc: 'The scope of feature flags, one of: `enabled`, `disabled`', values: %w[enabled disabled] use :pagination end @@ -39,22 +41,18 @@ module API end desc 'Create a new feature flag' do - detail 'This feature was introduced in GitLab 12.5' + detail 'Creates a new feature flag. This feature was introduced in GitLab 12.5.' success ::API::Entities::FeatureFlag + tags %w[feature_flags] end params do - requires :name, type: String, desc: 'The name of feature flag' + requires :name, type: String, desc: 'The name of the feature flag' optional :description, type: String, desc: 'The description of the feature flag' - optional :active, type: Boolean, desc: 'Active/inactive value of the flag' - optional :version, type: String, desc: 'The version of the feature flag' - optional :scopes, type: Array do - requires :environment_scope, type: String, desc: 'The environment scope of the scope' - requires :active, type: Boolean, desc: 'Active/inactive of the scope' - requires :strategies, type: JSON, desc: 'The strategies of the scope' - end + optional :active, type: Boolean, desc: 'The active state of the flag. Defaults to `true`. Supported in GitLab 13.3 and later' + optional :version, type: String, desc: 'The version of the feature flag. Must be `new_version_flag`. Omit to create a Legacy feature flag.' optional :strategies, type: Array do - requires :name, type: String, desc: 'The strategy name' - requires :parameters, type: JSON, desc: 'The strategy parameters' + requires :name, type: String, desc: 'The strategy name. Can be `default`, `gradualRolloutUserId`, `userWithId`, or `gitlabUserList`. In GitLab 13.5 and later, can be `flexibleRollout`' + requires :parameters, type: JSON, desc: 'The strategy parameters as a JSON-formatted string e.g. `{"userIds":"user1"}`', documentation: { type: 'String' } optional :scopes, type: Array do requires :environment_scope, type: String, desc: 'The environment scope of the scope' end @@ -87,9 +85,10 @@ module API requires :feature_flag_name, type: String, desc: 'The name of the feature flag' end resource 'feature_flags/:feature_flag_name', requirements: FEATURE_FLAG_ENDPOINT_REQUIREMENTS do - desc 'Get a feature flag of a project' do - detail 'This feature was introduced in GitLab 12.5' + desc 'Get a single feature flag' do + detail 'Gets a single feature flag. This feature was introduced in GitLab 12.5.' success ::API::Entities::FeatureFlag + tags %w[feature_flags] end get do authorize_read_feature_flag! @@ -99,20 +98,21 @@ module API end desc 'Update a feature flag' do - detail 'This feature was introduced in GitLab 13.2' + detail 'Updates a feature flag. This feature was introduced in GitLab 13.2.' success ::API::Entities::FeatureFlag + tags %w[feature_flags] end params do - optional :name, type: String, desc: 'The name of the feature flag' + optional :name, type: String, desc: 'The new name of the feature flag. Supported in GitLab 13.3 and later' optional :description, type: String, desc: 'The description of the feature flag' - optional :active, type: Boolean, desc: 'Active/inactive value of the flag' + optional :active, type: Boolean, desc: 'The active state of the flag. Supported in GitLab 13.3 and later' optional :strategies, type: Array do - optional :id, type: Integer, desc: 'The strategy id' - optional :name, type: String, desc: 'The strategy type' - optional :parameters, type: JSON, desc: 'The strategy parameters' + optional :id, type: Integer, desc: 'The feature flag strategy ID' + optional :name, type: String, desc: 'The strategy name' + optional :parameters, type: JSON, desc: 'The strategy parameters as a JSON-formatted string e.g. `{"userIds":"user1"}`', documentation: { type: 'String' } optional :_destroy, type: Boolean, desc: 'Delete the strategy when true' optional :scopes, type: Array do - optional :id, type: Integer, desc: 'The environment scope id' + optional :id, type: Integer, desc: 'The scope id' optional :environment_scope, type: String, desc: 'The environment scope of the scope' optional :_destroy, type: Boolean, desc: 'Delete the scope when true' end @@ -142,8 +142,9 @@ module API end desc 'Delete a feature flag' do - detail 'This feature was introduced in GitLab 12.5' + detail 'Deletes a feature flag. This feature was introduced in GitLab 12.5.' success ::API::Entities::FeatureFlag + tags %w[feature_flags] end delete do authorize_destroy_feature_flag! diff --git a/lib/api/generic_packages.rb b/lib/api/generic_packages.rb index 0098b074f05..f7785cb8b52 100644 --- a/lib/api/generic_packages.rb +++ b/lib/api/generic_packages.rb @@ -18,7 +18,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do diff --git a/lib/api/go_proxy.rb b/lib/api/go_proxy.rb index 2d9c0cd6ce1..fbc1d12e755 100755 --- a/lib/api/go_proxy.rb +++ b/lib/api/go_proxy.rb @@ -17,6 +17,10 @@ module API before { require_packages_enabled! } helpers do + def project + user_project(action: :read_package) + end + def case_decode(str) # Converts "github.com/!azure" to "github.com/Azure" # @@ -32,12 +36,12 @@ module API end def find_module - not_found! unless Feature.enabled?(:go_proxy, user_project) + not_found! unless Feature.enabled?(:go_proxy, project) module_name = case_decode params[:module_name] bad_request_missing_attribute!('Module Name') if module_name.blank? - mod = ::Packages::Go::ModuleFinder.new(user_project, module_name).execute + mod = ::Packages::Go::ModuleFinder.new(project, module_name).execute not_found! unless mod @@ -58,13 +62,13 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' requires :module_name, type: String, desc: 'Module name', coerce_with: ->(val) { CGI.unescape(val) } end route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, authenticate_non_public: true resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do before do - authorize_read_package! + authorize_read_package!(project) end namespace ':id/packages/go/*module_name/@v' do diff --git a/lib/api/helpers/packages_helpers.rb b/lib/api/helpers/packages_helpers.rb index 687c8330cc8..96a10d43401 100644 --- a/lib/api/helpers/packages_helpers.rb +++ b/lib/api/helpers/packages_helpers.rb @@ -3,6 +3,8 @@ module API module Helpers module PackagesHelpers + extend ::Gitlab::Utils::Override + MAX_PACKAGE_FILE_SIZE = 50.megabytes.freeze def require_packages_enabled! @@ -48,6 +50,34 @@ module API require_gitlab_workhorse! end + override :user_project + def user_project(action: :read_project) + case action + when :read_project + super() + when :read_package + user_project_with_read_package + else + raise ArgumentError, "unexpected action: #{action}" + end + end + + # This function is similar to the `find_project!` function, but it considers the `read_package` ability. + def user_project_with_read_package + strong_memoize(:user_project_with_read_package) do + project = find_project(params[:id]) + + next forbidden! unless authorized_project_scope?(project) + + next project if can?(current_user, :read_package, project&.packages_policy_subject) + # guest users can have :read_project but not :read_package + next forbidden! if can?(current_user, :read_project, project) + next unauthorized! if authenticate_non_public? + + not_found!('Project') + end + end + def track_package_event(event_name, scope, **args) ::Packages::CreateEventService.new(nil, current_user, event_name: event_name, scope: scope).execute category = args.delete(:category) || self.options[:for].name diff --git a/lib/api/integrations.rb b/lib/api/integrations.rb index 71c55704ddf..4eb5a966fc7 100644 --- a/lib/api/integrations.rb +++ b/lib/api/integrations.rb @@ -65,7 +65,7 @@ module API # The support for `:id/services` can be dropped if we create an API V5. [':id/services', ':id/integrations'].each do |path| params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do before { authenticate! } @@ -149,7 +149,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc "Trigger a slash command for #{integration_slug}" do diff --git a/lib/api/issue_links.rb b/lib/api/issue_links.rb index 563fb3358ed..0f92f7aeb91 100644 --- a/lib/api/issue_links.rb +++ b/lib/api/issue_links.rb @@ -10,7 +10,7 @@ module API urgency :low params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue' end resource :projects, requirements: { id: %r{[^/]+} } do diff --git a/lib/api/issues.rb b/lib/api/issues.rb index b8b4019765d..b08819e34e3 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -198,7 +198,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do include TimeTrackingEndpoints diff --git a/lib/api/keys.rb b/lib/api/keys.rb index fb1bedd5e92..77952bac01a 100644 --- a/lib/api/keys.rb +++ b/lib/api/keys.rb @@ -9,8 +9,13 @@ module API resource :keys do desc 'Get single ssh key by id. Only available to admin users' do + detail 'Get SSH key with user by ID of an SSH key. Note only administrators can lookup SSH key with user by ID\ + of an SSH key' success Entities::SSHKeyWithUser end + params do + requires :id, types: [String, Integer], desc: 'The ID of an SSH key', documentation: { example: '2' } + end get ":id" do authenticated_as_admin! @@ -19,11 +24,14 @@ module API present key, with: Entities::SSHKeyWithUser, current_user: current_user end - desc 'Get SSH Key information' do + desc 'Get user by fingerprint of SSH key' do success Entities::UserWithAdmin + detail 'You can search for a user that owns a specific SSH key. Note only administrators can lookup SSH key\ + with the fingerprint of an SSH key' end params do - requires :fingerprint, type: String, desc: 'Search for a SSH fingerprint' + requires :fingerprint, type: String, desc: 'The fingerprint of an SSH key', + documentation: { example: 'ba:81:59:68:d7:6c:cd:02:02:bf:6a:9b:55:4e:af:d1' } end get do authenticated_with_can_read_all_resources! diff --git a/lib/api/labels.rb b/lib/api/labels.rb index 0a107a96d61..2e00affbbdf 100644 --- a/lib/api/labels.rb +++ b/lib/api/labels.rb @@ -15,7 +15,7 @@ module API label_id: API::NO_SLASH_URL_PART_REGEX) params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: LABEL_ENDPOINT_REQUIREMENTS do desc 'Get all labels of the project' do diff --git a/lib/api/maven_packages.rb b/lib/api/maven_packages.rb index 72313d6a588..e5799352fe9 100644 --- a/lib/api/maven_packages.rb +++ b/lib/api/maven_packages.rb @@ -220,7 +220,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Download the maven package file' do diff --git a/lib/api/merge_request_diffs.rb b/lib/api/merge_request_diffs.rb index 26841bf6644..c7f0f88eacc 100644 --- a/lib/api/merge_request_diffs.rb +++ b/lib/api/merge_request_diffs.rb @@ -10,7 +10,7 @@ module API feature_category :code_review params do - requires :id, type: String, desc: 'The ID of the project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Get a list of merge request diff versions' do diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index a0e7d0b10cd..bb2861aa221 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -170,7 +170,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do include TimeTrackingEndpoints diff --git a/lib/api/ml/mlflow.rb b/lib/api/ml/mlflow.rb index 356a4ef53d4..56bfac1530e 100644 --- a/lib/api/ml/mlflow.rb +++ b/lib/api/ml/mlflow.rb @@ -80,7 +80,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'API to interface with MLFlow Client, REST API version 1.28.0' do diff --git a/lib/api/npm_project_packages.rb b/lib/api/npm_project_packages.rb index 166c0b755fe..494b493f5e0 100644 --- a/lib/api/npm_project_packages.rb +++ b/lib/api/npm_project_packages.rb @@ -11,7 +11,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end namespace 'projects/:id/packages/npm' do desc 'Download the NPM tarball' do diff --git a/lib/api/nuget_project_packages.rb b/lib/api/nuget_project_packages.rb index 3e05ea13311..cc4e9777b07 100644 --- a/lib/api/nuget_project_packages.rb +++ b/lib/api/nuget_project_packages.rb @@ -91,7 +91,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project', regexp: ::API::Concerns::Packages::NugetEndpoints::POSITIVE_INTEGER_REGEX + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project', regexp: ::API::Concerns::Packages::NugetEndpoints::POSITIVE_INTEGER_REGEX end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do namespace ':id/packages/nuget' do diff --git a/lib/api/package_files.rb b/lib/api/package_files.rb index 278dc4c2044..bf1cd88c18c 100644 --- a/lib/api/package_files.rb +++ b/lib/api/package_files.rb @@ -14,7 +14,7 @@ module API helpers ::API::Helpers::PackagesHelpers params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' requires :package_id, type: Integer, desc: 'The ID of a package' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do diff --git a/lib/api/pages.rb b/lib/api/pages.rb index 5f695f3853d..7e230bd3c67 100644 --- a/lib/api/pages.rb +++ b/lib/api/pages.rb @@ -10,7 +10,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Unpublish pages' do diff --git a/lib/api/pages_domains.rb b/lib/api/pages_domains.rb index 9cf61967ba4..967847a8e62 100644 --- a/lib/api/pages_domains.rb +++ b/lib/api/pages_domains.rb @@ -54,7 +54,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do before do diff --git a/lib/api/project_clusters.rb b/lib/api/project_clusters.rb index 4644d38ea80..6faa549d787 100644 --- a/lib/api/project_clusters.rb +++ b/lib/api/project_clusters.rb @@ -13,7 +13,7 @@ module API urgency :low params do - requires :id, type: String, desc: 'The ID of the project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Get all clusters from the project' do diff --git a/lib/api/project_container_repositories.rb b/lib/api/project_container_repositories.rb index 6a6275ed02a..2ee7b73c74c 100644 --- a/lib/api/project_container_repositories.rb +++ b/lib/api/project_container_repositories.rb @@ -16,7 +16,7 @@ module API urgency :low params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end route_setting :authentication, job_token_allowed: true, job_token_scope: :project resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do diff --git a/lib/api/project_debian_distributions.rb b/lib/api/project_debian_distributions.rb index b8ca9428fa3..1e27f5c8856 100644 --- a/lib/api/project_debian_distributions.rb +++ b/lib/api/project_debian_distributions.rb @@ -3,7 +3,7 @@ module API class ProjectDebianDistributions < ::API::Base params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end before do diff --git a/lib/api/project_events.rb b/lib/api/project_events.rb index e8829216336..6315a6c9b1a 100644 --- a/lib/api/project_events.rb +++ b/lib/api/project_events.rb @@ -12,7 +12,7 @@ module API urgency :low params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc "List a Project's visible events" do diff --git a/lib/api/project_export.rb b/lib/api/project_export.rb index 29fdfe45566..8f3f50adc86 100644 --- a/lib/api/project_export.rb +++ b/lib/api/project_export.rb @@ -11,7 +11,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: { id: %r{[^/]+} } do desc 'Get export status' do diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb index 466e80d68c8..008fb5da0b2 100644 --- a/lib/api/project_hooks.rb +++ b/lib/api/project_hooks.rb @@ -37,7 +37,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do namespace ':id/hooks' do diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb index 0da8c1ecedd..268a238421e 100644 --- a/lib/api/project_import.rb +++ b/lib/api/project_import.rb @@ -108,7 +108,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end desc 'Get a project import status' do detail 'This feature was introduced in GitLab 10.6.' diff --git a/lib/api/project_milestones.rb b/lib/api/project_milestones.rb index 9f82dbf9813..a7a583aaa23 100644 --- a/lib/api/project_milestones.rb +++ b/lib/api/project_milestones.rb @@ -11,7 +11,7 @@ module API urgency :low params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Get a list of project milestones' do diff --git a/lib/api/project_packages.rb b/lib/api/project_packages.rb index 800966408fc..d09c481403f 100644 --- a/lib/api/project_packages.rb +++ b/lib/api/project_packages.rb @@ -14,7 +14,7 @@ module API helpers ::API::Helpers::PackagesHelpers params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Get all project packages' do diff --git a/lib/api/project_repository_storage_moves.rb b/lib/api/project_repository_storage_moves.rb index b9cb2e957bc..817dc49c431 100644 --- a/lib/api/project_repository_storage_moves.rb +++ b/lib/api/project_repository_storage_moves.rb @@ -55,7 +55,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Get a list of all project repository storage moves' do diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb index 14792730eae..743cd4e4fbb 100644 --- a/lib/api/project_snippets.rb +++ b/lib/api/project_snippets.rb @@ -9,7 +9,7 @@ module API feature_category :snippets params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do helpers Helpers::SnippetsHelpers diff --git a/lib/api/project_statistics.rb b/lib/api/project_statistics.rb index 3db8d20ebac..445feebc2b9 100644 --- a/lib/api/project_statistics.rb +++ b/lib/api/project_statistics.rb @@ -10,7 +10,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Get the list of project fetch statistics for the last 30 days' diff --git a/lib/api/project_templates.rb b/lib/api/project_templates.rb index f6e1286d616..6c3e6368113 100644 --- a/lib/api/project_templates.rb +++ b/lib/api/project_templates.rb @@ -15,7 +15,7 @@ module API feature_category :source_code_management params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' requires :type, type: String, values: TEMPLATE_TYPES, desc: 'The type (dockerfiles|gitignores|gitlab_ci_ymls|licenses|metrics_dashboard_ymls|issues|merge_requests) of the template' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do diff --git a/lib/api/projects.rb b/lib/api/projects.rb index e579d18bb3f..81ccd6ba349 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -336,7 +336,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Get a single project' do diff --git a/lib/api/protected_branches.rb b/lib/api/protected_branches.rb index 443b3e90dc3..786045684b8 100644 --- a/lib/api/protected_branches.rb +++ b/lib/api/protected_branches.rb @@ -13,7 +13,10 @@ module API helpers Helpers::ProtectedBranchesHelpers params do - requires :id, type: String, desc: 'The ID of a project', documentation: { example: 'gitlab-org/gitlab' } + requires :id, + types: [String, Integer], + desc: 'The ID or URL-encoded path of the project', + documentation: { example: 'gitlab-org/gitlab' } end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc "Get a project's protected branches" do diff --git a/lib/api/protected_tags.rb b/lib/api/protected_tags.rb index 7d4d15ab4b3..7b55b1fd61d 100644 --- a/lib/api/protected_tags.rb +++ b/lib/api/protected_tags.rb @@ -13,7 +13,7 @@ module API helpers Helpers::ProtectedTagsHelpers params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc "Get a project's protected tags" do diff --git a/lib/api/pypi_packages.rb b/lib/api/pypi_packages.rb index a2386411524..ffc88d1c1cf 100644 --- a/lib/api/pypi_packages.rb +++ b/lib/api/pypi_packages.rb @@ -157,7 +157,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do diff --git a/lib/api/releases.rb b/lib/api/releases.rb index cdfcce9dddb..ec9907b18f9 100644 --- a/lib/api/releases.rb +++ b/lib/api/releases.rb @@ -4,6 +4,8 @@ module API class Releases < ::API::Base include PaginationParams + releases_tags = %w[releases] + RELEASE_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS .merge(tag_name: API::NO_SLASH_URL_PART_REGEX) RELEASE_CLI_USER_AGENT = 'GitLab-release-cli' @@ -12,20 +14,37 @@ module API urgency :low params do - requires :id, type: String, desc: 'The ID of a group' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the group' end resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do before { authorize_read_group_releases! } - desc 'Get a list of releases for projects in this group.' do + desc 'List group releases' do + detail 'Returns a list of group releases.' success Entities::Release + failure [ + { code: 400, message: 'Bad request' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not found' } + ] + is_array true + tags releases_tags end params do - requires :id, type: Integer, desc: 'The ID of the group to get releases for' - optional :sort, type: String, values: %w[asc desc], default: 'desc', - desc: 'Return projects sorted in ascending and descending order by released_at' - optional :simple, type: Boolean, default: false, - desc: 'Return only the ID, URL, name, and path of each project' + requires :id, + types: [String, Integer], + desc: 'The ID or URL-encoded path of the group owned by the authenticated user' + + optional :sort, + type: String, + values: %w[asc desc], + default: 'desc', + desc: 'The direction of the order. Either `desc` (default) for descending order or `asc` for ascending order' + + optional :simple, + type: Boolean, + default: false, + desc: 'Return only limited fields for each release' use :pagination end @@ -42,26 +61,38 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do before { authorize_read_releases! } after { track_release_event } - desc 'Get a project releases' do - detail 'This feature was introduced in GitLab 11.7.' + desc 'List Releases' do + detail 'Returns a paginated list of releases. This feature was introduced in GitLab 11.7.' named 'get_releases' + is_array true success Entities::Release + tags releases_tags end params do use :pagination - optional :order_by, type: String, values: %w[released_at created_at], default: 'released_at', - desc: 'Return releases ordered by `released_at` or `created_at`.' - optional :sort, type: String, values: %w[asc desc], default: 'desc', - desc: 'Return releases sorted in `asc` or `desc` order.' - optional :include_html_description, type: Boolean, - desc: 'If `true`, a response includes HTML rendered markdown of the release description.' + + optional :order_by, + type: String, + values: %w[released_at created_at], + default: 'released_at', + desc: 'The field to use as order. Either `released_at` (default) or `created_at`' + + optional :sort, + type: String, + values: %w[asc desc], + default: 'desc', + desc: 'The direction of the order. Either `desc` (default) for descending order or `asc` for ascending order' + + optional :include_html_description, + type: Boolean, + desc: 'If `true`, a response includes HTML rendered markdown of the release description' end route_setting :authentication, job_token_allowed: true get ':id/releases' do @@ -81,15 +112,22 @@ module API include_html_description: params[:include_html_description] end - desc 'Get a single project release' do - detail 'This feature was introduced in GitLab 11.7.' + desc 'Get a release by a tag name' do + detail 'Gets a release for the given tag. This feature was introduced in GitLab 11.7.' named 'get_release' success Entities::Release + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' } + ] + tags releases_tags end params do - requires :tag_name, type: String, desc: 'The name of the tag', as: :tag - optional :include_html_description, type: Boolean, - desc: 'If `true`, a response includes HTML rendered markdown of the release description.' + requires :tag_name, type: String, desc: 'The Git tag the release is associated with', as: :tag + + optional :include_html_description, + type: Boolean, + desc: 'If `true`, a response includes HTML rendered markdown of the release description' end route_setting :authentication, job_token_allowed: true get ':id/releases/:tag_name', requirements: RELEASE_ENDPOINT_REQUIREMENTS do @@ -103,13 +141,19 @@ module API desc 'Download a project release asset file' do detail 'This feature was introduced in GitLab 15.4.' named 'download_release_asset_file' + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' } + ] + tags releases_tags end params do - requires :tag_name, type: String, - desc: 'The name of the tag.', as: :tag - requires :file_path, type: String, - file_path: true, - desc: 'The path to the file to download, as specified when creating the release asset.' + requires :tag_name, type: String, desc: 'The Git tag the release is associated with', as: :tag + + requires :file_path, + type: String, + file_path: true, + desc: 'The path to the file to download, as specified when creating the release asset' end route_setting :authentication, job_token_allowed: true get ':id/releases/:tag_name/downloads/*file_path', format: false, requirements: RELEASE_ENDPOINT_REQUIREMENTS do @@ -127,9 +171,17 @@ module API desc 'Get the latest project release' do detail 'This feature was introduced in GitLab 15.4.' named 'get_latest_release' + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' } + ] + tags releases_tags end params do - requires :suffix_path, type: String, file_path: true, desc: 'The path to be suffixed to the latest release' + requires :suffix_path, + type: String, + file_path: true, + desc: 'The path to be suffixed to the latest release' end route_setting :authentication, job_token_allowed: true get ':id/releases/permalink/latest(/)(*suffix_path)', format: false, requirements: RELEASE_ENDPOINT_REQUIREMENTS do @@ -156,27 +208,50 @@ module API redirect redirect_url end - desc 'Create a new release' do - detail 'This feature was introduced in GitLab 11.7.' + desc 'Create a release' do + detail 'Creates a release. Developer level access to the project is required to create a release. This feature was introduced in GitLab 11.7.' named 'create_release' success Entities::Release + failure [ + { code: 400, message: 'Bad request' }, + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not found' }, + { code: 409, message: 'Conflict' }, + { code: 422, message: 'Unprocessable entity' } + ] + tags releases_tags end params do - requires :tag_name, type: String, desc: 'The name of the tag', as: :tag + requires :tag_name, type: String, desc: 'The tag where the release is created from', as: :tag optional :tag_message, type: String, desc: 'Message to use if creating a new annotated tag' - optional :name, type: String, desc: 'The name of the release' - optional :description, type: String, desc: 'The release notes' - optional :ref, type: String, desc: 'Commit SHA or branch name to use if creating a new tag' + optional :name, type: String, desc: 'The release name' + optional :description, type: String, desc: 'The description of the release. You can use Markdown' + + optional :ref, + type: String, + desc: "If a tag specified in `tag_name` doesn't exist, the release is created from `ref` and tagged " \ + "with `tag_name`. It can be a commit SHA, another tag name, or a branch name." + optional :assets, type: Hash do optional :links, type: Array do - requires :name, type: String, desc: 'The name of the link' - requires :url, type: String, desc: 'The URL of the link' - optional :filepath, type: String, desc: 'The filepath of the link' - optional :link_type, type: String, desc: 'The link type, one of: "runbook", "image", "package" or "other"' + requires :name, type: String, desc: 'The name of the link. Link names must be unique within the release' + requires :url, type: String, desc: 'The URL of the link. Link URLs must be unique within the release' + optional :filepath, type: String, desc: 'Optional path for a direct asset link' + optional :link_type, type: String, desc: 'The type of the link: `other`, `runbook`, `image`, `package`. Defaults to `other`' end end - optional :milestones, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The titles of the related milestones', default: [] - optional :released_at, type: DateTime, desc: 'The date when the release will be/was ready. Defaults to the current time.' + + optional :milestones, + type: Array[String], + coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, + desc: 'The title of each milestone the release is associated with. GitLab Premium customers can specify group milestones', + default: [] + + optional :released_at, + type: DateTime, + desc: 'Date and time for the release. Defaults to the current time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). ' \ + 'Only provide this field if creating an upcoming or historical release.' end route_setting :authentication, job_token_allowed: true post ':id/releases' do @@ -196,16 +271,27 @@ module API end desc 'Update a release' do - detail 'This feature was introduced in GitLab 11.7.' + detail 'Updates a release. Developer level access to the project is required to update a release. This feature was introduced in GitLab 11.7.' named 'update_release' success Entities::Release + failure [ + { code: 400, message: 'Bad request' }, + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not found' } + ] + tags releases_tags end params do - requires :tag_name, type: String, desc: 'The name of the tag', as: :tag - optional :name, type: String, desc: 'The name of the release' - optional :description, type: String, desc: 'Release notes with markdown support' - optional :released_at, type: DateTime, desc: 'The date when the release will be/was ready.' - optional :milestones, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The titles of the related milestones' + requires :tag_name, type: String, desc: 'The Git tag the release is associated with', as: :tag + optional :name, type: String, desc: 'The release name' + optional :description, type: String, desc: 'The description of the release. You can use Markdown' + optional :released_at, type: DateTime, desc: 'The date when the release is/was ready. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`)' + + optional :milestones, + type: Array[String], + coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, + desc: 'The title of each milestone to associate with the release. GitLab Premium customers can specify group milestones. To remove all milestones from the release, specify `[]`' end route_setting :authentication, job_token_allowed: true put ':id/releases/:tag_name', requirements: RELEASE_ENDPOINT_REQUIREMENTS do @@ -226,12 +312,19 @@ module API end desc 'Delete a release' do - detail 'This feature was introduced in GitLab 11.7.' + detail "Delete a release. Deleting a release doesn't delete the associated tag. Maintainer level access to the project is required to delete a release. This feature was introduced in GitLab 11.7." named 'delete_release' success Entities::Release + failure [ + { code: 400, message: 'Bad request' }, + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not found' } + ] + tags releases_tags end params do - requires :tag_name, type: String, desc: 'The name of the tag', as: :tag + requires :tag_name, type: String, desc: 'The Git tag the release is associated with', as: :tag end route_setting :authentication, job_token_allowed: true delete ':id/releases/:tag_name', requirements: RELEASE_ENDPOINT_REQUIREMENTS do diff --git a/lib/api/remote_mirrors.rb b/lib/api/remote_mirrors.rb index 8de155312fb..e041daca688 100644 --- a/lib/api/remote_mirrors.rb +++ b/lib/api/remote_mirrors.rb @@ -11,7 +11,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc "List the project's remote mirrors" do diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb index c2b77cd2fc4..640b23b402b 100644 --- a/lib/api/repositories.rb +++ b/lib/api/repositories.rb @@ -41,7 +41,7 @@ module API feature_category :source_code_management params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do helpers do diff --git a/lib/api/rpm_project_packages.rb b/lib/api/rpm_project_packages.rb index 13bbdfc0e6c..40b8d022c6c 100644 --- a/lib/api/rpm_project_packages.rb +++ b/lib/api/rpm_project_packages.rb @@ -21,7 +21,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do namespace ':id/packages/rpm' do diff --git a/lib/api/search.rb b/lib/api/search.rb index 8742d870425..cf6a1385783 100644 --- a/lib/api/search.rb +++ b/lib/api/search.rb @@ -171,7 +171,7 @@ module API detail 'This feature was introduced in GitLab 10.5.' end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' requires :search, type: String, desc: 'The expression it should be searched for' requires :scope, type: String, diff --git a/lib/api/tags.rb b/lib/api/tags.rb index 8d7b4045639..0022b51bd92 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -13,7 +13,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Get a project repository tags' do diff --git a/lib/api/terraform/state.rb b/lib/api/terraform/state.rb index 61583ece82e..c54667964d5 100644 --- a/lib/api/terraform/state.rb +++ b/lib/api/terraform/state.rb @@ -41,7 +41,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do diff --git a/lib/api/terraform/state_version.rb b/lib/api/terraform/state_version.rb index ca37c786666..2d74ae4fd27 100644 --- a/lib/api/terraform/state_version.rb +++ b/lib/api/terraform/state_version.rb @@ -14,7 +14,7 @@ module API end params do - requires :id, type: String, desc: 'The ID of a project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb index 996534f4194..870d93a951f 100644 --- a/lib/gitlab/gitaly_client.rb +++ b/lib/gitlab/gitaly_client.rb @@ -370,6 +370,8 @@ module Gitlab end def self.expected_server_version + return ENV[SERVER_VERSION_FILE] if ENV[SERVER_VERSION_FILE] + path = Rails.root.join(SERVER_VERSION_FILE) path.read.chomp end diff --git a/rubocop/cop/graphql/enum_names.rb b/rubocop/cop/graphql/enum_names.rb new file mode 100644 index 00000000000..74847cb8d17 --- /dev/null +++ b/rubocop/cop/graphql/enum_names.rb @@ -0,0 +1,87 @@ +# frozen_string_literal: true + +# This cop enforces the enum naming conventions from the enum style guide: +# https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#enums +# +# @example +# +# # bad +# class FooBar < BaseEnum +# value 'FOO' +# end +# +# class SubparEnum < BaseEnum +# end +# +# class UngoodEnum < BaseEnum +# graphql_name 'UngoodEnum' +# end +# +# # good +# +# class GreatEnum < BaseEnum +# graphql_name 'Great' +# +# value 'BAR' +# end +# +# class NiceEnum < BaseEnum +# declarative_enum NiceDeclarativeEnum +# end + +module RuboCop + module Cop + module Graphql + class EnumNames < RuboCop::Cop::Base + SEE_SG_MSG = "See https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#enums" + CLASS_NAME_SUFFIX_MSG = "Enum class names must end with `Enum`. #{SEE_SG_MSG}" + GRAPHQL_NAME_MISSING_MSG = "A `graphql_name` must be defined for a GraphQL enum. #{SEE_SG_MSG}" + GRAPHQL_NAME_WITH_ENUM_MSG = "The `graphql_name` must not contain the string \"Enum\". #{SEE_SG_MSG}" + + def_node_matcher :enum_subclass, <<~PATTERN + (class $(const nil? _) (const {nil? cbase} /.*Enum$/) ...) + PATTERN + + def_node_search :find_graphql_name, <<~PATTERN + (... `(send nil? :graphql_name $(...)) ...) + PATTERN + + def_node_search :declarative_enum?, <<~PATTERN + (... (send nil? :declarative_enum ...) ...) + PATTERN + + def on_class(node) + const_node = enum_subclass(node) + return unless const_node + + check_class_name(const_node) + check_graphql_name(node) + end + + private + + def check_class_name(const_node) + return unless const_node&.const_name + return if const_node.const_name.end_with?('Enum') + + add_offense(const_node, message: CLASS_NAME_SUFFIX_MSG) + end + + def check_graphql_name(node) + graphql_name_node = find_graphql_name(node)&.first + + if graphql_name_node + return unless graphql_name_node&.str_content + return unless graphql_name_node.str_content.downcase.include?('enum') + + add_offense(graphql_name_node, message: GRAPHQL_NAME_WITH_ENUM_MSG) + else + return if declarative_enum?(node) + + add_offense(node, message: GRAPHQL_NAME_MISSING_MSG) + end + end + end + end + end +end diff --git a/scripts/setup/as-if-jh.sh b/scripts/setup/as-if-jh.sh new file mode 100755 index 00000000000..38c3ac9b913 --- /dev/null +++ b/scripts/setup/as-if-jh.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +prepare_jh_branch() { + set -eu # https://explainshell.com/explain?cmd=set+-eu + + JH_BRANCH="$(./scripts/setup/find-jh-branch.rb)" + export JH_BRANCH + + echoinfo "JH_BRANCH: ${JH_BRANCH}" +} + +download_jh_path() { + set -eu # https://explainshell.com/explain?cmd=set+-eu + + for path in "$@"; do + # https://www.shellcheck.net/wiki/SC3043 + # shellcheck disable=SC3043 + local output="${path}.tar.gz" + + echoinfo "Downloading ${path}" + + curl --location -o "${output}" -H "Private-Token: ${ADD_JH_FILES_TOKEN}" "https://gitlab.com/api/v4/projects/${GITLAB_JH_MIRROR_PROJECT}/repository/archive?sha=${JH_BRANCH}&path=${path}" + + tar -zxf "${output}" + rm "${output}" + mv gitlab-"${JH_BRANCH}"-*/"${path}" ./ + done +} diff --git a/spec/frontend/boards/components/board_content_spec.js b/spec/frontend/boards/components/board_content_spec.js index 97d9e08f5d4..bb1650cd2cc 100644 --- a/spec/frontend/boards/components/board_content_spec.js +++ b/spec/frontend/boards/components/board_content_spec.js @@ -1,6 +1,6 @@ import { GlAlert } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; -import Vue from 'vue'; +import Vue, { nextTick } from 'vue'; import Draggable from 'vuedraggable'; import Vuex from 'vuex'; import EpicsSwimlanes from 'ee_component/boards/components/epics_swimlanes.vue'; @@ -53,6 +53,29 @@ describe('BoardContent', () => { }); }; + beforeAll(() => { + global.ResizeObserver = class MockResizeObserver { + constructor(callback) { + this.callback = callback; + + this.entries = []; + } + + observe(entry) { + this.entries.push(entry); + } + + disconnect() { + this.entries = []; + this.callback = null; + } + + trigger() { + this.callback(this.entries); + } + }; + }); + afterEach(() => { wrapper.destroy(); }); @@ -74,6 +97,17 @@ describe('BoardContent', () => { expect(wrapper.findComponent(EpicsSwimlanes).exists()).toBe(false); expect(wrapper.findComponent(GlAlert).exists()).toBe(false); }); + + it('resizes the list on resize', async () => { + window.innerHeight = 1000; + jest.spyOn(Element.prototype, 'getBoundingClientRect').mockReturnValue({ top: 100 }); + + wrapper.vm.resizeObserver.trigger(); + + await nextTick(); + + expect(wrapper.findComponent({ ref: 'list' }).attributes('style')).toBe('height: 900px;'); + }); }); describe('when issuableType is not issue', () => { diff --git a/spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js b/spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js deleted file mode 100644 index 962ff068b92..00000000000 --- a/spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js +++ /dev/null @@ -1,151 +0,0 @@ -import { mount } from '@vue/test-utils'; -import Vue from 'vue'; -import Vuex from 'vuex'; -import CodequalityIssueBody from '~/reports/codequality_report/components/codequality_issue_body.vue'; -import GroupedCodequalityReportsApp from '~/reports/codequality_report/grouped_codequality_reports_app.vue'; -import { getStoreConfig } from '~/reports/codequality_report/store'; -import { STATUS_NOT_FOUND } from '~/reports/constants'; -import { parsedReportIssues } from './mock_data'; - -Vue.use(Vuex); - -describe('Grouped code quality reports app', () => { - let wrapper; - let mockStore; - - const PATHS = { - codequalityHelpPath: 'codequality_help.html', - baseBlobPath: 'base/blob/path/', - headBlobPath: 'head/blob/path/', - }; - - const mountComponent = (props = {}) => { - wrapper = mount(GroupedCodequalityReportsApp, { - store: mockStore, - propsData: { - ...PATHS, - ...props, - }, - }); - }; - - const findWidget = () => wrapper.find('.js-codequality-widget'); - const findIssueBody = () => wrapper.findComponent(CodequalityIssueBody); - - beforeEach(() => { - const { state, ...storeConfig } = getStoreConfig(); - mockStore = new Vuex.Store({ - ...storeConfig, - actions: { - setPaths: () => {}, - fetchReports: () => {}, - }, - state: { - ...state, - ...PATHS, - }, - }); - - mountComponent(); - }); - - afterEach(() => { - wrapper.destroy(); - }); - - describe('when it is loading reports', () => { - beforeEach(() => { - mockStore.state.isLoading = true; - }); - - it('should render loading text', () => { - expect(findWidget().text()).toEqual('Loading Code quality report'); - }); - }); - - describe('when base and head reports are loaded and compared', () => { - describe('with no issues', () => { - beforeEach(() => { - mockStore.state.newIssues = []; - mockStore.state.resolvedIssues = []; - }); - - it('renders no changes text', () => { - expect(findWidget().text()).toEqual('No changes to code quality'); - }); - }); - - describe('with issues', () => { - describe('with new issues', () => { - beforeEach(() => { - mockStore.state.newIssues = parsedReportIssues.newIssues; - mockStore.state.resolvedIssues = []; - }); - - it('renders summary text', () => { - expect(findWidget().text()).toContain('Code quality degraded'); - }); - - it('renders custom codequality issue body', () => { - expect(findIssueBody().props('issue')).toEqual(parsedReportIssues.newIssues[0]); - }); - }); - - describe('with resolved issues', () => { - beforeEach(() => { - mockStore.state.newIssues = []; - mockStore.state.resolvedIssues = parsedReportIssues.resolvedIssues; - }); - - it('renders summary text', () => { - expect(findWidget().text()).toContain('Code quality improved'); - }); - - it('renders custom codequality issue body', () => { - expect(findIssueBody().props('issue')).toEqual(parsedReportIssues.resolvedIssues[0]); - }); - }); - - describe('with new and resolved issues', () => { - beforeEach(() => { - mockStore.state.newIssues = parsedReportIssues.newIssues; - mockStore.state.resolvedIssues = parsedReportIssues.resolvedIssues; - }); - - it('renders summary text', () => { - expect(findWidget().text()).toContain( - 'Code quality scanning detected 2 changes in merged results', - ); - }); - - it('renders custom codequality issue body', () => { - expect(findIssueBody().props('issue')).toEqual(parsedReportIssues.newIssues[0]); - }); - }); - }); - }); - - describe('on error', () => { - beforeEach(() => { - mockStore.state.hasError = true; - }); - - it('renders error text', () => { - expect(findWidget().text()).toContain('Failed to load Code quality report'); - }); - - it('does not render a help icon', () => { - expect(findWidget().find('[data-testid="question-o-icon"]').exists()).toBe(false); - }); - - describe('when base report was not found', () => { - beforeEach(() => { - mockStore.state.status = STATUS_NOT_FOUND; - }); - - it('renders a help icon with more information', () => { - expect(findWidget().find('[data-testid="question-o-icon"]').exists()).toBe(true); - }); - }); - }); -}); diff --git a/spec/frontend/reports/codequality_report/store/actions_spec.js b/spec/frontend/reports/codequality_report/store/actions_spec.js index 71f1a0f4de0..1878b9f44b2 100644 --- a/spec/frontend/reports/codequality_report/store/actions_spec.js +++ b/spec/frontend/reports/codequality_report/store/actions_spec.js @@ -28,7 +28,6 @@ describe('Codequality Reports actions', () => { baseBlobPath: 'baseBlobPath', headBlobPath: 'headBlobPath', reportsPath: 'reportsPath', - helpPath: 'codequalityHelpPath', }; return testAction( diff --git a/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js b/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js index 4826fecf98d..b17239384a7 100644 --- a/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js @@ -7,6 +7,14 @@ import ActionButtons from '~/vue_merge_request_widget/components/action_buttons. import Widget from '~/vue_merge_request_widget/components/widget/widget.vue'; import WidgetContentRow from '~/vue_merge_request_widget/components/widget/widget_content_row.vue'; +jest.mock('~/vue_merge_request_widget/components/extensions/telemetry', () => ({ + createTelemetryHub: jest.fn().mockReturnValue({ + viewed: jest.fn(), + expanded: jest.fn(), + fullReportClicked: jest.fn(), + }), +})); + describe('~/vue_merge_request_widget/components/widget/widget.vue', () => { let wrapper; @@ -30,6 +38,7 @@ describe('~/vue_merge_request_widget/components/widget/widget.vue', () => { slots, stubs: { StatusIcon, + ActionButtons, ContentRow: WidgetContentRow, }, }); @@ -323,4 +332,58 @@ describe('~/vue_merge_request_widget/components/widget/widget.vue', () => { expect(wrapper.findByText('Failed to load').exists()).toBe(false); }); }); + + describe('telemetry - enabled', () => { + beforeEach(() => { + createComponent({ + propsData: { + isCollapsible: true, + fetchCollapsedData: jest.fn(), + fetchExpandedData: jest.fn(), + actionButtons: [ + { + fullReport: true, + href: '#', + target: '_blank', + id: 'full-report-button', + text: 'Full Report', + }, + ], + }, + }); + }); + + it('should call create a telemetry hub', () => { + expect(wrapper.vm.telemetryHub).not.toBe(null); + }); + + it('should call the viewed state', async () => { + await nextTick(); + expect(wrapper.vm.telemetryHub.viewed).toHaveBeenCalledTimes(1); + }); + + it('when full report is clicked it should call the respective telemetry event', async () => { + expect(wrapper.vm.telemetryHub.fullReportClicked).not.toHaveBeenCalled(); + wrapper.findByText('Full Report').vm.$emit('click'); + await nextTick(); + expect(wrapper.vm.telemetryHub.fullReportClicked).toHaveBeenCalledTimes(1); + }); + }); + + describe('telemetry - disabled', () => { + beforeEach(() => { + createComponent({ + propsData: { + isCollapsible: true, + telemetry: false, + fetchCollapsedData: jest.fn(), + fetchExpandedData: jest.fn(), + }, + }); + }); + + it('should not call create a telemetry hub', () => { + expect(wrapper.vm.telemetryHub).toBe(null); + }); + }); }); diff --git a/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js b/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js index 6622749da92..02454af7242 100644 --- a/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js +++ b/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js @@ -563,21 +563,6 @@ describe('MrWidgetOptions', () => { }); }); - describe('code quality widget', () => { - beforeEach(() => { - jest.spyOn(document, 'dispatchEvent'); - }); - it('renders the component when refactorCodeQualityExtension is false', () => { - createComponent(mockData, {}, { refactorCodeQualityExtension: false }); - expect(wrapper.find('.js-codequality-widget').exists()).toBe(true); - }); - - it('does not render the component when refactorCodeQualityExtension is true', () => { - createComponent(mockData, {}, { refactorCodeQualityExtension: true }); - expect(wrapper.find('.js-codequality-widget').exists()).toBe(true); - }); - }); - describe('pipeline for target branch after merge', () => { describe('with information for target branch pipeline', () => { beforeEach(() => { @@ -917,8 +902,7 @@ describe('MrWidgetOptions', () => { }); it('extension polling is not called if enablePolling flag is not passed', () => { - // called one time due to parent component polling (mount) - expect(pollRequest).toHaveBeenCalledTimes(1); + expect(pollRequest).toHaveBeenCalledTimes(0); }); }); @@ -1004,7 +988,7 @@ describe('MrWidgetOptions', () => { await createComponent(); - expect(pollRequest).toHaveBeenCalledTimes(2); + expect(pollRequest).toHaveBeenCalledTimes(1); }); }); @@ -1042,7 +1026,7 @@ describe('MrWidgetOptions', () => { registerExtension(pollingErrorExtension); await createComponent(); - expect(pollRequest).toHaveBeenCalledTimes(2); + expect(pollRequest).toHaveBeenCalledTimes(1); }); it('captures sentry error and displays error when poll has failed', async () => { diff --git a/spec/lib/api/helpers/packages_helpers_spec.rb b/spec/lib/api/helpers/packages_helpers_spec.rb index d764ed4afff..b9c887b3e16 100644 --- a/spec/lib/api/helpers/packages_helpers_spec.rb +++ b/spec/lib/api/helpers/packages_helpers_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe API::Helpers::PackagesHelpers do - let_it_be(:helper) { Class.new.include(described_class).new } + let_it_be(:helper) { Class.new.include(API::Helpers).include(described_class).new } let_it_be(:project) { create(:project) } let_it_be(:group) { create(:group) } let_it_be(:package) { create(:package) } @@ -121,4 +121,121 @@ RSpec.describe API::Helpers::PackagesHelpers do expect(subject).to eq nil end end + + describe '#user_project' do + before do + allow(helper).to receive(:params).and_return(id: project.id) + end + + it 'calls find_project! on default action' do + expect(helper).to receive(:find_project!) + + helper.user_project + end + + it 'calls find_project! on read_project action' do + expect(helper).to receive(:find_project!) + + helper.user_project(action: :read_project) + end + + it 'calls user_project_with_read_package on read_package action' do + expect(helper).to receive(:user_project_with_read_package) + + helper.user_project(action: :read_package) + end + + it 'throws ArgumentError on unexpected action' do + expect { helper.user_project(action: :other_action) }.to raise_error(ArgumentError, 'unexpected action: other_action') + end + end + + describe '#user_project_with_read_package' do + before do + helper.clear_memoization(:user_project_with_read_package) + + allow(helper).to receive(:params).and_return(id: params_id) + allow(helper).to receive(:route_authentication_setting).and_return({ authenticate_non_public: true }) + allow(helper).to receive(:current_user).and_return(user) + allow(helper).to receive(:initial_current_user).and_return(user) + end + + subject { helper.user_project_with_read_package } + + context 'with non-existing project' do + let_it_be(:params_id) { non_existing_record_id } + + context 'with current user' do + let_it_be(:user) { create(:user) } + + it 'returns Not Found' do + expect(helper).to receive(:render_api_error!).with('404 Project Not Found', 404) + + is_expected.to be_nil + end + end + + context 'without current user' do + let_it_be(:user) { nil } + + it 'returns Unauthorized' do + expect(helper).to receive(:render_api_error!).with('401 Unauthorized', 401) + + is_expected.to be_nil + end + end + end + + context 'with existing project' do + let_it_be(:params_id) { project.id } + + context 'with current user' do + let_it_be(:user) { create(:user) } + + context 'as developer member' do + before do + project.add_developer(user) + end + + it 'returns project' do + is_expected.to eq(project) + end + end + + context 'as guest member' do + before do + project.add_guest(user) + end + + it 'returns Forbidden' do + expect(helper).to receive(:render_api_error!).with('403 Forbidden', 403) + + is_expected.to be_nil + end + end + end + + context 'without current user' do + let_it_be(:user) { nil } + + it 'returns Unauthorized' do + expect(helper).to receive(:render_api_error!).with('401 Unauthorized', 401) + + is_expected.to be_nil + end + end + end + + context 'if no authorized project scope' do + let_it_be(:params_id) { project.id } + let_it_be(:user) { nil } + + it 'returns Forbidden' do + expect(helper).to receive(:authorized_project_scope?).and_return(false) + expect(helper).to receive(:render_api_error!).with('403 Forbidden', 403) + + is_expected.to be_nil + end + end + end end diff --git a/spec/models/concerns/pg_full_text_searchable_spec.rb b/spec/models/concerns/pg_full_text_searchable_spec.rb index 5a693f084e6..d9d5ec3f177 100644 --- a/spec/models/concerns/pg_full_text_searchable_spec.rb +++ b/spec/models/concerns/pg_full_text_searchable_spec.rb @@ -125,6 +125,17 @@ RSpec.describe PgFullTextSearchable do end end + context 'when search term is a path with underscores' do + let(:path) { 'browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb' } + let(:with_underscore) { model_class.create!(project: project, title: 'issue with path', description: "some #{path} other text") } + + it 'allows searching by the path' do + with_underscore.update_search_data! + + expect(model_class.pg_full_text_search(path)).to contain_exactly(with_underscore) + end + end + context 'when text has numbers preceded by a dash' do let(:with_dash) { model_class.create!(project: project, title: 'issue with dash', description: 'ABC-123') } diff --git a/spec/models/factories_spec.rb b/spec/models/factories_spec.rb index 01331b0552f..072f5496bca 100644 --- a/spec/models/factories_spec.rb +++ b/spec/models/factories_spec.rb @@ -162,6 +162,13 @@ RSpec.describe 'factories', :saas do board_assignee_lists ].index_with(true) + if Gitlab.jh? + licensed_features.merge! %i[ + dingtalk_integration + feishu_bot_integration + ].index_with(true) + end + before do stub_licensed_features(licensed_features) end diff --git a/spec/requests/api/go_proxy_spec.rb b/spec/requests/api/go_proxy_spec.rb index fd74d06f383..5498ed6df13 100644 --- a/spec/requests/api/go_proxy_spec.rb +++ b/spec/requests/api/go_proxy_spec.rb @@ -406,6 +406,19 @@ RSpec.describe API::GoProxy do expect(response).to have_gitlab_http_status(:unauthorized) end end + + context 'with access to package registry for everyone' do + let_it_be(:user) { nil } + + before do + project.reload.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC) + end + + it_behaves_like 'a module version list resource', 'v1.0.1', 'v1.0.2', 'v1.0.3' + it_behaves_like 'a module version information resource', 'v1.0.1' + it_behaves_like 'a module file resource', 'v1.0.1' + it_behaves_like 'a module archive resource', 'v1.0.1', ['README.md', 'go.mod', 'a.go'] + end end context 'with a public project' do diff --git a/spec/rubocop/cop/graphql/enum_names_spec.rb b/spec/rubocop/cop/graphql/enum_names_spec.rb new file mode 100644 index 00000000000..f45df068381 --- /dev/null +++ b/spec/rubocop/cop/graphql/enum_names_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'rubocop_spec_helper' +require_relative '../../../../rubocop/cop/graphql/enum_names' + +RSpec.describe RuboCop::Cop::Graphql::EnumNames do + describe 'class name' do + it 'adds an offense when class name does not end with `Enum`' do + expect_offense(<<~ENUM) + module Types + class Fake < BaseEnum + ^^^^ #{described_class::CLASS_NAME_SUFFIX_MSG} + graphql_name 'Fake' + end + end + ENUM + end + end + + describe 'graphql_name' do + it 'adds an offense when `graphql_name` is not set' do + expect_offense(<<~ENUM) + module Types + class FakeEnum < BaseEnum + ^^^^^^^^^^^^^^^^^^^^^^^^^ #{described_class::GRAPHQL_NAME_MISSING_MSG} + end + end + ENUM + end + + it 'adds no offense when `declarative_enum` is used' do + expect_no_offenses(<<~ENUM) + module Types + class FakeEnum < BaseEnum + declarative_enum ::FakeModule::FakeDeclarativeEnum + end + end + ENUM + end + + it 'adds an offense when `graphql_name` includes `enum`' do + expect_offense(<<~ENUM) + module Types + class FakeEnum < BaseEnum + graphql_name 'FakeEnum' + ^^^^^^^^^^ #{described_class::GRAPHQL_NAME_WITH_ENUM_MSG} + end + end + ENUM + end + end +end diff --git a/spec/scripts/trigger-build_spec.rb b/spec/scripts/trigger-build_spec.rb index ac8e3c7797c..9032ba85b9f 100644 --- a/spec/scripts/trigger-build_spec.rb +++ b/spec/scripts/trigger-build_spec.rb @@ -229,6 +229,7 @@ RSpec.describe Trigger do context "when set in a file" do before do + stub_env(version_file) allow(File).to receive(:read).and_call_original end diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb index 5c87b9ac8bb..c758d3d5477 100644 --- a/spec/services/groups/update_service_spec.rb +++ b/spec/services/groups/update_service_spec.rb @@ -101,6 +101,15 @@ RSpec.describe Groups::UpdateService do expect(public_group.reload.name).to eq('new-name') end end + + context 'when the path does not change' do + let(:params) { { name: 'new-name', path: public_group.path } } + + it 'allows the update' do + expect(subject).to be true + expect(public_group.reload.name).to eq('new-name') + end + end end context 'within subgroup' do diff --git a/spec/services/incident_management/timeline_events/create_service_spec.rb b/spec/services/incident_management/timeline_events/create_service_spec.rb index 4e9cc4fa09c..3a924a40772 100644 --- a/spec/services/incident_management/timeline_events/create_service_spec.rb +++ b/spec/services/incident_management/timeline_events/create_service_spec.rb @@ -288,7 +288,7 @@ RSpec.describe IncidentManagement::TimelineEvents::CreateService do let_it_be(:severity) { create(:issuable_severity, severity: :critical, issue: incident) } - let(:expected_note) { "@#{current_user.username} changed the incident severity to **Critical**" } + let(:expected_note) { "@#{current_user.username} changed the incident severity to **Critical - S1**" } let(:expected_action) { 'severity' } it_behaves_like 'successfully created timeline event' diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb index d2f4fa0b8ef..37df90fff22 100644 --- a/spec/tasks/gitlab/gitaly_rake_spec.rb +++ b/spec/tasks/gitlab/gitaly_rake_spec.rb @@ -10,7 +10,7 @@ RSpec.describe 'gitlab:gitaly namespace rake task', :silence_stdout do let(:repo) { 'https://gitlab.com/gitlab-org/gitaly.git' } let(:clone_path) { Rails.root.join('tmp/tests/gitaly').to_s } let(:storage_path) { Rails.root.join('tmp/tests/repositories').to_s } - let(:version) { File.read(Rails.root.join(Gitlab::GitalyClient::SERVER_VERSION_FILE)).chomp } + let(:version) { Gitlab::GitalyClient.expected_server_version } describe 'clone' do subject { run_rake_task('gitlab:gitaly:clone', clone_path, storage_path) } diff --git a/workhorse/go.mod b/workhorse/go.mod index 0ca3cce1d2f..d4bb000c05e 100644 --- a/workhorse/go.mod +++ b/workhorse/go.mod @@ -20,7 +20,7 @@ require ( github.com/johannesboyne/gofakes3 v0.0.0-20220627085814-c3ac35da23b2 github.com/jpillora/backoff v1.0.0 github.com/mitchellh/copystructure v1.2.0 - github.com/prometheus/client_golang v1.13.0 + github.com/prometheus/client_golang v1.13.1 github.com/rafaeljusto/redigomock/v3 v3.1.1 github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a github.com/sirupsen/logrus v1.9.0 diff --git a/workhorse/go.sum b/workhorse/go.sum index caa3404527d..264d477e758 100644 --- a/workhorse/go.sum +++ b/workhorse/go.sum @@ -810,8 +810,9 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= +github.com/prometheus/client_golang v1.13.1 h1:3gMjIY2+/hzmqhtUC/aQNYldJA6DtH3CgQvwS+02K1c= +github.com/prometheus/client_golang v1.13.1/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= |