diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-06 18:08:52 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-06 18:08:52 +0300 |
commit | 7b69a22d499787378aa30561822ef797a99c22e5 (patch) | |
tree | 630c757f4b55abd7ee445def6577587ec57e860d | |
parent | d75e21489f113731bfe02b6c88e58879b5859103 (diff) |
Add latest changes from gitlab-org/gitlab@master
77 files changed, 735 insertions, 324 deletions
diff --git a/.gitlab/ci/docs.gitlab-ci.yml b/.gitlab/ci/docs.gitlab-ci.yml index d47650e0f83..f7783cc1d33 100644 --- a/.gitlab/ci/docs.gitlab-ci.yml +++ b/.gitlab/ci/docs.gitlab-ci.yml @@ -70,6 +70,16 @@ docs-lint markdown: script: - scripts/lint-doc.sh +docs-lint blueprint: + extends: + - .default-retry + - .docs:rules:docs-blueprints-lint + image: ${GITLAB_DEPENDENCY_PROXY_ADDRESS}ruby:${RUBY_VERSION}-slim + stage: lint + needs: [] + script: + - scripts/lint-docs-blueprints.rb + docs code_quality: extends: - .reports:rules:code_quality diff --git a/.gitlab/ci/review-apps/main.gitlab-ci.yml b/.gitlab/ci/review-apps/main.gitlab-ci.yml index a9ebf3b1120..2b22d96fc2d 100644 --- a/.gitlab/ci/review-apps/main.gitlab-ci.yml +++ b/.gitlab/ci/review-apps/main.gitlab-ci.yml @@ -100,7 +100,7 @@ review-build-cng: environment: name: review/${CI_COMMIT_REF_SLUG}${SCHEDULE_TYPE} # No separator for SCHEDULE_TYPE so it's compatible as before and looks nice without it url: https://gitlab-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN} - on_stop: review-stop + on_stop: trigger-review-stop review-deploy: extends: @@ -173,12 +173,6 @@ review-deploy-sample-projects: # because some repos are private and CI_JOB_TOKEN cannot access files. # See https://gitlab.com/gitlab-org/gitlab/issues/191273 GIT_DEPTH: 1 - before_script: - - source ./scripts/utils.sh - - source ./scripts/review_apps/review-apps.sh - - !reference [".use-kube-context", before_script] - script: - - retry delete_helm_release review-delete-deployment: extends: @@ -186,11 +180,23 @@ review-delete-deployment: - .review:rules:review-delete-deployment dependencies: [] stage: prepare + before_script: + - source ./scripts/utils.sh + - source ./scripts/review_apps/review-apps.sh + - !reference [".use-kube-context", before_script] + script: + - retry delete_helm_release -review-stop: +trigger-review-stop: extends: - .review-stop-base - - .review:rules:review-stop - resource_group: review/${CI_COMMIT_REF_SLUG}${SCHEDULE_TYPE} # CI_ENVIRONMENT_SLUG is not available here and we want this to be the same as the environment + - .review:rules:trigger-review-stop stage: deploy needs: [] + before_script: + - source ./scripts/utils.sh + - install_gitlab_gem + script: + - review_stop_job_id="$(scripts/api/get_job_id.rb --pipeline-id "${PARENT_PIPELINE_ID}" --job-name "review-stop")" + - | + curl --request POST --header "Private-Token: ${PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/jobs/${review_stop_job_id}/play" diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml index 20e4dec7dbf..f0e87e0161a 100644 --- a/.gitlab/ci/review.gitlab-ci.yml +++ b/.gitlab/ci/review.gitlab-ci.yml @@ -4,9 +4,12 @@ review-cleanup: - .review:rules:review-cleanup image: ${REVIEW_APPS_IMAGE} stage: prepare + needs: [] environment: name: review/regular-cleanup action: access + variables: + GIT_DEPTH: 1 before_script: - source scripts/utils.sh - !reference [".use-kube-context", before_script] @@ -15,6 +18,21 @@ review-cleanup: script: - scripts/review_apps/automated_cleanup.rb || (scripts/slack review-apps-monitoring "☠️ \`${CI_JOB_NAME}\` failed! ☠️ See ${CI_JOB_URL} - <https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/blob/main/runbooks/review-apps.md#review-cleanup-job-failed|📗 RUNBOOK 📕>" warning "GitLab Bot" && exit 1); +review-stop: + extends: + - review-cleanup + - .review:rules:review-stop + environment: + name: review/${CI_COMMIT_REF_SLUG}${SCHEDULE_TYPE} # No separator for SCHEDULE_TYPE so it's compatible as before and looks nice without it + action: stop + resource_group: review/${CI_COMMIT_REF_SLUG}${SCHEDULE_TYPE} # CI_ENVIRONMENT_SLUG is not available here and we want this to be the same as the environment + before_script: + - source ./scripts/utils.sh + - source ./scripts/review_apps/review-apps.sh + - !reference [".use-kube-context", before_script] + script: + - retry delete_helm_release + .base-review-checks: extends: - .default-retry diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index ebc8cd49968..ced63024fc3 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -228,6 +228,11 @@ - "scripts/lint-doc.sh" - ".gitlab/ci/docs.gitlab-ci.yml" +.docs-blueprints-patterns: &docs-blueprints-patterns + - "doc/architecture/blueprints/**/*" + - "scripts/lint-docs-blueprints.rb" + - ".gitlab/ci/docs.gitlab-ci.yml" + .docs-deprecations-and-removals-patterns: &docs-deprecations-and-removals-patterns - "doc/update/deprecations.md" - "doc/update/removals.md" @@ -851,6 +856,11 @@ - <<: *if-default-refs changes: *docs-patterns +.docs:rules:docs-blueprints-lint: + rules: + - <<: *if-default-refs + changes: *docs-blueprints-patterns + .docs:rules:deprecations-and-removals: rules: - <<: *if-default-refs @@ -2060,7 +2070,7 @@ # The following rules needs to be the same as the one for .review:rules:start-review-app-pipeline # except that: # - all rules have `when: manual` and `allow_failure: true` here -.review:rules:review-cleanup: +.review:rules:review-stop-merge-requests: rules: - <<: *if-not-ee when: never @@ -2097,9 +2107,20 @@ changes: *code-patterns when: manual allow_failure: true + +.review:rules:review-cleanup: + rules: + - !reference [".review:rules:review-stop-merge-requests", rules] - <<: *if-dot-com-ee-schedule-default-branch-maintenance allow_failure: true +.review:rules:review-stop: + rules: + - !reference [".review:rules:review-stop-merge-requests", rules] + - <<: *if-dot-com-gitlab-org-schedule + when: manual + allow_failure: true + .review:rules:review-k8s-resources-count-checks: rules: - <<: *if-dot-com-ee-schedule-default-branch-maintenance @@ -2118,7 +2139,7 @@ - "scripts/review_apps/gcp-quotas-checks.rb" allow_failure: true -.review:rules:review-stop: +.review:rules:trigger-review-stop: rules: - when: manual allow_failure: true diff --git a/app/assets/javascripts/deprecated_notes.js b/app/assets/javascripts/deprecated_notes.js index 8019a10a042..7503df9194b 100644 --- a/app/assets/javascripts/deprecated_notes.js +++ b/app/assets/javascripts/deprecated_notes.js @@ -17,6 +17,7 @@ import { escape, uniqueId } from 'lodash'; import Vue from 'vue'; import { renderGFM } from '~/behaviors/markdown/render_gfm'; import { createAlert, VARIANT_INFO } from '~/flash'; +import { sanitize } from '~/lib/dompurify'; import '~/lib/utils/jquery_at_who'; import AjaxCache from '~/lib/utils/ajax_cache'; import { loadingIconForLegacyJS } from '~/loading_icon_for_legacy_js'; @@ -517,8 +518,36 @@ export default class Notes { if (discussionContainer.length === 0) { if (noteEntity.diff_discussion_html) { const discussionElement = document.createElement('table'); - // eslint-disable-next-line no-unsanitized/method - discussionElement.insertAdjacentHTML('afterbegin', noteEntity.diff_discussion_html); + let internalNote; + let discussionDOM; + + if (!noteEntity.on_image) { + /* + DOMPurify will strip table-less <tr>/<td>, so to get it to stop deleting + nodes (since our note HTML starts with a table-less <tr>), we need to wrap + the noteEntity discussion HTML in a <table> to perform the other + sanitization. + */ + internalNote = sanitize(`<table>${noteEntity.diff_discussion_html}</table>`, { + RETURN_DOM: true, + }); + /* + Since we wrapped the <tr> in a <table>, we need to extract the <tr> back out. + DOMPurify returns a Body Element, so we have to start there, then get the + wrapping table, and then get the content we actually want. + Curiously, DOMPurify **ADDS** a totally novel <tbody>, so we're actually + inserting a completely as-yet-unseen <tbody> element here. + */ + discussionDOM = internalNote.querySelector('table').firstChild; + } else { + // Image comments don't need <table> manipulation, they're already <div>s + internalNote = sanitize(noteEntity.diff_discussion_html, { + RETURN_DOM: true, + }); + discussionDOM = internalNote.firstChild; + } + + discussionElement.insertAdjacentElement('afterbegin', discussionDOM); renderGFM(discussionElement); const $discussion = $(discussionElement).unwrap(); diff --git a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue index 7c283c42a03..697d953874c 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue @@ -67,7 +67,7 @@ export default { rulesLeft() { return getApprovalRuleNamesLeft( this.multipleApprovalRulesAvailable, - (this.approvalState.approvalState?.rules || []).filter((r) => r.approvalsRequired !== 0), + (this.approvalState.approvalState?.rules || []).filter((r) => !r.approved), ); }, approvalLeftMessage() { diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb index 63c1378ad11..9fe44659250 100644 --- a/app/controllers/projects/cycle_analytics_controller.rb +++ b/app/controllers/projects/cycle_analytics_controller.rb @@ -48,7 +48,7 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController end def load_value_stream - @value_stream = Analytics::CycleAnalytics::ProjectValueStream.build_default_value_stream(@project) + @value_stream = Analytics::CycleAnalytics::ValueStream.build_default_value_stream(@project.project_namespace) end def cycle_analytics_json diff --git a/app/finders/protected_branches_finder.rb b/app/finders/protected_branches_finder.rb index a452a1f993b..dfc9a64737d 100644 --- a/app/finders/protected_branches_finder.rb +++ b/app/finders/protected_branches_finder.rb @@ -11,15 +11,21 @@ class ProtectedBranchesFinder LIMIT = 100 - attr_accessor :project, :params + attr_accessor :project_or_group, :params - def initialize(project, params = {}) - @project = project + def initialize(project_or_group, params = {}) + @project_or_group = project_or_group @params = params end def execute - protected_branches = project.limited_protected_branches(LIMIT) + protected_branches = if project_or_group.is_a?(Group) + project_or_group.protected_branches + else + project_or_group.all_protected_branches + end + + protected_branches = protected_branches.limit(LIMIT) by_name(protected_branches) end diff --git a/app/models/analytics/cycle_analytics/project_level.rb b/app/models/analytics/cycle_analytics/project_level.rb index d43793f60c9..813263fe833 100644 --- a/app/models/analytics/cycle_analytics/project_level.rb +++ b/app/models/analytics/cycle_analytics/project_level.rb @@ -33,8 +33,8 @@ module Analytics private def build_stage(stage_name) - stage_params = stage_params_by_name(stage_name).merge(project: project) - Analytics::CycleAnalytics::ProjectStage.new(stage_params) + stage_params = stage_params_by_name(stage_name).merge(namespace: project.project_namespace) + Analytics::CycleAnalytics::Stage.new(stage_params) end def stage_params_by_name(name) diff --git a/app/models/analytics/cycle_analytics/project_stage.rb b/app/models/analytics/cycle_analytics/project_stage.rb deleted file mode 100644 index 7e121399b1e..00000000000 --- a/app/models/analytics/cycle_analytics/project_stage.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -module Analytics - module CycleAnalytics - class ProjectStage < ApplicationRecord - include Analytics::CycleAnalytics::Stageable - - belongs_to :project, optional: false - belongs_to :value_stream, class_name: 'Analytics::CycleAnalytics::ProjectValueStream', foreign_key: :project_value_stream_id - - alias_attribute :value_stream_id, :project_value_stream_id - - delegate :group, to: :project - alias_attribute :parent, :project - alias_attribute :parent_id, :project_id - - validate :validate_project_group_for_label_events, if: -> { start_event_label_based? || end_event_label_based? } - - def self.distinct_stages_within_hierarchy(group) - with_preloaded_labels - .where(project_id: group.all_projects.select(:id)) - .select("DISTINCT ON(stage_event_hash_id) #{quoted_table_name}.*") - end - - private - - # Project should belong to a group when the stage has Label based events since only GroupLabels are allowed. - def validate_project_group_for_label_events - errors.add(:project, s_('CycleAnalyticsStage|should be under a group')) unless project.group - end - end - end -end diff --git a/app/models/analytics/cycle_analytics/project_value_stream.rb b/app/models/analytics/cycle_analytics/project_value_stream.rb deleted file mode 100644 index 3eba7e87b17..00000000000 --- a/app/models/analytics/cycle_analytics/project_value_stream.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -class Analytics::CycleAnalytics::ProjectValueStream < ApplicationRecord - belongs_to :project - - has_many :stages, class_name: 'Analytics::CycleAnalytics::ProjectStage' - - validates :project, :name, presence: true - validates :name, length: { minimum: 3, maximum: 100, allow_nil: false }, uniqueness: { scope: :project_id } - - def custom? - false - end - - def stages - [] - end - - def self.build_default_value_stream(project) - new(name: Analytics::CycleAnalytics::Stages::BaseService::DEFAULT_VALUE_STREAM_NAME, project: project) - end -end diff --git a/app/models/analytics/cycle_analytics/stage_event_hash.rb b/app/models/analytics/cycle_analytics/stage_event_hash.rb index 043a5d45e3d..6443a970945 100644 --- a/app/models/analytics/cycle_analytics/stage_event_hash.rb +++ b/app/models/analytics/cycle_analytics/stage_event_hash.rb @@ -3,7 +3,6 @@ module Analytics module CycleAnalytics class StageEventHash < ApplicationRecord - has_many :cycle_analytics_project_stages, class_name: 'Analytics::CycleAnalytics::ProjectStage', inverse_of: :stage_event_hash has_many :cycle_analytics_stages, class_name: 'Analytics::CycleAnalytics::Stage', inverse_of: :stage_event_hash validates :hash_sha256, presence: true @@ -34,13 +33,9 @@ module Analytics end def self.unused_hashes_for(id) - project_stage_exists_query = Analytics::CycleAnalytics::ProjectStage.where(stage_event_hash_id: id).select('1').limit(1) stage_exists_query = ::Analytics::CycleAnalytics::Stage.where(stage_event_hash_id: id).select('1').limit(1) - where - .not('EXISTS (?)', project_stage_exists_query) - .where - .not('EXISTS (?)', stage_exists_query) + where.not('EXISTS (?)', stage_exists_query) end end end diff --git a/app/models/issue.rb b/app/models/issue.rb index ff5fb61146e..8b4477d4fcb 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -329,13 +329,22 @@ class Issue < ApplicationRecord '#' end + # Alternative prefix for situations where the standard prefix would be + # interpreted as a comment, most notably to begin commit messages with + # (e.g. "GL-123: My commit") + def self.alternative_reference_prefix + 'GL-' + end + # Pattern used to extract `#123` issue references from text # # This pattern supports cross-project references. def self.reference_pattern @reference_pattern ||= %r{ - (#{Project.reference_pattern})? - #{Regexp.escape(reference_prefix)}#{Gitlab::Regex.issue} + (?: + (#{Project.reference_pattern})?#{Regexp.escape(reference_prefix)} | + #{Regexp.escape(alternative_reference_prefix)} + )#{Gitlab::Regex.issue} }x end diff --git a/app/models/project.rb b/app/models/project.rb index 1941397b846..ef30a61229e 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -396,9 +396,6 @@ class Project < ApplicationRecord has_one :ci_cd_settings, class_name: 'ProjectCiCdSetting', inverse_of: :project, autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :remote_mirrors, inverse_of: :project - has_many :cycle_analytics_stages, class_name: 'Analytics::CycleAnalytics::ProjectStage', inverse_of: :project - has_many :value_streams, class_name: 'Analytics::CycleAnalytics::ProjectValueStream', inverse_of: :project - has_many :external_pull_requests, inverse_of: :project has_many :sourced_pipelines, class_name: 'Ci::Sources::Pipeline', foreign_key: :source_project_id diff --git a/app/workers/projects/refresh_build_artifacts_size_statistics_worker.rb b/app/workers/projects/refresh_build_artifacts_size_statistics_worker.rb index ec23bde5898..065d103fe07 100644 --- a/app/workers/projects/refresh_build_artifacts_size_statistics_worker.rb +++ b/app/workers/projects/refresh_build_artifacts_size_statistics_worker.rb @@ -19,6 +19,7 @@ module Projects refresh = Projects::RefreshBuildArtifactsSizeStatisticsService.new.execute return unless refresh + log_extra_metadata_on_done(:refresh_id, refresh.id) log_extra_metadata_on_done(:project_id, refresh.project_id) log_extra_metadata_on_done(:last_job_artifact_id, refresh.last_job_artifact_id) log_extra_metadata_on_done(:last_batch, refresh.destroyed?) diff --git a/db/docs/analytics_cycle_analytics_project_stages.yml b/db/docs/analytics_cycle_analytics_project_stages.yml index dc5b1db16cd..4d6baab5a00 100644 --- a/db/docs/analytics_cycle_analytics_project_stages.yml +++ b/db/docs/analytics_cycle_analytics_project_stages.yml @@ -1,10 +1,9 @@ --- table_name: analytics_cycle_analytics_project_stages classes: -- Analytics::CycleAnalytics::ProjectStage feature_categories: - value_stream_management -description: Persists project level value stream analytics stages. +description: Persists project level value stream analytics stages. Scheduled for removal in https://gitlab.com/gitlab-org/gitlab/-/issues/390194 introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/15061 milestone: '12.2' gitlab_schema: gitlab_main diff --git a/db/docs/analytics_cycle_analytics_project_value_streams.yml b/db/docs/analytics_cycle_analytics_project_value_streams.yml index 5881c048d3b..839b49f1bed 100644 --- a/db/docs/analytics_cycle_analytics_project_value_streams.yml +++ b/db/docs/analytics_cycle_analytics_project_value_streams.yml @@ -1,10 +1,9 @@ --- table_name: analytics_cycle_analytics_project_value_streams classes: -- Analytics::CycleAnalytics::ProjectValueStream feature_categories: - value_stream_management -description: Used to store the value stream configurations for projects +description: Used to store the value stream configurations for projects. Scheduled for removal in https://gitlab.com/gitlab-org/gitlab/-/issues/390194 introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60925 milestone: '13.12' gitlab_schema: gitlab_main diff --git a/db/post_migrate/20230112141236_schedule_vulnerabilities_feedback_migration2.rb b/db/post_migrate/20230112141236_schedule_vulnerabilities_feedback_migration2.rb index 08ceea1022c..7d8cba96a91 100644 --- a/db/post_migrate/20230112141236_schedule_vulnerabilities_feedback_migration2.rb +++ b/db/post_migrate/20230112141236_schedule_vulnerabilities_feedback_migration2.rb @@ -14,23 +14,10 @@ class ScheduleVulnerabilitiesFeedbackMigration2 < Gitlab::Database::Migration[2. restrict_gitlab_migration gitlab_schema: :gitlab_main def up - queue_batched_background_migration( - MIGRATION, - TABLE_NAME, - BATCH_COLUMN, - job_interval: DELAY_INTERVAL, - batch_size: BATCH_SIZE, - max_batch_size: MAX_BATCH_SIZE, - sub_batch_size: SUB_BATCH_SIZE - ) + # rescheduled by 20230203122602_schedule_vulnerabilities_feedback_migration3.rb end def down - delete_batched_background_migration( - MIGRATION, - TABLE_NAME, - BATCH_COLUMN, - [] - ) + # no-op end end diff --git a/db/post_migrate/20230119123256_add_fk_index_to_ci_resources_on_partition_id_and_build_id.rb b/db/post_migrate/20230119123256_add_fk_index_to_ci_resources_on_partition_id_and_build_id.rb new file mode 100644 index 00000000000..8c1b8c81235 --- /dev/null +++ b/db/post_migrate/20230119123256_add_fk_index_to_ci_resources_on_partition_id_and_build_id.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddFkIndexToCiResourcesOnPartitionIdAndBuildId < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + INDEX_NAME = :index_ci_resources_on_partition_id_build_id + TABLE_NAME = :ci_resources + COLUMNS = [:partition_id, :build_id] + + def up + add_concurrent_index(TABLE_NAME, COLUMNS, name: INDEX_NAME) + end + + def down + remove_concurrent_index_by_name(TABLE_NAME, INDEX_NAME) + end +end diff --git a/db/post_migrate/20230119123257_add_fk_to_ci_resources_on_partition_id_and_build_id.rb b/db/post_migrate/20230119123257_add_fk_to_ci_resources_on_partition_id_and_build_id.rb new file mode 100644 index 00000000000..8279def779e --- /dev/null +++ b/db/post_migrate/20230119123257_add_fk_to_ci_resources_on_partition_id_and_build_id.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +class AddFkToCiResourcesOnPartitionIdAndBuildId < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + SOURCE_TABLE_NAME = :ci_resources + TARGET_TABLE_NAME = :ci_builds + COLUMN = :build_id + TARGET_COLUMN = :id + FK_NAME = :fk_e169a8e3d5_p + PARTITION_COLUMN = :partition_id + + def up + add_concurrent_foreign_key( + SOURCE_TABLE_NAME, + TARGET_TABLE_NAME, + column: [PARTITION_COLUMN, COLUMN], + target_column: [PARTITION_COLUMN, TARGET_COLUMN], + validate: false, + reverse_lock_order: true, + on_update: :cascade, + on_delete: :nullify, + name: FK_NAME + ) + end + + def down + with_lock_retries do + remove_foreign_key_if_exists(SOURCE_TABLE_NAME, name: FK_NAME) + end + end +end diff --git a/db/post_migrate/20230119123258_validate_fk_on_ci_resources_partition_id_and_build_id.rb b/db/post_migrate/20230119123258_validate_fk_on_ci_resources_partition_id_and_build_id.rb new file mode 100644 index 00000000000..3df7f27d921 --- /dev/null +++ b/db/post_migrate/20230119123258_validate_fk_on_ci_resources_partition_id_and_build_id.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class ValidateFkOnCiResourcesPartitionIdAndBuildId < Gitlab::Database::Migration[2.1] + TABLE_NAME = :ci_resources + FK_NAME = :fk_e169a8e3d5_p + COLUMNS = [:partition_id, :build_id] + + def up + validate_foreign_key(TABLE_NAME, COLUMNS, name: FK_NAME) + end + + def down + # no-op + end +end diff --git a/db/post_migrate/20230119123259_remove_fk_to_ci_builds_ci_resources_on_build_id.rb b/db/post_migrate/20230119123259_remove_fk_to_ci_builds_ci_resources_on_build_id.rb new file mode 100644 index 00000000000..fcf6989f30d --- /dev/null +++ b/db/post_migrate/20230119123259_remove_fk_to_ci_builds_ci_resources_on_build_id.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class RemoveFkToCiBuildsCiResourcesOnBuildId < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + SOURCE_TABLE_NAME = :ci_resources + TARGET_TABLE_NAME = :ci_builds + COLUMN = :build_id + TARGET_COLUMN = :id + FK_NAME = :fk_e169a8e3d5 + + def up + with_lock_retries do + remove_foreign_key_if_exists(SOURCE_TABLE_NAME, name: FK_NAME) + end + end + + def down + add_concurrent_foreign_key( + SOURCE_TABLE_NAME, + TARGET_TABLE_NAME, + column: COLUMN, + target_column: TARGET_COLUMN, + validate: true, + reverse_lock_order: true, + on_delete: :nullify, + name: FK_NAME + ) + end +end diff --git a/db/post_migrate/20230203122602_schedule_vulnerabilities_feedback_migration3.rb b/db/post_migrate/20230203122602_schedule_vulnerabilities_feedback_migration3.rb new file mode 100644 index 00000000000..1d960c22b72 --- /dev/null +++ b/db/post_migrate/20230203122602_schedule_vulnerabilities_feedback_migration3.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +class ScheduleVulnerabilitiesFeedbackMigration3 < Gitlab::Database::Migration[2.1] + MIGRATION = 'MigrateVulnerabilitiesFeedbackToVulnerabilitiesStateTransition' + TABLE_NAME = :vulnerability_feedback + BATCH_COLUMN = :id + DELAY_INTERVAL = 5.minutes + BATCH_SIZE = 250 + MAX_BATCH_SIZE = 250 + SUB_BATCH_SIZE = 50 + + disable_ddl_transaction! + + restrict_gitlab_migration gitlab_schema: :gitlab_main + + def up + # Delete the previous jobs + delete_batched_background_migration( + MIGRATION, + TABLE_NAME, + BATCH_COLUMN, + [] + ) + + # Reschedule the migration + queue_batched_background_migration( + MIGRATION, + TABLE_NAME, + BATCH_COLUMN, + job_interval: DELAY_INTERVAL, + batch_size: BATCH_SIZE, + max_batch_size: MAX_BATCH_SIZE, + sub_batch_size: SUB_BATCH_SIZE + ) + end + + def down + delete_batched_background_migration( + MIGRATION, + TABLE_NAME, + BATCH_COLUMN, + [] + ) + end +end diff --git a/db/schema_migrations/20230119123256 b/db/schema_migrations/20230119123256 new file mode 100644 index 00000000000..3a2db3c1d76 --- /dev/null +++ b/db/schema_migrations/20230119123256 @@ -0,0 +1 @@ +bbf6542b726466ae98323f1e7dd636874e01228ec584166ab617a917822b3fa1
\ No newline at end of file diff --git a/db/schema_migrations/20230119123257 b/db/schema_migrations/20230119123257 new file mode 100644 index 00000000000..78d75e164c0 --- /dev/null +++ b/db/schema_migrations/20230119123257 @@ -0,0 +1 @@ +e69eabf71bfdfc9c5aa50829d08b3ef1473e5359d01e08e1bdc94fcbb7c58e6e
\ No newline at end of file diff --git a/db/schema_migrations/20230119123258 b/db/schema_migrations/20230119123258 new file mode 100644 index 00000000000..0bb155c2106 --- /dev/null +++ b/db/schema_migrations/20230119123258 @@ -0,0 +1 @@ +6af88109e5186a6a2f18418f441e232757ee0b03cb8af62e72c86ca4d12075c9
\ No newline at end of file diff --git a/db/schema_migrations/20230119123259 b/db/schema_migrations/20230119123259 new file mode 100644 index 00000000000..c4ffc37f790 --- /dev/null +++ b/db/schema_migrations/20230119123259 @@ -0,0 +1 @@ +49e256cdd550386c989cb6edea22873547b96120cfd8b5652de532dbbe21928c
\ No newline at end of file diff --git a/db/schema_migrations/20230203122602 b/db/schema_migrations/20230203122602 new file mode 100644 index 00000000000..a03143094f5 --- /dev/null +++ b/db/schema_migrations/20230203122602 @@ -0,0 +1 @@ +bb8b177385489eeefda9b8c1e9534398ec759d95fbf46ee3af02a3964a03e1ae
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 23f7d581dfc..2fd40d7e085 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -29246,6 +29246,8 @@ CREATE UNIQUE INDEX index_ci_resource_groups_on_project_id_and_key ON ci_resourc CREATE INDEX index_ci_resources_on_build_id ON ci_resources USING btree (build_id); +CREATE INDEX index_ci_resources_on_partition_id_build_id ON ci_resources USING btree (partition_id, build_id); + CREATE UNIQUE INDEX index_ci_resources_on_resource_group_id_and_build_id ON ci_resources USING btree (resource_group_id, build_id); CREATE INDEX index_ci_runner_machines_on_contacted_at_desc_and_id_desc ON ci_runner_machines USING btree (contacted_at DESC, id DESC); @@ -34347,7 +34349,7 @@ ALTER TABLE ONLY issues ADD CONSTRAINT fk_df75a7c8b8 FOREIGN KEY (promoted_to_epic_id) REFERENCES epics(id) ON DELETE SET NULL; ALTER TABLE ONLY ci_resources - ADD CONSTRAINT fk_e169a8e3d5 FOREIGN KEY (build_id) REFERENCES ci_builds(id) ON DELETE SET NULL; + ADD CONSTRAINT fk_e169a8e3d5_p FOREIGN KEY (partition_id, build_id) REFERENCES ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE SET NULL; ALTER TABLE ONLY ci_sources_pipelines ADD CONSTRAINT fk_e1bad85861 FOREIGN KEY (pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE; diff --git a/doc/administration/logs/index.md b/doc/administration/logs/index.md index 9893c6935a3..eab4c9b7d83 100644 --- a/doc/administration/logs/index.md +++ b/doc/administration/logs/index.md @@ -866,6 +866,19 @@ Depending on your installation method, this file is located at: - Omnibus GitLab: `/var/log/gitlab/gitlab-rails/database_load_balancing.log` - Installations from source: `/home/git/gitlab/log/database_load_balancing.log` +## `zoekt.log` **(PREMIUM SELF)** + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110980) in GitLab 15.9. + +This file logs information related to the +[Exact code search](../../user/search/exact_code_search.md) feature which is +powered by Zoekt. + +Depending on your installation method, this file is located at: + +- Omnibus GitLab: `/var/log/gitlab/gitlab-rails/zoekt.log` +- Installations from source: `/home/git/gitlab/log/zoekt.log` + ## `elasticsearch.log` **(PREMIUM SELF)** > Introduced in GitLab 12.6. diff --git a/doc/api/api_resources.md b/doc/api/api_resources.md index 0b39e30885e..b7c1def0ba4 100644 --- a/doc/api/api_resources.md +++ b/doc/api/api_resources.md @@ -135,6 +135,7 @@ The following API resources are available in the group context: | [Issues](issues.md) | `/groups/:id/issues` (also available for projects and standalone) | | [Issues Statistics](issues_statistics.md) | `/groups/:id/issues_statistics` (also available for projects and standalone) | | [Linked epics](linked_epics.md) | `/groups/:id/epics/.../related_epics` | +| [Member Roles](member_roles.md) | `/groups/:id/member_roles` | | [Members](members.md) | `/groups/:id/members` (also available for projects) | | [Merge requests](merge_requests.md) | `/groups/:id/merge_requests` (also available for projects and standalone) | | [Notes](notes.md) (comments) | `/groups/:id/epics/.../notes` (also available for projects) | diff --git a/doc/api/draft_notes.md b/doc/api/draft_notes.md index f1749346dc2..e8047d737dd 100644 --- a/doc/api/draft_notes.md +++ b/doc/api/draft_notes.md @@ -50,3 +50,46 @@ GET /projects/:id/merge_requests/:merge_request_iid/draft_notes curl --header "PRIVATE-TOKEN: <your_access_token>" \ "https://gitlab.example.com/api/v4/projects/14/merge_requests/11/draft_notes" ``` + +## Get a single draft note + +Returns a single draft note for a given merge request. + +```plaintext +GET /projects/:id/merge_requests/:merge_request_iid/draft_notes/:draft_note_id +``` + +| Attribute | Type | Required | Description | +| ------------------- | ---------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| `id` | integer or string | yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). +| `draft_note_id` | integer | yes | The ID of a draft note. +| `merge_request_iid` | integer | yes | The IID of a project merge request. + +```json +{ + id: 5, + author_id: 23, + merge_request_id: 11, + resolve_discussion: false, + discussion_id: nil, + note: "Example title", + commit_id: nil, + line_code: nil, + position: + { + base_sha: nil, + start_sha: nil, + head_sha: nil, + old_path: nil, + new_path: nil, + position_type: "text", + old_line: nil, + new_line: nil, + line_range: nil + } +} +``` + +```shell +curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/14/merge_requests/11/draft_notes/5" +``` diff --git a/doc/api/member_roles.md b/doc/api/member_roles.md new file mode 100644 index 00000000000..693dfb49c82 --- /dev/null +++ b/doc/api/member_roles.md @@ -0,0 +1,116 @@ +--- +stage: Manage +group: Authentication and Authorization +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +--- + +# Member roles API **(ULTIMATE)** + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96996) in GitLab 15.4. [Deployed behind the `customizable_roles` flag](../administration/feature_flags.md), disabled by default. + +## List all member roles of a group + +Gets a list of group member roles viewable by the authenticated user. + +```plaintext +GET /groups/:id/member_roles +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user | + +If successful, returns [`200`](rest/index.md#status-codes) and the following response attributes: + +| Attribute | Type | Description | +|:-------------------------|:---------|:----------------------| +| `[].id` | integer | The ID of the member role. | +| `[].group_id` | integer | The ID of the group that the member role belongs to. | +| `[].base_access_level` | integer | Base access level for member role. | +| `[].read_code` | boolean | Permission to read code. | + +Example request: + +```shell +curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/member_roles" +``` + +Example response: + +```json +[ + { + "id": 2, + "group_id": 84, + "base_access_level": 10, + "read_code": true + }, + { + "id": 3, + "group_id": 84, + "base_access_level": 10, + "read_code": false + } +] +``` + +## Add a member role to a group + +Adds a member role to a group. + +```plaintext +POST /groups/:id/member_roles +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user. | +| `base_access-level` | integer | yes | Base access level for configured role. | +| `read_code` | boolean | no | Permission to read code. | + +If successful, returns [`201`](rest/index.md#status-codes) and the following attributes: + +| Attribute | Type | Description | +|:-------------------------|:---------|:----------------------| +| `id` | integer | The ID of the member role. | +| `group_id` | integer | The ID of the group that the member role belongs to. | +| `base_access_level` | integer | Base access level for member role. | +| `read_code` | boolean | Permission to read code. | + +Example request: + +```shell + curl --request POST --header "Content-Type: application/json" --header "Authorization: Bearer $YOUR_ACCESS_TOKEN" --data '{"base_access_level" : 10, "read_code" : true}' "https://example.gitlab.com/api/v4/groups/:id/member_roles" +``` + +Example response: + +```json +{ + "id": 3, + "group_id": 84, + "base_access_level": 10, + "read_code": true +} +``` + +### Remove member role of a group + +Deletes a member role of a group. + +```plaintext +DELETE /groups/:id/member_roles/:member_role_id +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](rest/index.md#namespaced-path-encoding) owned by the authenticated user. | +| `member_role_id` | integer | yes | The ID of the member role. | + +If successful, returns [`204`](rest/index.md#status-codes) and an empty response. + +Example request: + +```shell +curl --request DELETE --header "Content-Type: application/json" --header "Authorization: Bearer $YOUR_ACCESS_TOKEN" "https://example.gitlab.com/api/v4/groups/:group_id/member_roles/:member_role_id" +``` diff --git a/doc/architecture/blueprints/_template.md b/doc/architecture/blueprints/_template.md index 11a312b3334..f7dea60e9b7 100644 --- a/doc/architecture/blueprints/_template.md +++ b/doc/architecture/blueprints/_template.md @@ -47,13 +47,11 @@ of this file) is [here](/doc/architecture/blueprints/_template.md). Blueprint statuses you can use: - "proposed" -- "ongoing" - "accepted" +- "ongoing" - "implemented" - "rejected" -Any other one-word status should be fine, see which ones have colors defined: -https://gitlab.com/gitlab-org/gitlab-docs/-/blob/main/content/assets/stylesheets/labels.scss#L22 --> # {+ Title of Blueprint +} diff --git a/doc/architecture/blueprints/object_storage/index.md b/doc/architecture/blueprints/object_storage/index.md index 950a5f13c38..4a8eeaf86a9 100644 --- a/doc/architecture/blueprints/object_storage/index.md +++ b/doc/architecture/blueprints/object_storage/index.md @@ -1,5 +1,5 @@ --- -status: ready +status: accepted creation-date: "2021-11-18" authors: [ "@nolith" ] coach: "@glopezfernandez" diff --git a/doc/architecture/blueprints/rate_limiting/index.md b/doc/architecture/blueprints/rate_limiting/index.md index 26114fce7f2..b466a54e922 100644 --- a/doc/architecture/blueprints/rate_limiting/index.md +++ b/doc/architecture/blueprints/rate_limiting/index.md @@ -1,5 +1,5 @@ --- -status: ready +status: accepted creation-date: "2022-09-08" authors: [ "@grzesiek", "@marshall007", "@fabiopitino", "@hswimelar" ] coach: "@andrewn" diff --git a/doc/architecture/blueprints/runner_tokens/index.md b/doc/architecture/blueprints/runner_tokens/index.md index f63e453bfae..f676ac5dc81 100644 --- a/doc/architecture/blueprints/runner_tokens/index.md +++ b/doc/architecture/blueprints/runner_tokens/index.md @@ -1,5 +1,5 @@ --- -status: ready +status: ongoing creation-date: "2022-10-27" authors: [ "@pedropombeiro", "@tmaczukin" ] coach: "@ayufan" diff --git a/doc/development/gitlab_shell/index.md b/doc/development/gitlab_shell/index.md index 7f2c113fa0c..7097fd48cea 100644 --- a/doc/development/gitlab_shell/index.md +++ b/doc/development/gitlab_shell/index.md @@ -21,7 +21,7 @@ Ruby to build and test, but not to run. GitLab Shell runs on `port 22` on an Omnibus installation. To use a regular SSH service, configure it on an alternative port. -Download and install the current version of Go from [golang.org](https://golang.org/dl/). +Download and install the current version of Go from [golang.org](https://go.dev/dl/). We follow the [Golang Release Policy](https://golang.org/doc/devel/release.html#policy) and support: diff --git a/doc/development/secure_coding_guidelines.md b/doc/development/secure_coding_guidelines.md index 3791cd4861e..876def53b2d 100644 --- a/doc/development/secure_coding_guidelines.md +++ b/doc/development/secure_coding_guidelines.md @@ -1270,7 +1270,7 @@ This sensitive data must be handled carefully to avoid leaks which could lead to - The [Gitleaks Git hook](https://gitlab.com/gitlab-com/gl-security/security-research/gitleaks-endpoint-installer) is recommended for preventing credentials from being committed. - Never log credentials under any circumstance. Issue [#353857](https://gitlab.com/gitlab-org/gitlab/-/issues/353857) is an example of credential leaks through log file. - When credentials are required in a CI/CD job, use [masked variables](../ci/variables/index.md#mask-a-cicd-variable) to help prevent accidental exposure in the job logs. Be aware that when [debug logging](../ci/variables/index.md#enable-debug-logging) is enabled, all masked CI/CD variables are visible in job logs. Also consider using [protected variables](../ci/variables/index.md#protect-a-cicd-variable) when possible so that sensitive CI/CD variables are only available to pipelines running on protected branches or protected tags. -- Proper scanners must be enabled depending on what data those credentials are protecting. See the [Application Security Inventory Policy](https://about.gitlab.com/handbook/security/security-engineering-and-research/application-security/inventory.html#policies) and our [Data Classification Standards](https://about.gitlab.com/handbook/security/data-classification-standard.html#data-classification-standards). +- Proper scanners must be enabled depending on what data those credentials are protecting. See the [Application Security Inventory Policy](https://about.gitlab.com/handbook/security/security-engineering/application-security/inventory.html#policies) and our [Data Classification Standards](https://about.gitlab.com/handbook/security/data-classification-standard.html#data-classification-standards). - To store and/or share credentials between teams, refer to [1Password for Teams](https://about.gitlab.com/handbook/security/#1password-for-teams) and follow [the 1Password Guidelines](https://about.gitlab.com/handbook/security/#1password-guidelines). - If you need to share a secret with a team member, use 1Password. Do not share a secret over email, Slack, or other service on the Internet. diff --git a/doc/development/value_stream_analytics.md b/doc/development/value_stream_analytics.md index 33a6744d5cd..77a32b62e32 100644 --- a/doc/development/value_stream_analytics.md +++ b/doc/development/value_stream_analytics.md @@ -261,7 +261,7 @@ considered legacy, which will be phased out at some point. - Rails Controller (`Analytics::CycleAnalytics` module): Value stream analytics exposes its data via JSON endpoints, implemented within the `analytics` workspace. Configuring the stages are also implements JSON endpoints (CRUD). - Services (`Analytics::CycleAnalytics` module): All `Stage` related actions are delegated to respective service objects. -- Models (`Analytics::CycleAnalytics` module): Models are used to persist the `Stage` objects `ProjectStage` and `Stage`. +- Models (`Analytics::CycleAnalytics` module): Models are used to persist the `Stage` objects. - Feature classes (`Gitlab::Analytics::CycleAnalytics` module): - Responsible for composing queries and define feature specific business logic. - `DataCollector`, `Event`, `StageEvents`, etc. diff --git a/doc/operations/metrics/embed_grafana.md b/doc/operations/metrics/embed_grafana.md index 9b2a381fcf6..7bc88165b95 100644 --- a/doc/operations/metrics/embed_grafana.md +++ b/doc/operations/metrics/embed_grafana.md @@ -3,8 +3,13 @@ stage: Monitor group: Respond info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments --- +<!--- start_remove The following content will be removed on remove_date: '2023-08-22' --> +# Embed Grafana panels in Markdown (deprecated) **(FREE)** -# Embed Grafana panels in Markdown **(FREE)** +WARNING: +This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110290) in GitLab 15.9 +and is planned for removal in 16.0. We intend to replace this feature with the ability to [embed charts](https://gitlab.com/groups/gitlab-org/opstrace/-/epics/33) with the [GitLab Observability UI](https://gitlab.com/gitlab-org/opstrace/opstrace-ui). +This change is a breaking change. Grafana panels can be embedded in [GitLab Flavored Markdown](../../user/markdown.md). You can embed Grafana panels using either: @@ -83,3 +88,4 @@ To generate a link to a panel: See the following example of a rendered panel. ![GitLab Rendered Grafana Panel](img/rendered_grafana_embed_v12_5.png) +<!--- end_remove --> diff --git a/doc/topics/plan_and_track.md b/doc/topics/plan_and_track.md index d79a8ad066b..67d5e996837 100644 --- a/doc/topics/plan_and_track.md +++ b/doc/topics/plan_and_track.md @@ -54,6 +54,7 @@ Align your work across teams. Use these day-to-day planning features. +- [Your work sidebar](../topics/your_work.md) - [Keyboard shortcuts](../user/shortcuts.md) - [Quick actions](../user/project/quick_actions.md) - [Markdown](../user/markdown.md) diff --git a/doc/topics/your_work.md b/doc/topics/your_work.md new file mode 100644 index 00000000000..862f9ae8430 --- /dev/null +++ b/doc/topics/your_work.md @@ -0,0 +1,18 @@ +--- +stage: Manage +group: Foundations +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +--- + +# Your work sidebar + +- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/384342) in GitLab 15.9. + +The **Your work** left sidebar provides access to your: + +- [Projects](../user/project/working_with_projects.md#view-projects) +- [Groups](../user/group/index.md) +- [Issues](../user/project/issues/index.md) +- [Merge requests](../user/project/merge_requests/index.md) +- [To-do List](../user/todos.md) +- [Milestones](../user/project/milestones/index.md) diff --git a/doc/user/project/issues/crosslinking_issues.md b/doc/user/project/issues/crosslinking_issues.md index 0b5605bb767..52da1acd32a 100644 --- a/doc/user/project/issues/crosslinking_issues.md +++ b/doc/user/project/issues/crosslinking_issues.md @@ -24,6 +24,13 @@ add `#xxx` to the commit message, where `xxx` is the issue number. git commit -m "this is my commit message. Ref #xxx" ``` +Since commit messages cannot usually begin with a `#` character, you may use +the alternative `GL-xxx` notation as well: + +```shell +git commit -m "GL-xxx: this is my commit message" +``` + If they are in different projects, but in the same group, add `projectname#xxx` to the commit message. diff --git a/doc/user/project/repository/vscode.md b/doc/user/project/repository/vscode.md index 257f98155d8..9260d59bc3a 100644 --- a/doc/user/project/repository/vscode.md +++ b/doc/user/project/repository/vscode.md @@ -15,8 +15,9 @@ do more day-to-day tasks in Visual Studio Code, such as: from the Visual Studio Code [command palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette). - Create and [review](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow#merge-request-reviews) merge requests directly from Visual Studio Code. -- [Validate your GitLab CI configuration](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow#validate-gitlab-ci-configuration). +- [Validate your GitLab CI/CD configuration](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow#validate-gitlab-cicd-configuration). - [View the status of your pipeline](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow#information-about-your-branch-pipelines-mr-closing-issue). +- [View the output of CI/CD jobs](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow#view-the-job-output). - [Create](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow#create-snippet) and paste snippets to, and from, your editor. - [Browse repositories](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow#browse-a-repository-without-cloning) diff --git a/lib/gitlab/analytics/cycle_analytics/aggregated/base_query_builder.rb b/lib/gitlab/analytics/cycle_analytics/aggregated/base_query_builder.rb index dd8149aba94..41f94e79f91 100644 --- a/lib/gitlab/analytics/cycle_analytics/aggregated/base_query_builder.rb +++ b/lib/gitlab/analytics/cycle_analytics/aggregated/base_query_builder.rb @@ -24,7 +24,7 @@ module Gitlab def initialize(stage:, params: {}) @stage = stage @params = params - @root_ancestor = stage.parent.root_ancestor + @root_ancestor = stage.namespace.root_ancestor @stage_event_model = MODEL_CLASSES.fetch(stage.subject_class.to_s) end @@ -90,7 +90,7 @@ module Gitlab end def filter_by_stage_parent(query) - query.by_project_id(stage.parent_id) + query.by_project_id(stage.namespace.project.id) end def base_query diff --git a/lib/gitlab/analytics/cycle_analytics/aggregated/data_collector.rb b/lib/gitlab/analytics/cycle_analytics/aggregated/data_collector.rb index 3abf380d461..8b40a8c2b26 100644 --- a/lib/gitlab/analytics/cycle_analytics/aggregated/data_collector.rb +++ b/lib/gitlab/analytics/cycle_analytics/aggregated/data_collector.rb @@ -5,7 +5,7 @@ module Gitlab module CycleAnalytics module Aggregated # Arguments: - # stage - an instance of CycleAnalytics::ProjectStage or CycleAnalytics::Stage + # stage - an instance of CycleAnalytics::Stage # params: # current_user: an instance of User # from: DateTime diff --git a/lib/gitlab/analytics/cycle_analytics/data_collector.rb b/lib/gitlab/analytics/cycle_analytics/data_collector.rb index 0db027b9861..ffafafda4ac 100644 --- a/lib/gitlab/analytics/cycle_analytics/data_collector.rb +++ b/lib/gitlab/analytics/cycle_analytics/data_collector.rb @@ -4,7 +4,7 @@ module Gitlab module Analytics module CycleAnalytics # Arguments: - # stage - an instance of CycleAnalytics::ProjectStage or CycleAnalytics::Stage + # stage - an instance of CycleAnalytics::Stage # params: # current_user: an instance of User # from: DateTime diff --git a/lib/gitlab/analytics/cycle_analytics/default_stages.rb b/lib/gitlab/analytics/cycle_analytics/default_stages.rb index 43683ae174e..0f1380125fa 100644 --- a/lib/gitlab/analytics/cycle_analytics/default_stages.rb +++ b/lib/gitlab/analytics/cycle_analytics/default_stages.rb @@ -6,7 +6,7 @@ # Example: # # params = Gitlab::Analytics::CycleAnalytics::DefaultStages.params_for_issue_stage -# Analytics::CycleAnalytics::ProjectStage.new(params) +# Analytics::CycleAnalytics::Stage.new(params) module Gitlab module Analytics module CycleAnalytics diff --git a/lib/gitlab/analytics/cycle_analytics/request_params.rb b/lib/gitlab/analytics/cycle_analytics/request_params.rb index d058782ae87..2df3680db5f 100644 --- a/lib/gitlab/analytics/cycle_analytics/request_params.rb +++ b/lib/gitlab/analytics/cycle_analytics/request_params.rb @@ -196,7 +196,7 @@ module Gitlab return unless value_stream strong_memoize(:stage) do - ::Analytics::CycleAnalytics::StageFinder.new(parent: project || group, stage_id: stage_id).execute if stage_id + ::Analytics::CycleAnalytics::StageFinder.new(parent: project&.project_namespace || group, stage_id: stage_id).execute if stage_id end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index e98cb7bdf17..68cbb209d5a 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -12276,9 +12276,6 @@ msgstr "" msgid "CycleAnalyticsStage|is not available for the selected group" msgstr "" -msgid "CycleAnalyticsStage|should be under a group" -msgstr "" - msgid "CycleAnalytics|%{selectedLabelsCount} selected (%{maxLabels} max)" msgstr "" diff --git a/scripts/lint-docs-blueprints.rb b/scripts/lint-docs-blueprints.rb new file mode 100755 index 00000000000..35e0013cb34 --- /dev/null +++ b/scripts/lint-docs-blueprints.rb @@ -0,0 +1,86 @@ +#!/usr/bin/env ruby + +# frozen_string_literal: true + +# Taken from Jekyll +# https://github.com/jekyll/jekyll/blob/3.5-stable/lib/jekyll/document.rb#L13 +YAML_FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m.freeze +READ_LIMIT_BYTES = 1024 + +require 'yaml' + +def extract_front_matter(path) + File.open(path, 'r') do |f| + data = if match = YAML_FRONT_MATTER_REGEXP.match(f.read(READ_LIMIT_BYTES)) + YAML.safe_load(match[1]) + else + {} + end + + BlueprintFrontMatter.new(data) + end +end + +class BlueprintFrontMatter + STATUSES = %w[proposed accepted ongoing implemented rejected] + + attr_reader :errors + + def initialize(metadata) + @metadata = metadata + @errors = [] + end + + def validate + validate_status + validate_authors + validate_creation_date + end + + private + + def validate_status + status = @metadata['status'] + + add_error('Missing status') unless status + + return if STATUSES.include?(status) + + add_error("Unsupported status '#{status}': expected one of '#{STATUSES.join(', ')}'") + end + + def validate_authors + authors = @metadata['authors'] + + add_error('Missing authors') unless authors + add_error('Authors must be an array') unless authors.is_a?(Array) + end + + def validate_creation_date + return if @metadata['creation-date'] =~ /\d{4}-[01]\d-[0123]\d/ + + add_error("Invalid creation-date: the date format must be 'yyyy-mm-dd'") + end + + def add_error(msg) + @errors << msg + end +end + +if $PROGRAM_NAME == __FILE__ + exit_code = 0 + + Dir['doc/architecture/blueprints/*/index.md'].each do |blueprint| + meta = extract_front_matter(blueprint) + meta.validate + + next if meta.errors.empty? + + exit_code = 1 + + puts("✖ ERROR: Invalid #{blueprint}:") + meta.errors.each { |e| puts(" - #{e}") } + end + + exit(exit_code) +end diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index 0875bb4e92a..5a02d712117 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -46,7 +46,7 @@ RSpec.describe 'Database schema', feature_category: :database do ci_pending_builds: %w[partition_id], ci_pipeline_variables: %w[partition_id], ci_pipelines: %w[partition_id], - ci_resources: %w[partition_id], + ci_resources: %w[partition_id build_id], ci_runner_projects: %w[runner_id], ci_running_builds: %w[partition_id], ci_sources_pipelines: %w[partition_id source_partition_id], @@ -187,7 +187,6 @@ RSpec.describe 'Database schema', feature_category: :database do # These pre-existing enums have limits > 2 bytes IGNORED_LIMIT_ENUMS = { 'Analytics::CycleAnalytics::Stage' => %w[start_event_identifier end_event_identifier], - 'Analytics::CycleAnalytics::ProjectStage' => %w[start_event_identifier end_event_identifier], 'Ci::Bridge' => %w[failure_reason], 'Ci::Build' => %w[failure_reason], 'Ci::BuildMetadata' => %w[timeout_source], diff --git a/spec/factories/analytics/cycle_analytics/project_stages.rb b/spec/factories/analytics/cycle_analytics/project_stages.rb deleted file mode 100644 index e673c4957b0..00000000000 --- a/spec/factories/analytics/cycle_analytics/project_stages.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -FactoryBot.define do - factory :cycle_analytics_project_stage, class: 'Analytics::CycleAnalytics::ProjectStage' do - project - sequence(:name) { |n| "Stage ##{n}" } - hidden { false } - issue_stage - value_stream { association(:cycle_analytics_project_value_stream, project: project) } - - trait :issue_stage do - start_event_identifier { Gitlab::Analytics::CycleAnalytics::StageEvents::IssueCreated.identifier } - end_event_identifier { Gitlab::Analytics::CycleAnalytics::StageEvents::IssueStageEnd.identifier } - end - end -end diff --git a/spec/factories/analytics/cycle_analytics/project_value_streams.rb b/spec/factories/analytics/cycle_analytics/project_value_streams.rb deleted file mode 100644 index 45a6470b0aa..00000000000 --- a/spec/factories/analytics/cycle_analytics/project_value_streams.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -FactoryBot.define do - factory :cycle_analytics_project_value_stream, class: 'Analytics::CycleAnalytics::ProjectValueStream' do - sequence(:name) { |n| "Value Stream ##{n}" } - - project - end -end diff --git a/spec/factories/analytics/cycle_analytics/stages.rb b/spec/factories/analytics/cycle_analytics/stages.rb index abf051f9a69..4f6f38f6f33 100644 --- a/spec/factories/analytics/cycle_analytics/stages.rb +++ b/spec/factories/analytics/cycle_analytics/stages.rb @@ -2,11 +2,19 @@ FactoryBot.define do factory :cycle_analytics_stage, class: 'Analytics::CycleAnalytics::Stage' do + transient do + project { nil } + end + sequence(:name) { |n| "Stage ##{n}" } start_event_identifier { Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestCreated.identifier } end_event_identifier { Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestMerged.identifier } namespace { association(:group) } value_stream { association(:cycle_analytics_value_stream, namespace: namespace) } + + after(:build) do |stage, evaluator| + stage.namespace = evaluator.project.reload.project_namespace if evaluator.project + end end end diff --git a/spec/finders/analytics/cycle_analytics/stage_finder_spec.rb b/spec/finders/analytics/cycle_analytics/stage_finder_spec.rb index 0275205028a..3e10ed78ab9 100644 --- a/spec/finders/analytics/cycle_analytics/stage_finder_spec.rb +++ b/spec/finders/analytics/cycle_analytics/stage_finder_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Analytics::CycleAnalytics::StageFinder do let(:stage_id) { { id: Gitlab::Analytics::CycleAnalytics::DefaultStages.names.first } } - subject { described_class.new(parent: project, stage_id: stage_id[:id]).execute } + subject { described_class.new(parent: project.project_namespace, stage_id: stage_id[:id]).execute } context 'when looking up in-memory default stage by name exists' do it { expect(subject).not_to be_persisted } diff --git a/spec/finders/protected_branches_finder_spec.rb b/spec/finders/protected_branches_finder_spec.rb index 487d1be697a..5926891ac9d 100644 --- a/spec/finders/protected_branches_finder_spec.rb +++ b/spec/finders/protected_branches_finder_spec.rb @@ -3,35 +3,57 @@ require 'spec_helper' RSpec.describe ProtectedBranchesFinder do - let(:project) { create(:project) } - let!(:protected_branch) { create(:protected_branch, project: project) } - let!(:another_protected_branch) { create(:protected_branch, project: project) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, namespace: group) } + + let!(:project_protected_branch) { create(:protected_branch, project: project) } + let!(:another_project_protected_branch) { create(:protected_branch, project: project) } + let!(:group_protected_branch) { create(:protected_branch, project: nil, group: group) } + let!(:another_group_protected_branch) { create(:protected_branch, project: nil, group: group) } let!(:other_protected_branch) { create(:protected_branch) } + let(:params) { {} } + subject { described_class.new(entity, params).execute } + describe '#execute' do - subject { described_class.new(project, params).execute } + shared_examples 'execute by entity' do + it 'returns all protected branches of project by default' do + expect(subject).to match_array(expected_branches) + end - it 'returns all protected branches of project by default' do - expect(subject).to match_array([protected_branch, another_protected_branch]) - end + context 'when search param is present' do + let(:params) { { search: group_protected_branch.name } } - context 'when search param is present' do - let(:params) { { search: protected_branch.name } } + it 'filters by search param' do + expect(subject).to eq([group_protected_branch]) + end + end + + context 'when there are more protected branches than the limit' do + before do + stub_const("#{described_class}::LIMIT", 1) + end - it 'filters by search param' do - expect(subject).to eq([protected_branch]) + it 'returns limited protected branches of project' do + expect(subject.count).to eq(1) + end end end - context 'when there are more protected branches than the limit' do - before do - stub_const("#{described_class}::LIMIT", 1) + it_behaves_like 'execute by entity' do + let(:entity) { project } + let(:expected_branches) do + [ + project_protected_branch, another_project_protected_branch, + group_protected_branch, another_group_protected_branch + ] end + end - it 'returns limited protected branches of project' do - expect(subject.count).to eq(1) - end + it_behaves_like 'execute by entity' do + let(:entity) { group } + let(:expected_branches) { [group_protected_branch, another_group_protected_branch] } end end end diff --git a/spec/frontend/notes/deprecated_notes_spec.js b/spec/frontend/notes/deprecated_notes_spec.js index 4756a107a15..6d3bc19bd45 100644 --- a/spec/frontend/notes/deprecated_notes_spec.js +++ b/spec/frontend/notes/deprecated_notes_spec.js @@ -28,6 +28,10 @@ window.gl = window.gl || {}; gl.utils = gl.utils || {}; gl.utils.disableButtonIfEmptyField = () => {}; +function wrappedDiscussionNote(note) { + return `<table><tbody>${note}</tbody></table>`; +} + // the following test is unreliable and failing in main 2-3 times a day // see https://gitlab.com/gitlab-org/gitlab/issues/206906#note_290602581 // eslint-disable-next-line jest/no-disabled-tests @@ -436,22 +440,40 @@ describe.skip('Old Notes (~/deprecated_notes.js)', () => { ); }); - it('should append to row selected with line_code', () => { - $form.length = 0; - note.discussion_line_code = 'line_code'; - note.diff_discussion_html = '<tr></tr>'; + describe('HTML output', () => { + let line; - const line = document.createElement('div'); - line.id = note.discussion_line_code; - document.body.appendChild(line); + beforeEach(() => { + $form.length = 0; + note.discussion_line_code = 'line_code'; + note.diff_discussion_html = '<tr></tr>'; - // Override mocks for this single test - $form.closest.mockReset(); - $form.closest.mockReturnValue($form); + line = document.createElement('div'); + line.id = note.discussion_line_code; + document.body.appendChild(line); - Notes.prototype.renderDiscussionNote.call(notes, note, $form); + // Override mocks for these tests + $form.closest.mockReset(); + $form.closest.mockReturnValue($form); + }); - expect(line.nextSibling.outerHTML).toEqual(note.diff_discussion_html); + it('should append to row selected with line_code', () => { + Notes.prototype.renderDiscussionNote.call(notes, note, $form); + + expect(line.nextSibling.outerHTML).toEqual( + wrappedDiscussionNote(note.diff_discussion_html), + ); + }); + + it('sanitizes the output html without stripping leading <tr> or <td> elements', () => { + const sanitizedDiscussion = '<tr><td><a>I am a dolphin!</a></td></tr>'; + note.diff_discussion_html = + '<tr><td><a href="javascript:alert(1)">I am a dolphin!</a></td></tr>'; + + Notes.prototype.renderDiscussionNote.call(notes, note, $form); + + expect(line.nextSibling.outerHTML).toEqual(wrappedDiscussionNote(sanitizedDiscussion)); + }); }); }); diff --git a/spec/lib/banzai/filter/references/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/references/issue_reference_filter_spec.rb index 37fdd3ec806..d8a97c6c3dc 100644 --- a/spec/lib/banzai/filter/references/issue_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/references/issue_reference_filter_spec.rb @@ -47,57 +47,55 @@ RSpec.describe Banzai::Filter::References::IssueReferenceFilter, feature_categor end end - context 'internal reference' do - let(:reference) { "##{issue.iid}" } - + shared_examples 'an internal reference' do it_behaves_like 'a reference containing an element node' it_behaves_like 'a reference with issue type information' it 'links to a valid reference' do - doc = reference_filter("Fixed #{reference}") + doc = reference_filter("Fixed #{written_reference}") expect(doc.css('a').first.attr('href')) .to eq issue_url end it 'links with adjacent text' do - doc = reference_filter("Fixed (#{reference}.)") + doc = reference_filter("Fixed (#{written_reference}.)") expect(doc.text).to eql("Fixed (#{reference}.)") end it 'ignores invalid issue IDs' do - invalid = invalidate_reference(reference) + invalid = invalidate_reference(written_reference) exp = act = "Fixed #{invalid}" expect(reference_filter(act).to_html).to eq exp end it 'includes a title attribute' do - doc = reference_filter("Issue #{reference}") + doc = reference_filter("Issue #{written_reference}") expect(doc.css('a').first.attr('title')).to eq issue.title end it 'escapes the title attribute' do issue.update_attribute(:title, %{"></a>whatever<a title="}) - doc = reference_filter("Issue #{reference}") + doc = reference_filter("Issue #{written_reference}") expect(doc.text).to eq "Issue #{reference}" end it 'renders non-HTML tooltips' do - doc = reference_filter("Issue #{reference}") + doc = reference_filter("Issue #{written_reference}") expect(doc.at_css('a')).not_to have_attribute('data-html') end it 'includes default classes' do - doc = reference_filter("Issue #{reference}") + doc = reference_filter("Issue #{written_reference}") expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-issue' end it 'includes a data-project attribute' do - doc = reference_filter("Issue #{reference}") + doc = reference_filter("Issue #{written_reference}") link = doc.css('a').first expect(link).to have_attribute('data-project') @@ -105,7 +103,7 @@ RSpec.describe Banzai::Filter::References::IssueReferenceFilter, feature_categor end it 'includes a data-issue attribute' do - doc = reference_filter("See #{reference}") + doc = reference_filter("See #{written_reference}") link = doc.css('a').first expect(link).to have_attribute('data-issue') @@ -113,7 +111,7 @@ RSpec.describe Banzai::Filter::References::IssueReferenceFilter, feature_categor end it 'includes data attributes for issuable popover' do - doc = reference_filter("See #{reference}") + doc = reference_filter("See #{written_reference}") link = doc.css('a').first expect(link.attr('data-project-path')).to eq project.full_path @@ -121,21 +119,21 @@ RSpec.describe Banzai::Filter::References::IssueReferenceFilter, feature_categor end it 'includes a data-original attribute' do - doc = reference_filter("See #{reference}") + doc = reference_filter("See #{written_reference}") link = doc.css('a').first expect(link).to have_attribute('data-original') - expect(link.attr('data-original')).to eq reference + expect(link.attr('data-original')).to eq written_reference end it 'does not escape the data-original attribute' do inner_html = 'element <code>node</code> inside' - doc = reference_filter(%{<a href="#{reference}">#{inner_html}</a>}) + doc = reference_filter(%{<a href="#{written_reference}">#{inner_html}</a>}) expect(doc.children.first.attr('data-original')).to eq inner_html end it 'includes a data-reference-format attribute' do - doc = reference_filter("Issue #{reference}+") + doc = reference_filter("Issue #{written_reference}+") link = doc.css('a').first expect(link).to have_attribute('data-reference-format') @@ -153,7 +151,7 @@ RSpec.describe Banzai::Filter::References::IssueReferenceFilter, feature_categor end it 'supports an :only_path context' do - doc = reference_filter("Issue #{reference}", only_path: true) + doc = reference_filter("Issue #{written_reference}", only_path: true) link = doc.css('a').first.attr('href') expect(link).not_to match %r(https?://) @@ -161,7 +159,7 @@ RSpec.describe Banzai::Filter::References::IssueReferenceFilter, feature_categor end it 'does not process links containing issue numbers followed by text' do - href = "#{reference}st" + href = "#{written_reference}st" doc = reference_filter("<a href='#{href}'></a>") link = doc.css('a').first.attr('href') @@ -169,6 +167,20 @@ RSpec.describe Banzai::Filter::References::IssueReferenceFilter, feature_categor end end + context 'standard internal reference' do + let(:written_reference) { "##{issue.iid}" } + let(:reference) { "##{issue.iid}" } + + it_behaves_like 'an internal reference' + end + + context 'alternative internal_reference' do + let(:written_reference) { "GL-#{issue.iid}" } + let(:reference) { "##{issue.iid}" } + + it_behaves_like 'an internal reference' + end + context 'cross-project / cross-namespace complete reference' do let(:reference) { "#{project2.full_path}##{issue.iid}" } let(:issue) { create(:issue, project: project2) } diff --git a/spec/lib/gitlab/analytics/cycle_analytics/aggregated/base_query_builder_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/aggregated/base_query_builder_spec.rb index bf2f8d8159b..bd6af4269b4 100644 --- a/spec/lib/gitlab/analytics/cycle_analytics/aggregated/base_query_builder_spec.rb +++ b/spec/lib/gitlab/analytics/cycle_analytics/aggregated/base_query_builder_spec.rb @@ -17,7 +17,7 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::Aggregated::BaseQueryBuilder d let_it_be(:issue_outside_project) { create(:issue) } let_it_be(:stage) do - create(:cycle_analytics_project_stage, + create(:cycle_analytics_stage, project: project, start_event_identifier: :issue_created, end_event_identifier: :issue_deployed_to_production diff --git a/spec/lib/gitlab/analytics/cycle_analytics/aggregated/records_fetcher_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/aggregated/records_fetcher_spec.rb index 7e36d89a2a1..aa0a1b66eef 100644 --- a/spec/lib/gitlab/analytics/cycle_analytics/aggregated/records_fetcher_spec.rb +++ b/spec/lib/gitlab/analytics/cycle_analytics/aggregated/records_fetcher_spec.rb @@ -8,7 +8,7 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::Aggregated::RecordsFetcher do let_it_be(:issue_2) { create(:issue, project: project) } let_it_be(:issue_3) { create(:issue, project: project) } - let_it_be(:stage) { create(:cycle_analytics_project_stage, start_event_identifier: :issue_created, end_event_identifier: :issue_deployed_to_production, project: project) } + let_it_be(:stage) { create(:cycle_analytics_stage, start_event_identifier: :issue_created, end_event_identifier: :issue_deployed_to_production, namespace: project.reload.project_namespace) } let_it_be(:stage_event_1) { create(:cycle_analytics_issue_stage_event, stage_event_hash_id: stage.stage_event_hash_id, project_id: project.id, issue_id: issue_1.id, start_event_timestamp: 2.years.ago, end_event_timestamp: 1.year.ago) } # duration: 1 year let_it_be(:stage_event_2) { create(:cycle_analytics_issue_stage_event, stage_event_hash_id: stage.stage_event_hash_id, project_id: project.id, issue_id: issue_2.id, start_event_timestamp: 5.years.ago, end_event_timestamp: 2.years.ago) } # duration: 3 years diff --git a/spec/lib/gitlab/analytics/cycle_analytics/average_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/average_spec.rb index e2fdd4918d5..de325454b34 100644 --- a/spec/lib/gitlab/analytics/cycle_analytics/average_spec.rb +++ b/spec/lib/gitlab/analytics/cycle_analytics/average_spec.rb @@ -21,7 +21,7 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::Average do let(:stage) do build( - :cycle_analytics_project_stage, + :cycle_analytics_stage, start_event_identifier: Gitlab::Analytics::CycleAnalytics::StageEvents::IssueCreated.identifier, end_event_identifier: Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstMentionedInCommit.identifier, project: project diff --git a/spec/lib/gitlab/analytics/cycle_analytics/base_query_builder_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/base_query_builder_spec.rb index e83ee0c6b75..59b2bacea50 100644 --- a/spec/lib/gitlab/analytics/cycle_analytics/base_query_builder_spec.rb +++ b/spec/lib/gitlab/analytics/cycle_analytics/base_query_builder_spec.rb @@ -10,10 +10,10 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::BaseQueryBuilder do let(:params) { { current_user: user } } let(:records) do - stage = build(:cycle_analytics_project_stage, { + stage = build(:cycle_analytics_stage, { start_event_identifier: :merge_request_created, end_event_identifier: :merge_request_merged, - project: project + namespace: project.reload.project_namespace }) described_class.new(stage: stage, params: params).build.to_a end diff --git a/spec/lib/gitlab/analytics/cycle_analytics/median_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/median_spec.rb index 4db5d64164e..39cfab49f54 100644 --- a/spec/lib/gitlab/analytics/cycle_analytics/median_spec.rb +++ b/spec/lib/gitlab/analytics/cycle_analytics/median_spec.rb @@ -9,10 +9,10 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::Median do let(:stage) do build( - :cycle_analytics_project_stage, + :cycle_analytics_stage, start_event_identifier: Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestCreated.identifier, end_event_identifier: Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestMerged.identifier, - project: project + namespace: project.reload.project_namespace ) end diff --git a/spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb index 7f70a4cfc4e..e9a9dfeca82 100644 --- a/spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb +++ b/spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb @@ -58,7 +58,7 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::RecordsFetcher do let_it_be(:issue2) { create(:issue, project: project, confidential: true) } let(:stage) do - build(:cycle_analytics_project_stage, { + build(:cycle_analytics_stage, { start_event_identifier: :plan_stage_start, end_event_identifier: :issue_first_mentioned_in_commit, project: project @@ -88,7 +88,7 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::RecordsFetcher do let(:mr1) { create(:merge_request, created_at: 5.days.ago, source_project: project, allow_broken: true) } let(:mr2) { create(:merge_request, created_at: 4.days.ago, source_project: project, allow_broken: true) } let(:stage) do - build(:cycle_analytics_project_stage, { + build(:cycle_analytics_stage, { start_event_identifier: :merge_request_created, end_event_identifier: :merge_request_merged, project: project @@ -110,10 +110,10 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::RecordsFetcher do let_it_be(:issue3) { create(:issue, project: project) } let(:stage) do - build(:cycle_analytics_project_stage, { + build(:cycle_analytics_stage, { start_event_identifier: :plan_stage_start, end_event_identifier: :issue_first_mentioned_in_commit, - project: project + namespace: project.project_namespace }) end diff --git a/spec/lib/gitlab/analytics/cycle_analytics/sorting_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/sorting_spec.rb index daf85ea379a..d0ada3d195b 100644 --- a/spec/lib/gitlab/analytics/cycle_analytics/sorting_spec.rb +++ b/spec/lib/gitlab/analytics/cycle_analytics/sorting_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Gitlab::Analytics::CycleAnalytics::Sorting do - let(:stage) { build(:cycle_analytics_project_stage, start_event_identifier: :merge_request_created, end_event_identifier: :merge_request_merged) } + let(:stage) { build(:cycle_analytics_stage, start_event_identifier: :merge_request_created, end_event_identifier: :merge_request_merged) } subject(:order_values) { described_class.new(query: MergeRequest.joins(:metrics), stage: stage).apply(sort, direction).order_values } diff --git a/spec/migrations/20230112141236_schedule_vulnerabilities_feedback_migration2_spec.rb b/spec/migrations/20230203122602_schedule_vulnerabilities_feedback_migration3_spec.rb index 0e0f08fea0e..91604b1f57c 100644 --- a/spec/migrations/20230112141236_schedule_vulnerabilities_feedback_migration2_spec.rb +++ b/spec/migrations/20230203122602_schedule_vulnerabilities_feedback_migration3_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' require_migration! -RSpec.describe ScheduleVulnerabilitiesFeedbackMigration2, feature_category: :vulnerability_management do +RSpec.describe ScheduleVulnerabilitiesFeedbackMigration3, feature_category: :vulnerability_management do let(:migration) { described_class::MIGRATION } describe '#up' do diff --git a/spec/models/analytics/cycle_analytics/project_stage_spec.rb b/spec/models/analytics/cycle_analytics/project_stage_spec.rb deleted file mode 100644 index 1e7335987a4..00000000000 --- a/spec/models/analytics/cycle_analytics/project_stage_spec.rb +++ /dev/null @@ -1,50 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Analytics::CycleAnalytics::ProjectStage do - describe 'associations' do - it { is_expected.to belong_to(:project).required } - end - - it 'default stages must be valid' do - project = build(:project) - - Gitlab::Analytics::CycleAnalytics::DefaultStages.all.each do |params| - stage = described_class.new(params.merge(project: project)) - expect(stage).to be_valid - end - end - - it_behaves_like 'value stream analytics stage' do - let(:factory) { :cycle_analytics_project_stage } - let(:parent) { build(:project) } - let(:parent_name) { :project } - end - - describe '.distinct_stages_within_hierarchy' do - let_it_be(:top_level_group) { create(:group) } - let_it_be(:sub_group_1) { create(:group, parent: top_level_group) } - let_it_be(:sub_group_2) { create(:group, parent: sub_group_1) } - - let_it_be(:project_1) { create(:project, group: sub_group_1) } - let_it_be(:project_2) { create(:project, group: sub_group_2) } - let_it_be(:project_3) { create(:project, group: top_level_group) } - - let_it_be(:stage1) { create(:cycle_analytics_project_stage, project: project_1, start_event_identifier: :issue_created, end_event_identifier: :issue_deployed_to_production) } - let_it_be(:stage2) { create(:cycle_analytics_project_stage, project: project_3, start_event_identifier: :issue_created, end_event_identifier: :issue_deployed_to_production) } - - let_it_be(:stage3) { create(:cycle_analytics_project_stage, project: project_1, start_event_identifier: :merge_request_created, end_event_identifier: :merge_request_merged) } - let_it_be(:stage4) { create(:cycle_analytics_project_stage, project: project_3, start_event_identifier: :merge_request_created, end_event_identifier: :merge_request_merged) } - - subject(:distinct_start_and_end_event_identifiers) { described_class.distinct_stages_within_hierarchy(top_level_group).to_a.pluck(:start_event_identifier, :end_event_identifier) } - - it 'returns distinct stages by start and end events (using stage_event_hash_id)' do - expect(distinct_start_and_end_event_identifiers).to match_array( - [ - %w[issue_created issue_deployed_to_production], - %w[merge_request_created merge_request_merged] - ]) - end - end -end diff --git a/spec/models/analytics/cycle_analytics/project_value_stream_spec.rb b/spec/models/analytics/cycle_analytics/project_value_stream_spec.rb deleted file mode 100644 index d84ecedc634..00000000000 --- a/spec/models/analytics/cycle_analytics/project_value_stream_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Analytics::CycleAnalytics::ProjectValueStream, type: :model do - describe 'associations' do - it { is_expected.to belong_to(:project) } - it { is_expected.to have_many(:stages) } - end - - describe 'validations' do - it { is_expected.to validate_presence_of(:project) } - it { is_expected.to validate_presence_of(:name) } - it { is_expected.to validate_length_of(:name).is_at_most(100) } - - it 'validates uniqueness of name' do - project = create(:project) - create(:cycle_analytics_project_value_stream, name: 'test', project: project) - - value_stream = build(:cycle_analytics_project_value_stream, name: 'test', project: project) - - expect(value_stream).to be_invalid - expect(value_stream.errors.messages).to eq(name: [I18n.t('errors.messages.taken')]) - end - end - - it 'is not custom' do - expect(described_class.new).not_to be_custom - end - - describe '.build_default_value_stream' do - it 'builds the default value stream' do - project = build(:project) - - value_stream = described_class.build_default_value_stream(project) - expect(value_stream.name).to eq('default') - end - end -end diff --git a/spec/models/analytics/cycle_analytics/stage_event_hash_spec.rb b/spec/models/analytics/cycle_analytics/stage_event_hash_spec.rb index a24f237fa9d..43db610af5c 100644 --- a/spec/models/analytics/cycle_analytics/stage_event_hash_spec.rb +++ b/spec/models/analytics/cycle_analytics/stage_event_hash_spec.rb @@ -7,7 +7,6 @@ RSpec.describe Analytics::CycleAnalytics::StageEventHash, type: :model do let(:hash_sha256) { 'does_not_matter' } describe 'associations' do - it { is_expected.to have_many(:cycle_analytics_project_stages) } it { is_expected.to have_many(:cycle_analytics_stages) } end @@ -31,7 +30,7 @@ RSpec.describe Analytics::CycleAnalytics::StageEventHash, type: :model do end describe '.cleanup_if_unused' do - it 'removes the record if there is no project or group stages with given stage events hash' do + it 'removes the record if there is no stages with given stage events hash' do described_class.cleanup_if_unused(stage_event_hash.id) expect(described_class.find_by_id(stage_event_hash.id)).to be_nil diff --git a/spec/models/cycle_analytics/project_level_stage_adapter_spec.rb b/spec/models/cycle_analytics/project_level_stage_adapter_spec.rb index ee13aae50dc..1516de8defd 100644 --- a/spec/models/cycle_analytics/project_level_stage_adapter_spec.rb +++ b/spec/models/cycle_analytics/project_level_stage_adapter_spec.rb @@ -10,11 +10,14 @@ RSpec.describe CycleAnalytics::ProjectLevelStageAdapter, type: :model do end end - let_it_be(:project) { merge_request.target_project } + let_it_be(:project) { merge_request.target_project.reload } let(:stage) do - params = Gitlab::Analytics::CycleAnalytics::DefaultStages.find_by_name!(stage_name).merge(project: project) - Analytics::CycleAnalytics::ProjectStage.new(params) + params = Gitlab::Analytics::CycleAnalytics::DefaultStages + .find_by_name!(stage_name) + .merge(namespace: project.project_namespace) + + Analytics::CycleAnalytics::Stage.new(params) end around do |example| diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 9d6ab7d6137..cd7b46d40ae 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -121,8 +121,6 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do it { is_expected.to have_many(:lfs_file_locks) } it { is_expected.to have_many(:project_deploy_tokens) } it { is_expected.to have_many(:deploy_tokens).through(:project_deploy_tokens) } - it { is_expected.to have_many(:cycle_analytics_stages).inverse_of(:project) } - it { is_expected.to have_many(:value_streams).inverse_of(:project) } it { is_expected.to have_many(:external_pull_requests) } it { is_expected.to have_many(:sourced_pipelines) } it { is_expected.to have_many(:source_pipelines) } diff --git a/spec/serializers/analytics/cycle_analytics/stage_entity_spec.rb b/spec/serializers/analytics/cycle_analytics/stage_entity_spec.rb index 8b45e8a64fc..7c53acbf168 100644 --- a/spec/serializers/analytics/cycle_analytics/stage_entity_spec.rb +++ b/spec/serializers/analytics/cycle_analytics/stage_entity_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Analytics::CycleAnalytics::StageEntity do - let(:stage) { build(:cycle_analytics_project_stage, start_event_identifier: :merge_request_created, end_event_identifier: :merge_request_merged) } + let(:stage) { build(:cycle_analytics_stage, start_event_identifier: :merge_request_created, end_event_identifier: :merge_request_merged) } subject(:entity_json) { described_class.new(Analytics::CycleAnalytics::StagePresenter.new(stage)).as_json } diff --git a/spec/support/helpers/ci/template_helpers.rb b/spec/support/helpers/ci/template_helpers.rb index 2cdd242ac22..cd3ab4bd82d 100644 --- a/spec/support/helpers/ci/template_helpers.rb +++ b/spec/support/helpers/ci/template_helpers.rb @@ -13,14 +13,21 @@ module Ci def public_image_manifest(registry, repository, reference) token = public_image_repository_token(registry, repository) + headers = { + 'Authorization' => "Bearer #{token}", + 'Accept' => 'application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.index.v1+json' + } response = with_net_connect_allowed do - Gitlab::HTTP.get(image_manifest_url(registry, repository, reference), - headers: { 'Authorization' => "Bearer #{token}" }) + Gitlab::HTTP.get(image_manifest_url(registry, repository, reference), headers: headers) end - return unless response.success? - - Gitlab::Json.parse(response.body) + if response.success? + Gitlab::Json.parse(response.body) + elsif response.not_found? + nil + else + raise "Could not retrieve manifest: #{response.body}" + end end def public_image_repository_token(registry, repository) @@ -31,17 +38,17 @@ module Ci Gitlab::HTTP.get(image_manifest_url(registry, repository, 'latest')) end - return unless response.unauthorized? + raise 'Unauthorized' unless response.unauthorized? www_authenticate = response.headers['www-authenticate'] - return unless www_authenticate + raise 'Missing www-authenticate' unless www_authenticate realm, service, scope = www_authenticate.split(',').map { |s| s[/\w+="(.*)"/, 1] } token_response = with_net_connect_allowed do Gitlab::HTTP.get(realm, query: { service: service, scope: scope }) end - return unless token_response.success? + raise "Could not get token: #{token_response.body}" unless token_response.success? token_response['token'] end diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml index 58411fc3c4f..2259a20cd74 100644 --- a/spec/support/rspec_order_todo.yml +++ b/spec/support/rspec_order_todo.yml @@ -1636,7 +1636,6 @@ - './ee/spec/models/allowed_email_domain_spec.rb' - './ee/spec/models/analytics/cycle_analytics/aggregation_context_spec.rb' - './ee/spec/models/analytics/cycle_analytics/group_level_spec.rb' -- './ee/spec/models/analytics/cycle_analytics/project_stage_spec.rb' - './ee/spec/models/analytics/cycle_analytics/runtime_limiter_spec.rb' - './ee/spec/models/analytics/devops_adoption/enabled_namespace_spec.rb' - './ee/spec/models/analytics/devops_adoption/snapshot_spec.rb' @@ -7729,7 +7728,6 @@ - './spec/models/analytics/cycle_analytics/aggregation_spec.rb' - './spec/models/analytics/cycle_analytics/issue_stage_event_spec.rb' - './spec/models/analytics/cycle_analytics/merge_request_stage_event_spec.rb' -- './spec/models/analytics/cycle_analytics/project_stage_spec.rb' - './spec/models/analytics/cycle_analytics/project_value_stream_spec.rb' - './spec/models/analytics/cycle_analytics/stage_event_hash_spec.rb' - './spec/models/analytics/usage_trends/measurement_spec.rb' diff --git a/spec/workers/projects/refresh_build_artifacts_size_statistics_worker_spec.rb b/spec/workers/projects/refresh_build_artifacts_size_statistics_worker_spec.rb index 00c45255316..99627ff1ad2 100644 --- a/spec/workers/projects/refresh_build_artifacts_size_statistics_worker_spec.rb +++ b/spec/workers/projects/refresh_build_artifacts_size_statistics_worker_spec.rb @@ -17,12 +17,14 @@ RSpec.describe Projects::RefreshBuildArtifactsSizeStatisticsWorker do build( :project_build_artifacts_size_refresh, :running, + id: 99, project_id: 77, last_job_artifact_id: 123 ) end it 'logs refresh information' do + expect(worker).to receive(:log_extra_metadata_on_done).with(:refresh_id, refresh.id) expect(worker).to receive(:log_extra_metadata_on_done).with(:project_id, refresh.project_id) expect(worker).to receive(:log_extra_metadata_on_done).with(:last_job_artifact_id, refresh.last_job_artifact_id) expect(worker).to receive(:log_extra_metadata_on_done).with(:last_batch, refresh.destroyed?) |