diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-13 21:10:15 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-13 21:10:15 +0000 |
commit | a269fb8e7cca24b826dd3f53485641ffce93bbee (patch) | |
tree | 81a7c4f56feba26bb6244c8a3a76123533f68aa8 | |
parent | 7e064974b92de60a3ef4642905e8af98a364a7a0 (diff) | |
download | gitlab-ce-a269fb8e7cca24b826dd3f53485641ffce93bbee.tar.gz |
Add latest changes from gitlab-org/gitlab@master
54 files changed, 316 insertions, 102 deletions
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue index 81b0dbec0bd..fe51591c32d 100644 --- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue +++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue @@ -182,6 +182,10 @@ export default { required: false, default: false, }, + membersPagePath: { + type: String, + required: true, + }, }, data() { const defaults = { @@ -521,12 +525,22 @@ export default { /> </div> </div> - <span v-if="!visibilityAllowed(visibilityLevel)" class="form-text text-muted">{{ - s__( - 'ProjectSettings|Visibility options for this fork are limited by the current visibility of the source project.', - ) - }}</span> - <span class="form-text text-muted">{{ visibilityLevelDescription }}</span> + <span + v-if="!visibilityAllowed(visibilityLevel)" + class="gl-display-block gl-text-gray-500 gl-mt-2" + >{{ + s__( + 'ProjectSettings|Visibility options for this fork are limited by the current visibility of the source project.', + ) + }}</span + > + <span class="gl-display-block gl-text-gray-500 gl-mt-2"> + <gl-sprintf :message="visibilityLevelDescription"> + <template #membersPageLink="{ content }"> + <gl-link class="gl-link" :href="membersPagePath">{{ content }}</gl-link> + </template> + </gl-sprintf> + </span> <div v-if="showAdditonalSettings" class="gl-mt-4"> <strong class="gl-display-block">{{ s__('ProjectSettings|Additional options') }}</strong> <label diff --git a/app/assets/javascripts/pages/projects/shared/permissions/constants.js b/app/assets/javascripts/pages/projects/shared/permissions/constants.js index fb1acd5311c..cfca9d400e3 100644 --- a/app/assets/javascripts/pages/projects/shared/permissions/constants.js +++ b/app/assets/javascripts/pages/projects/shared/permissions/constants.js @@ -8,12 +8,10 @@ export const visibilityOptions = { export const visibilityLevelDescriptions = { [visibilityOptions.PRIVATE]: __( - 'The project is accessible only by members of the project. Access must be granted explicitly to each user.', - ), - [visibilityOptions.INTERNAL]: __('The project can be accessed by any user who is logged in.'), - [visibilityOptions.PUBLIC]: __( - 'The project can be accessed by anyone, regardless of authentication.', + `Only accessible by %{membersPageLinkStart}project members%{membersPageLinkEnd}. Membership must be explicitly granted to each user.`, ), + [visibilityOptions.INTERNAL]: __('Accessible by any user who is logged in.'), + [visibilityOptions.PUBLIC]: __('Accessible by anyone, regardless of authentication.'), }; export const featureAccessLevel = { diff --git a/app/controllers/projects/pipelines/tests_controller.rb b/app/controllers/projects/pipelines/tests_controller.rb index e5b2dd14f69..20bdb4d3af1 100644 --- a/app/controllers/projects/pipelines/tests_controller.rb +++ b/app/controllers/projects/pipelines/tests_controller.rb @@ -7,6 +7,7 @@ module Projects before_action :authorize_read_build! before_action :builds, only: [:show] + before_action :validate_test_reports!, only: [:show] feature_category :code_testing @@ -23,19 +24,21 @@ module Projects def show respond_to do |format| format.json do - if pipeline.has_expired_test_reports? - render json: { errors: 'Test report artifacts have expired' }, status: :not_found - else - render json: TestSuiteSerializer - .new(project: project, current_user: @current_user) - .represent(test_suite, details: true) - end + render json: TestSuiteSerializer + .new(project: project, current_user: @current_user) + .represent(test_suite, details: true) end end end private + def validate_test_reports! + unless pipeline.has_test_reports? + render json: { errors: 'Test report artifacts have expired' }, status: :not_found + end + end + def builds @builds ||= pipeline.latest_builds.id_in(build_ids).presence || render_404 end diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index c2e47e06361..7e3800c6a13 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -70,10 +70,10 @@ module Types description: 'Indicates if shared runners are enabled for the project.' field :service_desk_enabled, GraphQL::Types::Boolean, null: true, - description: 'Indicates if the project has service desk enabled.' + description: 'Indicates if the project has Service Desk enabled.' field :service_desk_address, GraphQL::Types::String, null: true, - description: 'E-mail address of the service desk.' + description: 'E-mail address of the Service Desk.' field :avatar_url, GraphQL::Types::String, null: true, calls_gitaly: true, description: 'URL to avatar image file of the project.' diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 8a0820b2ba3..2ece3e87500 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -388,7 +388,8 @@ module ProjectsHelper pagesAccessControlEnabled: Gitlab.config.pages.access_control, pagesAccessControlForced: ::Gitlab::Pages.access_control_is_forced?, pagesHelpPath: help_page_path('user/project/pages/introduction', anchor: 'gitlab-pages-access-control'), - issuesHelpPath: help_page_path('user/project/issues/index') + issuesHelpPath: help_page_path('user/project/issues/index'), + membersPagePath: project_project_members_path(project) } end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 2c5dc0c72b7..795def55306 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -1297,9 +1297,9 @@ module Ci end end - def has_expired_test_reports? - strong_memoize(:has_expired_test_reports) do - has_reports?(::Ci::JobArtifact.test_reports.expired) + def has_test_reports? + strong_memoize(:has_test_reports) do + has_reports?(::Ci::JobArtifact.test_reports) end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 1a3464d05a2..b1fdfed5ae0 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -1567,6 +1567,7 @@ class MergeRequest < ApplicationRecord variables.append(key: 'CI_MERGE_REQUEST_PROJECT_PATH', value: project.full_path) variables.append(key: 'CI_MERGE_REQUEST_PROJECT_URL', value: project.web_url) variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_NAME', value: target_branch.to_s) + variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_PROTECTED', value: ProtectedBranch.protected?(target_project, target_branch).to_s) variables.append(key: 'CI_MERGE_REQUEST_TITLE', value: title) variables.append(key: 'CI_MERGE_REQUEST_ASSIGNEES', value: assignee_username_list) if assignees.present? variables.append(key: 'CI_MERGE_REQUEST_MILESTONE', value: milestone.title) if milestone diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb index 7f41f0907d5..f8d500e106b 100644 --- a/app/models/remote_mirror.rb +++ b/app/models/remote_mirror.rb @@ -128,7 +128,7 @@ class RemoteMirror < ApplicationRecord def sync return unless sync? - if recently_scheduled? + if schedule_with_delay? RepositoryUpdateRemoteMirrorWorker.perform_in(backoff_delay, self.id, Time.current) else RepositoryUpdateRemoteMirrorWorker.perform_async(self.id, Time.current) @@ -261,7 +261,8 @@ class RemoteMirror < ApplicationRecord super end - def recently_scheduled? + def schedule_with_delay? + return false if Feature.enabled?(:remote_mirror_no_delay, project, type: :ops) return false unless self.last_update_started_at self.last_update_started_at >= Time.current - backoff_delay diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index 02ae30db1c3..70a04cd556a 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -65,7 +65,10 @@ module Projects # exception. relations_block: -> (project) { build_fork_network_member(project) }, skip_disk_validation: skip_disk_validation, - external_authorization_classification_label: @project.external_authorization_classification_label + external_authorization_classification_label: @project.external_authorization_classification_label, + suggestion_commit_message: @project.suggestion_commit_message, + merge_commit_template: @project.merge_commit_template, + squash_commit_template: @project.squash_commit_template } if @project.avatar.present? && @project.avatar.image? diff --git a/app/services/protected_branches/api_service.rb b/app/services/protected_branches/api_service.rb index d0d0737fd66..f604a57bcd1 100644 --- a/app/services/protected_branches/api_service.rb +++ b/app/services/protected_branches/api_service.rb @@ -10,8 +10,8 @@ module ProtectedBranches { name: params[:name], allow_force_push: allow_force_push?, - push_access_levels_attributes: AccessLevelParams.new(:push, params).access_levels, - merge_access_levels_attributes: AccessLevelParams.new(:merge, params).access_levels + push_access_levels_attributes: ::ProtectedRefs::AccessLevelParams.new(:push, params).access_levels, + merge_access_levels_attributes: ::ProtectedRefs::AccessLevelParams.new(:merge, params).access_levels } end diff --git a/app/services/protected_branches/access_level_params.rb b/app/services/protected_refs/access_level_params.rb index 6f7a289d9b4..59fc17868d1 100644 --- a/app/services/protected_branches/access_level_params.rb +++ b/app/services/protected_refs/access_level_params.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module ProtectedBranches +module ProtectedRefs class AccessLevelParams attr_reader :type, :params @@ -34,4 +34,4 @@ module ProtectedBranches end end -ProtectedBranches::AccessLevelParams.prepend_mod_with('ProtectedBranches::AccessLevelParams') +ProtectedRefs::AccessLevelParams.prepend_mod_with('ProtectedRefs::AccessLevelParams') diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml index c669f3efec6..9a09f6bee38 100644 --- a/app/views/devise/sessions/new.html.haml +++ b/app/views/devise/sessions/new.html.haml @@ -1,7 +1,9 @@ - page_title _("Sign in") - content_for :page_specific_javascripts do + = render "layouts/google_tag_manager_head" = render "layouts/one_trust" = render "layouts/bizible" += render "layouts/google_tag_manager_body" #signin-container - if any_form_based_providers_enabled? diff --git a/config/feature_flags/ops/remote_mirror_no_delay.yml b/config/feature_flags/ops/remote_mirror_no_delay.yml new file mode 100644 index 00000000000..17937b35cf0 --- /dev/null +++ b/config/feature_flags/ops/remote_mirror_no_delay.yml @@ -0,0 +1,7 @@ +--- +name: remote_mirror_no_delay +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92093 +milestone: '15.2' +type: ops +group: group::scalability +default_enabled: false diff --git a/db/migrate/20220524164122_limit_project_and_group_variables.rb b/db/migrate/20220524164122_limit_project_and_group_variables.rb new file mode 100644 index 00000000000..e8776e35730 --- /dev/null +++ b/db/migrate/20220524164122_limit_project_and_group_variables.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class LimitProjectAndGroupVariables < Gitlab::Database::Migration[2.0] + def change + add_column(:plan_limits, :project_ci_variables, :integer, default: 200, null: false) + add_column(:plan_limits, :group_ci_variables, :integer, default: 200, null: false) + end +end diff --git a/db/post_migrate/20220705180843_validate_requirements_issue_id_not_null.rb b/db/post_migrate/20220705180843_validate_requirements_issue_id_not_null.rb new file mode 100644 index 00000000000..a0c8954b481 --- /dev/null +++ b/db/post_migrate/20220705180843_validate_requirements_issue_id_not_null.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class ValidateRequirementsIssueIdNotNull < Gitlab::Database::Migration[2.0] + disable_ddl_transaction! + + def up + constraint_name = 'check_requirement_issue_not_null' + + validate_not_null_constraint(:requirements, :issue_id, constraint_name: constraint_name) + end + + def down + # No op + end +end diff --git a/db/schema_migrations/20220524164122 b/db/schema_migrations/20220524164122 new file mode 100644 index 00000000000..26e3b596910 --- /dev/null +++ b/db/schema_migrations/20220524164122 @@ -0,0 +1 @@ +dc449f4ea28da3662fce663dcd5cdc9e37417e14b39e26897cc35a2bebfa22f0
\ No newline at end of file diff --git a/db/schema_migrations/20220705180843 b/db/schema_migrations/20220705180843 new file mode 100644 index 00000000000..4e39726e61e --- /dev/null +++ b/db/schema_migrations/20220705180843 @@ -0,0 +1 @@ +755e06f8bd3a0a28820f6ec2ee52a39a7920eb9d8ae0315a8a179139c78645d9
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 14b1a1ba862..69a5285d4a1 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -18834,7 +18834,9 @@ CREATE TABLE plan_limits ( repository_size bigint DEFAULT 0 NOT NULL, security_policy_scan_execution_schedules integer DEFAULT 0 NOT NULL, web_hook_calls_mid integer DEFAULT 0 NOT NULL, - web_hook_calls_low integer DEFAULT 0 NOT NULL + web_hook_calls_low integer DEFAULT 0 NOT NULL, + project_ci_variables integer DEFAULT 200 NOT NULL, + group_ci_variables integer DEFAULT 200 NOT NULL ); CREATE SEQUENCE plan_limits_id_seq @@ -20277,7 +20279,8 @@ CREATE TABLE requirements ( description text, description_html text, issue_id bigint, - CONSTRAINT check_785ae25b9d CHECK ((char_length(description) <= 10000)) + CONSTRAINT check_785ae25b9d CHECK ((char_length(description) <= 10000)), + CONSTRAINT check_requirement_issue_not_null CHECK ((issue_id IS NOT NULL)) ); CREATE SEQUENCE requirements_id_seq @@ -24510,9 +24513,6 @@ ALTER TABLE sprints ALTER TABLE projects ADD CONSTRAINT check_fa75869cb1 CHECK ((project_namespace_id IS NOT NULL)) NOT VALID; -ALTER TABLE requirements - ADD CONSTRAINT check_requirement_issue_not_null CHECK ((issue_id IS NOT NULL)) NOT VALID; - ALTER TABLE ONLY ci_build_needs ADD CONSTRAINT ci_build_needs_pkey PRIMARY KEY (id); diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 32c34b176c1..97f442d6063 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -15183,8 +15183,8 @@ Represents vulnerability finding of a security report on the pipeline. | <a id="projectsecuritydashboardpath"></a>`securityDashboardPath` | [`String`](#string) | Path to project's security dashboard. | | <a id="projectsecurityscanners"></a>`securityScanners` | [`SecurityScanners`](#securityscanners) | Information about security analyzers used in the project. | | <a id="projectsentryerrors"></a>`sentryErrors` | [`SentryErrorCollection`](#sentryerrorcollection) | Paginated collection of Sentry errors on the project. | -| <a id="projectservicedeskaddress"></a>`serviceDeskAddress` | [`String`](#string) | E-mail address of the service desk. | -| <a id="projectservicedeskenabled"></a>`serviceDeskEnabled` | [`Boolean`](#boolean) | Indicates if the project has service desk enabled. | +| <a id="projectservicedeskaddress"></a>`serviceDeskAddress` | [`String`](#string) | E-mail address of the Service Desk. | +| <a id="projectservicedeskenabled"></a>`serviceDeskEnabled` | [`Boolean`](#boolean) | Indicates if the project has Service Desk enabled. | | <a id="projectsharedrunnersenabled"></a>`sharedRunnersEnabled` | [`Boolean`](#boolean) | Indicates if shared runners are enabled for the project. | | <a id="projectsnippetsenabled"></a>`snippetsEnabled` | [`Boolean`](#boolean) | Indicates if Snippets are enabled for the current user. | | <a id="projectsquashcommittemplate"></a>`squashCommitTemplate` | [`String`](#string) | Template used to create squash commit message in merge requests. | diff --git a/doc/api/protected_tags.md b/doc/api/protected_tags.md index e2b27692373..b2ab3227a7e 100644 --- a/doc/api/protected_tags.md +++ b/doc/api/protected_tags.md @@ -100,6 +100,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitla | `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user | | `name` | string | yes | The name of the tag or wildcard | | `create_access_level` | string | no | Access levels allowed to create (defaults: `40`, Maintainer role) | +| `allowed_to_create` | array | no | Array of access levels allowed to create tags, with each described by a hash of the form `{user_id: integer}`, `{group_id: integer}`, or `{access_level: integer}` | Example response: diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md index f8e83ac7123..6df614c1cda 100644 --- a/doc/ci/variables/predefined_variables.md +++ b/doc/ci/variables/predefined_variables.md @@ -139,29 +139,30 @@ These variables are available when: - The pipelines [are merge request pipelines](../pipelines/merge_request_pipelines.md). - The merge request is open. -| Variable | GitLab | Runner | Description | -|----------------------------------------|--------|--------|-------------| -| `CI_MERGE_REQUEST_APPROVED` | 14.1 | all | Approval status of the merge request. `true` when [merge request approvals](../../user/project/merge_requests/approvals/index.md) is available and the merge request has been approved. | -| `CI_MERGE_REQUEST_ASSIGNEES` | 11.9 | all | Comma-separated list of usernames of assignees for the merge request. | -| `CI_MERGE_REQUEST_ID` | 11.6 | all | The instance-level ID of the merge request. This is a unique ID across all projects on GitLab. | -| `CI_MERGE_REQUEST_IID` | 11.6 | all | The project-level IID (internal ID) of the merge request. This ID is unique for the current project. | -| `CI_MERGE_REQUEST_LABELS` | 11.9 | all | Comma-separated label names of the merge request. | -| `CI_MERGE_REQUEST_MILESTONE` | 11.9 | all | The milestone title of the merge request. | -| `CI_MERGE_REQUEST_PROJECT_ID` | 11.6 | all | The ID of the project of the merge request. | -| `CI_MERGE_REQUEST_PROJECT_PATH` | 11.6 | all | The path of the project of the merge request. For example `namespace/awesome-project`. | -| `CI_MERGE_REQUEST_PROJECT_URL` | 11.6 | all | The URL of the project of the merge request. For example, `http://192.168.10.15:3000/namespace/awesome-project`. | -| `CI_MERGE_REQUEST_REF_PATH` | 11.6 | all | The ref path of the merge request. For example, `refs/merge-requests/1/head`. | -| `CI_MERGE_REQUEST_SOURCE_BRANCH_NAME` | 11.6 | all | The source branch name of the merge request. | -| `CI_MERGE_REQUEST_SOURCE_BRANCH_SHA` | 11.9 | all | The HEAD SHA of the source branch of the merge request. The variable is empty in merge request pipelines. The SHA is present only in [merged results pipelines](../pipelines/merged_results_pipelines.md). | -| `CI_MERGE_REQUEST_SOURCE_PROJECT_ID` | 11.6 | all | The ID of the source project of the merge request. | -| `CI_MERGE_REQUEST_SOURCE_PROJECT_PATH` | 11.6 | all | The path of the source project of the merge request. | -| `CI_MERGE_REQUEST_SOURCE_PROJECT_URL` | 11.6 | all | The URL of the source project of the merge request. | -| `CI_MERGE_REQUEST_TARGET_BRANCH_NAME` | 11.6 | all | The target branch name of the merge request. | -| `CI_MERGE_REQUEST_TARGET_BRANCH_SHA` | 11.9 | all | The HEAD SHA of the target branch of the merge request. The variable is empty in merge request pipelines. The SHA is present only in [merged results pipelines](../pipelines/merged_results_pipelines.md). | -| `CI_MERGE_REQUEST_TITLE` | 11.9 | all | The title of the merge request. | -| `CI_MERGE_REQUEST_EVENT_TYPE` | 12.3 | all | The event type of the merge request. Can be `detached`, `merged_result` or `merge_train`. | -| `CI_MERGE_REQUEST_DIFF_ID` | 13.7 | all | The version of the merge request diff. | -| `CI_MERGE_REQUEST_DIFF_BASE_SHA` | 13.7 | all | The base SHA of the merge request diff. | +| Variable | GitLab | Runner | Description | +|---------------------------------------------|--------|--------|-------------| +| `CI_MERGE_REQUEST_APPROVED` | 14.1 | all | Approval status of the merge request. `true` when [merge request approvals](../../user/project/merge_requests/approvals/index.md) is available and the merge request has been approved. | +| `CI_MERGE_REQUEST_ASSIGNEES` | 11.9 | all | Comma-separated list of usernames of assignees for the merge request. | +| `CI_MERGE_REQUEST_ID` | 11.6 | all | The instance-level ID of the merge request. This is a unique ID across all projects on GitLab. | +| `CI_MERGE_REQUEST_IID` | 11.6 | all | The project-level IID (internal ID) of the merge request. This ID is unique for the current project. | +| `CI_MERGE_REQUEST_LABELS` | 11.9 | all | Comma-separated label names of the merge request. | +| `CI_MERGE_REQUEST_MILESTONE` | 11.9 | all | The milestone title of the merge request. | +| `CI_MERGE_REQUEST_PROJECT_ID` | 11.6 | all | The ID of the project of the merge request. | +| `CI_MERGE_REQUEST_PROJECT_PATH` | 11.6 | all | The path of the project of the merge request. For example `namespace/awesome-project`. | +| `CI_MERGE_REQUEST_PROJECT_URL` | 11.6 | all | The URL of the project of the merge request. For example, `http://192.168.10.15:3000/namespace/awesome-project`. | +| `CI_MERGE_REQUEST_REF_PATH` | 11.6 | all | The ref path of the merge request. For example, `refs/merge-requests/1/head`. | +| `CI_MERGE_REQUEST_SOURCE_BRANCH_NAME` | 11.6 | all | The source branch name of the merge request. | +| `CI_MERGE_REQUEST_SOURCE_BRANCH_SHA` | 11.9 | all | The HEAD SHA of the source branch of the merge request. The variable is empty in merge request pipelines. The SHA is present only in [merged results pipelines](../pipelines/merged_results_pipelines.md). | +| `CI_MERGE_REQUEST_SOURCE_PROJECT_ID` | 11.6 | all | The ID of the source project of the merge request. | +| `CI_MERGE_REQUEST_SOURCE_PROJECT_PATH` | 11.6 | all | The path of the source project of the merge request. | +| `CI_MERGE_REQUEST_SOURCE_PROJECT_URL` | 11.6 | all | The URL of the source project of the merge request. | +| `CI_MERGE_REQUEST_TARGET_BRANCH_NAME` | 11.6 | all | The target branch name of the merge request. | +| `CI_MERGE_REQUEST_TARGET_BRANCH_PROTECTED` | 15.2 | all | The protection status for the target branch of the merge request. | +| `CI_MERGE_REQUEST_TARGET_BRANCH_SHA` | 11.9 | all | The HEAD SHA of the target branch of the merge request. The variable is empty in merge request pipelines. The SHA is present only in [merged results pipelines](../pipelines/merged_results_pipelines.md). | +| `CI_MERGE_REQUEST_TITLE` | 11.9 | all | The title of the merge request. | +| `CI_MERGE_REQUEST_EVENT_TYPE` | 12.3 | all | The event type of the merge request. Can be `detached`, `merged_result` or `merge_train`. | +| `CI_MERGE_REQUEST_DIFF_ID` | 13.7 | all | The version of the merge request diff. | +| `CI_MERGE_REQUEST_DIFF_BASE_SHA` | 13.7 | all | The base SHA of the merge request diff. | ## Predefined variables for external pull request pipelines diff --git a/doc/development/feature_flags/index.md b/doc/development/feature_flags/index.md index d21a46142a2..31ab6ca3c32 100644 --- a/doc/development/feature_flags/index.md +++ b/doc/development/feature_flags/index.md @@ -520,6 +520,8 @@ Feature.remove(:feature_flag_name) ``` - Any change behind a feature flag that is **enabled** by default **should** have a changelog entry. +- The changelog for a feature flag should describe the feature and not the + flag, unless a default on feature flag is removed keeping the new code (`other` in the flowchart above). ## Feature flags in tests diff --git a/lib/api/helpers/protected_tags_helpers.rb b/lib/api/helpers/protected_tags_helpers.rb new file mode 100644 index 00000000000..cad4ec8d5bd --- /dev/null +++ b/lib/api/helpers/protected_tags_helpers.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module API + module Helpers + module ProtectedTagsHelpers + extend ActiveSupport::Concern + extend Grape::API::Helpers + + params :optional_params_ee do + end + end + end +end + +API::Helpers::ProtectedTagsHelpers.prepend_mod_with('API::Helpers::ProtectedTagsHelpers') diff --git a/lib/api/protected_tags.rb b/lib/api/protected_tags.rb index b9385df1f8d..4611ee58479 100644 --- a/lib/api/protected_tags.rb +++ b/lib/api/protected_tags.rb @@ -10,6 +10,8 @@ module API feature_category :source_code_management + helpers Helpers::ProtectedTagsHelpers + params do requires :id, type: String, desc: 'The ID of a project' end @@ -50,14 +52,15 @@ module API end params do requires :name, type: String, desc: 'The name of the protected tag' - optional :create_access_level, type: Integer, default: Gitlab::Access::MAINTAINER, + optional :create_access_level, type: Integer, values: ProtectedTag::CreateAccessLevel.allowed_access_levels, desc: 'Access levels allowed to create (defaults: `40`, maintainer access level)' + use :optional_params_ee end post ':id/protected_tags' do protected_tags_params = { name: params[:name], - create_access_levels_attributes: [{ access_level: params[:create_access_level] }] + create_access_levels_attributes: ::ProtectedRefs::AccessLevelParams.new(:create, params).access_levels } protected_tag = ::ProtectedTags::CreateService.new(user_project, diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a7e9a6a6c75..abba06393da 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -1979,6 +1979,12 @@ msgstr "" msgid "AccessibilityReport|The accessibility scanning found an error of the following type: %{code}" msgstr "" +msgid "Accessible by any user who is logged in." +msgstr "" + +msgid "Accessible by anyone, regardless of authentication." +msgstr "" + msgid "Account" msgstr "" @@ -27093,6 +27099,9 @@ msgstr "" msgid "Only Task can be assigned as a child in hierarchy." msgstr "" +msgid "Only accessible by %{membersPageLinkStart}project members%{membersPageLinkEnd}. Membership must be explicitly granted to each user." +msgstr "" + msgid "Only active projects show up in the search and on the dashboard." msgstr "" @@ -38859,21 +38868,12 @@ msgstr "" msgid "The project can be accessed by any logged in user except external users." msgstr "" -msgid "The project can be accessed by any user who is logged in." -msgstr "" - -msgid "The project can be accessed by anyone, regardless of authentication." -msgstr "" - msgid "The project can be accessed without any authentication." msgstr "" msgid "The project has already been added to your dashboard." msgstr "" -msgid "The project is accessible only by members of the project. Access must be granted explicitly to each user." -msgstr "" - msgid "The project is still being deleted. Please try again later." msgstr "" diff --git a/spec/controllers/projects/pipelines/tests_controller_spec.rb b/spec/controllers/projects/pipelines/tests_controller_spec.rb index 2db54dbe671..deb96cc5bf9 100644 --- a/spec/controllers/projects/pipelines/tests_controller_spec.rb +++ b/spec/controllers/projects/pipelines/tests_controller_spec.rb @@ -45,6 +45,21 @@ RSpec.describe Projects::Pipelines::TestsController do pipeline.job_artifacts.first.update!(expire_at: Date.yesterday) end + it 'renders test suite', :aggregate_failures do + get_tests_show_json(build_ids) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['name']).to eq('test') + expect(json_response['total_count']).to eq(3) + expect(json_response['test_cases'].size).to eq(3) + end + end + + context 'when artifacts do not exist' do + before do + pipeline.job_artifacts.each(&:destroy) + end + it 'renders not_found errors', :aggregate_failures do get_tests_show_json(build_ids) @@ -68,7 +83,6 @@ RSpec.describe Projects::Pipelines::TestsController do expect(response).to have_gitlab_http_status(:ok) expect(json_response['name']).to eq('test') - expect(json_response['artifacts_expired']).to be_falsey # Each test failure in this pipeline has a matching failure in the default branch recent_failures = json_response['test_cases'].map { |tc| tc['recent_failures'] } diff --git a/spec/features/projects/settings/visibility_settings_spec.rb b/spec/features/projects/settings/visibility_settings_spec.rb index becb30c02b7..fc78b5b5769 100644 --- a/spec/features/projects/settings/visibility_settings_spec.rb +++ b/spec/features/projects/settings/visibility_settings_spec.rb @@ -16,7 +16,7 @@ RSpec.describe 'Projects > Settings > Visibility settings', :js do visibility_select_container = find('.project-visibility-setting') expect(visibility_select_container.find('select').value).to eq project.visibility_level.to_s - expect(visibility_select_container).to have_content 'The project can be accessed by anyone, regardless of authentication.' + expect(visibility_select_container).to have_content 'Accessible by anyone, regardless of authentication.' end it 'project visibility description updates on change' do @@ -25,7 +25,7 @@ RSpec.describe 'Projects > Settings > Visibility settings', :js do visibility_select.select('Private') expect(visibility_select.value).to eq '0' - expect(visibility_select_container).to have_content 'Access must be granted explicitly to each user.' + expect(visibility_select_container).to have_content 'Only accessible by project members. Membership must be explicitly granted to each user.' end context 'merge requests select' do @@ -86,7 +86,7 @@ RSpec.describe 'Projects > Settings > Visibility settings', :js do visibility_select_container = find('.project-visibility-setting') expect(visibility_select_container).to have_selector 'select[name="project[visibility_level]"]:disabled' - expect(visibility_select_container).to have_content 'The project can be accessed by anyone, regardless of authentication.' + expect(visibility_select_container).to have_content 'Accessible by anyone, regardless of authentication.' end context 'disable email notifications' do diff --git a/spec/frontend/__helpers__/performance.js b/spec/frontend/__helpers__/performance.js new file mode 100644 index 00000000000..3bdf163c22b --- /dev/null +++ b/spec/frontend/__helpers__/performance.js @@ -0,0 +1,8 @@ +// FIXME(vslobodin): Remove this stub once we have migrated to Jest 28. +// NOTE: Do not try to optimize these stubs as Jest 27 overwrites +// the "global.performance" object in every suite where fake timers are enabled. +export const stubPerformanceWebAPI = () => { + global.performance.getEntriesByName = () => []; + global.performance.mark = () => {}; + global.performance.measure = () => {}; +}; diff --git a/spec/frontend/design_management/components/image_spec.js b/spec/frontend/design_management/components/image_spec.js index e27b2bc9fa5..65ee0ae6238 100644 --- a/spec/frontend/design_management/components/image_spec.js +++ b/spec/frontend/design_management/components/image_spec.js @@ -1,6 +1,7 @@ import { GlIcon } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import { nextTick } from 'vue'; +import { stubPerformanceWebAPI } from 'helpers/performance'; import DesignImage from '~/design_management/components/image.vue'; describe('Design management large image component', () => { @@ -15,6 +16,10 @@ describe('Design management large image component', () => { wrapper.setData(data); } + beforeEach(() => { + stubPerformanceWebAPI(); + }); + afterEach(() => { wrapper.destroy(); }); diff --git a/spec/frontend/diffs/components/app_spec.js b/spec/frontend/diffs/components/app_spec.js index 76e4a944d87..96f2ac1692c 100644 --- a/spec/frontend/diffs/components/app_spec.js +++ b/spec/frontend/diffs/components/app_spec.js @@ -18,6 +18,7 @@ import HiddenFilesWarning from '~/diffs/components/hidden_files_warning.vue'; import axios from '~/lib/utils/axios_utils'; import * as urlUtils from '~/lib/utils/url_utility'; +import { stubPerformanceWebAPI } from 'helpers/performance'; import createDiffsStore from '../create_diffs_store'; import diffsMockData from '../mock_data/merge_request_diffs'; @@ -79,6 +80,7 @@ describe('diffs/components/app', () => { } beforeEach(() => { + stubPerformanceWebAPI(); // setup globals (needed for component to mount :/) window.mrTabs = { resetViewContainer: jest.fn(), diff --git a/spec/frontend/environment.js b/spec/frontend/environment.js index 8465b57c660..dc1c1dfbe4a 100644 --- a/spec/frontend/environment.js +++ b/spec/frontend/environment.js @@ -67,12 +67,6 @@ class CustomEnvironment extends JSDOMEnvironment { // Expose the jsdom (created in super class) to the global so that we can call reconfigure({ url: '' }) to properly set `window.location` this.global.jsdom = this.dom; - Object.assign(this.global.performance, { - mark: () => null, - measure: () => null, - getEntriesByName: () => [], - }); - // // Monaco-related environment variables // diff --git a/spec/frontend/ide/components/ide_spec.js b/spec/frontend/ide/components/ide_spec.js index 37b42001a80..9172c69b10e 100644 --- a/spec/frontend/ide/components/ide_spec.js +++ b/spec/frontend/ide/components/ide_spec.js @@ -2,6 +2,7 @@ import { shallowMount } from '@vue/test-utils'; import Vue from 'vue'; import Vuex from 'vuex'; import waitForPromises from 'helpers/wait_for_promises'; +import { stubPerformanceWebAPI } from 'helpers/performance'; import CannotPushCodeAlert from '~/ide/components/cannot_push_code_alert.vue'; import ErrorMessage from '~/ide/components/error_message.vue'; import Ide from '~/ide/components/ide.vue'; @@ -40,6 +41,8 @@ describe('WebIDE', () => { const findAlert = () => wrapper.findComponent(CannotPushCodeAlert); beforeEach(() => { + stubPerformanceWebAPI(); + store = createStore(); }); diff --git a/spec/frontend/ide/components/repo_editor_spec.js b/spec/frontend/ide/components/repo_editor_spec.js index 593fe6bf5a8..7a0bcda1b7a 100644 --- a/spec/frontend/ide/components/repo_editor_spec.js +++ b/spec/frontend/ide/components/repo_editor_spec.js @@ -6,6 +6,7 @@ import Vuex from 'vuex'; import { shallowMount } from '@vue/test-utils'; import '~/behaviors/markdown/render_gfm'; import waitForPromises from 'helpers/wait_for_promises'; +import { stubPerformanceWebAPI } from 'helpers/performance'; import { exampleConfigs, exampleFiles } from 'jest/ide/lib/editorconfig/mock_data'; import { EDITOR_CODE_INSTANCE_FN, EDITOR_DIFF_INSTANCE_FN } from '~/editor/constants'; import { EditorMarkdownExtension } from '~/editor/extensions/source_editor_markdown_ext'; @@ -130,6 +131,8 @@ describe('RepoEditor', () => { const findPreviewTab = () => wrapper.find('[data-testid="preview-tab"]'); beforeEach(() => { + stubPerformanceWebAPI(); + createInstanceSpy = jest.spyOn(SourceEditor.prototype, EDITOR_CODE_INSTANCE_FN); createDiffInstanceSpy = jest.spyOn(SourceEditor.prototype, EDITOR_DIFF_INSTANCE_FN); createModelSpy = jest.spyOn(monacoEditor, 'createModel'); diff --git a/spec/frontend/ide/ide_router_spec.js b/spec/frontend/ide/ide_router_spec.js index cd10812f8ea..adbdba1b11e 100644 --- a/spec/frontend/ide/ide_router_spec.js +++ b/spec/frontend/ide/ide_router_spec.js @@ -1,4 +1,5 @@ import waitForPromises from 'helpers/wait_for_promises'; +import { stubPerformanceWebAPI } from 'helpers/performance'; import { createRouter } from '~/ide/ide_router'; import { createStore } from '~/ide/stores'; @@ -12,6 +13,8 @@ describe('IDE router', () => { let router; beforeEach(() => { + stubPerformanceWebAPI(); + window.history.replaceState({}, '', '/'); store = createStore(); router = createRouter(store, DEFAULT_BRANCH); diff --git a/spec/frontend/ide/stores/actions/file_spec.js b/spec/frontend/ide/stores/actions/file_spec.js index 45d1beea3f8..6c1dee1e5ca 100644 --- a/spec/frontend/ide/stores/actions/file_spec.js +++ b/spec/frontend/ide/stores/actions/file_spec.js @@ -7,6 +7,7 @@ import { createStore } from '~/ide/stores'; import * as actions from '~/ide/stores/actions/file'; import * as types from '~/ide/stores/mutation_types'; import axios from '~/lib/utils/axios_utils'; +import { stubPerformanceWebAPI } from 'helpers/performance'; import { file, createTriggerRenameAction, createTriggerUpdatePayload } from '../../helpers'; const ORIGINAL_CONTENT = 'original content'; @@ -19,6 +20,8 @@ describe('IDE store file actions', () => { let router; beforeEach(() => { + stubPerformanceWebAPI(); + mock = new MockAdapter(axios); originalGon = window.gon; window.gon = { diff --git a/spec/frontend/ide/stores/actions/merge_request_spec.js b/spec/frontend/ide/stores/actions/merge_request_spec.js index 5592e2664c4..abc3ba5b0a2 100644 --- a/spec/frontend/ide/stores/actions/merge_request_spec.js +++ b/spec/frontend/ide/stores/actions/merge_request_spec.js @@ -1,5 +1,6 @@ import MockAdapter from 'axios-mock-adapter'; import { range } from 'lodash'; +import { stubPerformanceWebAPI } from 'helpers/performance'; import { TEST_HOST } from 'helpers/test_constants'; import testAction from 'helpers/vuex_action_helper'; import createFlash from '~/flash'; @@ -35,6 +36,8 @@ describe('IDE store merge request actions', () => { let mock; beforeEach(() => { + stubPerformanceWebAPI(); + store = createStore(); mock = new MockAdapter(axios); diff --git a/spec/frontend/ide/stores/actions/tree_spec.js b/spec/frontend/ide/stores/actions/tree_spec.js index fc44cbb21ae..d43393875eb 100644 --- a/spec/frontend/ide/stores/actions/tree_spec.js +++ b/spec/frontend/ide/stores/actions/tree_spec.js @@ -1,4 +1,5 @@ import MockAdapter from 'axios-mock-adapter'; +import { stubPerformanceWebAPI } from 'helpers/performance'; import { TEST_HOST } from 'helpers/test_constants'; import testAction from 'helpers/vuex_action_helper'; import { createRouter } from '~/ide/ide_router'; @@ -24,6 +25,8 @@ describe('Multi-file store tree actions', () => { }; beforeEach(() => { + stubPerformanceWebAPI(); + store = createStore(); router = createRouter(store); jest.spyOn(router, 'push').mockImplementation(); diff --git a/spec/frontend/ide/stores/actions_spec.js b/spec/frontend/ide/stores/actions_spec.js index 3889c4f11c3..f6d54491d77 100644 --- a/spec/frontend/ide/stores/actions_spec.js +++ b/spec/frontend/ide/stores/actions_spec.js @@ -1,4 +1,5 @@ import MockAdapter from 'axios-mock-adapter'; +import { stubPerformanceWebAPI } from 'helpers/performance'; import testAction from 'helpers/vuex_action_helper'; import eventHub from '~/ide/eventhub'; import { createRouter } from '~/ide/ide_router'; @@ -34,6 +35,8 @@ describe('Multi-file store actions', () => { let router; beforeEach(() => { + stubPerformanceWebAPI(); + store = createStore(); router = createRouter(store); diff --git a/spec/frontend/merge_request_tabs_spec.js b/spec/frontend/merge_request_tabs_spec.js index f0f051cbc8b..96ac655381b 100644 --- a/spec/frontend/merge_request_tabs_spec.js +++ b/spec/frontend/merge_request_tabs_spec.js @@ -2,6 +2,7 @@ import MockAdapter from 'axios-mock-adapter'; import $ from 'jquery'; import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; import initMrPage from 'helpers/init_vue_mr_page_helper'; +import { stubPerformanceWebAPI } from 'helpers/performance'; import axios from '~/lib/utils/axios_utils'; import MergeRequestTabs from '~/merge_request_tabs'; import '~/lib/utils/common_utils'; @@ -24,6 +25,8 @@ describe('MergeRequestTabs', () => { }; beforeEach(() => { + stubPerformanceWebAPI(); + initMrPage(); testContext.class = new MergeRequestTabs({ stubLocation }); @@ -350,10 +353,6 @@ describe('MergeRequestTabs', () => { describe('when switching tabs', () => { const SCROLL_TOP = 100; - beforeAll(() => { - jest.useFakeTimers(); - }); - beforeEach(() => { jest.spyOn(window, 'scrollTo').mockImplementation(() => {}); testContext.class.mergeRequestTabs = document.createElement('div'); @@ -362,10 +361,6 @@ describe('MergeRequestTabs', () => { testContext.class.scrollPositions = { newTab: SCROLL_TOP }; }); - afterAll(() => { - jest.useRealTimers(); - }); - it('scrolls to the stored position, if one is stored', () => { testContext.class.tabShown('newTab'); diff --git a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js index 46f83ac89e5..85660d09baa 100644 --- a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js +++ b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js @@ -51,6 +51,7 @@ const defaultProps = { requestCveAvailable: true, confirmationPhrase: 'my-fake-project', showVisibilityConfirmModal: false, + membersPagePath: '/my-fake-project/-/project_members', }; const FEATURE_ACCESS_LEVEL_ANONYMOUS = 30; @@ -59,7 +60,7 @@ describe('Settings Panel', () => { let wrapper; const mountComponent = ( - { currentSettings = {}, glFeatures = {}, ...customProps } = {}, + { currentSettings = {}, glFeatures = {}, stubs = {}, ...customProps } = {}, mountFn = shallowMount, ) => { const propsData = { @@ -76,6 +77,7 @@ describe('Settings Panel', () => { ...glFeatures, }, }, + stubs, }); }; @@ -176,7 +178,7 @@ describe('Settings Panel', () => { ); it('should set the visibility level description based upon the selected visibility level', () => { - wrapper = mountComponent(); + wrapper = mountComponent({ stubs: { GlSprintf } }); findProjectVisibilityLevelInput().setValue(visibilityOptions.INTERNAL); diff --git a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js index 49d64c6eac0..34784a88ba9 100644 --- a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js +++ b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js @@ -5,6 +5,7 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import createMockApollo from 'helpers/mock_apollo_helper'; +import { stubPerformanceWebAPI } from 'helpers/performance'; import waitForPromises from 'helpers/wait_for_promises'; import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql'; import getUserCallouts from '~/graphql_shared/queries/get_user_callouts.query.graphql'; @@ -502,9 +503,7 @@ describe('Pipeline graph wrapper', () => { describe('when no duration is obtained', () => { beforeEach(async () => { - jest.spyOn(window.performance, 'getEntriesByName').mockImplementation(() => { - return []; - }); + stubPerformanceWebAPI(); createComponentWithApollo({ provide: { diff --git a/spec/frontend/snippets/components/edit_spec.js b/spec/frontend/snippets/components/edit_spec.js index 8a767765149..f49ceb2fede 100644 --- a/spec/frontend/snippets/components/edit_spec.js +++ b/spec/frontend/snippets/components/edit_spec.js @@ -5,6 +5,7 @@ import { merge } from 'lodash'; import VueApollo, { ApolloMutation } from 'vue-apollo'; import { useFakeDate } from 'helpers/fake_date'; import createMockApollo from 'helpers/mock_apollo_helper'; +import { stubPerformanceWebAPI } from 'helpers/performance'; import waitForPromises from 'helpers/wait_for_promises'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import GetSnippetQuery from 'shared_queries/snippet/snippet.query.graphql'; @@ -96,6 +97,8 @@ describe('Snippet Edit app', () => { const originalRelativeUrlRoot = gon.relative_url_root; beforeEach(() => { + stubPerformanceWebAPI(); + getSpy = jest.fn().mockResolvedValue(createQueryResponse()); // See `mutateSpy` declaration comment for why we send a key diff --git a/spec/frontend/snippets/components/show_spec.js b/spec/frontend/snippets/components/show_spec.js index c73bf8f80a2..b29ed97099f 100644 --- a/spec/frontend/snippets/components/show_spec.js +++ b/spec/frontend/snippets/components/show_spec.js @@ -12,6 +12,7 @@ import { SNIPPET_VISIBILITY_PUBLIC, } from '~/snippets/constants'; import CloneDropdownButton from '~/vue_shared/components/clone_dropdown.vue'; +import { stubPerformanceWebAPI } from 'helpers/performance'; describe('Snippet view app', () => { let wrapper; @@ -45,6 +46,10 @@ describe('Snippet view app', () => { const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); const findEmbedDropdown = () => wrapper.findComponent(EmbedDropdown); + beforeEach(() => { + stubPerformanceWebAPI(); + }); + afterEach(() => { wrapper.destroy(); }); diff --git a/spec/frontend_integration/diffs/diffs_interopability_spec.js b/spec/frontend_integration/diffs/diffs_interopability_spec.js index 064e3d21180..8e9bc4f0a5f 100644 --- a/spec/frontend_integration/diffs/diffs_interopability_spec.js +++ b/spec/frontend_integration/diffs/diffs_interopability_spec.js @@ -1,6 +1,7 @@ import { waitFor } from '@testing-library/dom'; import setWindowLocation from 'helpers/set_window_location_helper'; import { TEST_HOST } from 'helpers/test_constants'; +import { stubPerformanceWebAPI } from 'helpers/performance'; import initDiffsApp from '~/diffs'; import { createStore } from '~/mr_notes/stores'; import { @@ -74,6 +75,10 @@ const startDiffsApp = () => { describe('diffs third party interoperability', () => { let vm; + beforeEach(() => { + stubPerformanceWebAPI(); + }); + afterEach(() => { vm.$destroy(); document.body.innerHTML = ''; diff --git a/spec/frontend_integration/ide/ide_integration_spec.js b/spec/frontend_integration/ide/ide_integration_spec.js index a002ce91deb..da48c600764 100644 --- a/spec/frontend_integration/ide/ide_integration_spec.js +++ b/spec/frontend_integration/ide/ide_integration_spec.js @@ -3,8 +3,9 @@ import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; import { setTestTimeout } from 'helpers/timeout'; import waitForPromises from 'helpers/wait_for_promises'; import { waitForText } from 'helpers/wait_for_text'; -import { createCommitId } from 'test_helpers/factories/commit_id'; import { useOverclockTimers } from 'test_helpers/utils/overclock_timers'; +import { createCommitId } from 'test_helpers/factories/commit_id'; +import { stubPerformanceWebAPI } from 'helpers/performance'; import * as ideHelper from './helpers/ide_helper'; import startWebIDE from './helpers/start'; @@ -15,6 +16,7 @@ describe('WebIDE', () => { let container; beforeEach(() => { + stubPerformanceWebAPI(); // For some reason these tests were timing out in CI. // We will investigate in https://gitlab.com/gitlab-org/gitlab/-/issues/298714 setTestTimeout(20000); diff --git a/spec/frontend_integration/ide/user_opens_file_spec.js b/spec/frontend_integration/ide/user_opens_file_spec.js index c3131f6ad45..af6e2f3b44b 100644 --- a/spec/frontend_integration/ide/user_opens_file_spec.js +++ b/spec/frontend_integration/ide/user_opens_file_spec.js @@ -1,6 +1,7 @@ import { screen } from '@testing-library/dom'; import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; import { useOverclockTimers } from 'test_helpers/utils/overclock_timers'; +import { stubPerformanceWebAPI } from 'helpers/performance'; import * as ideHelper from './helpers/ide_helper'; import startWebIDE from './helpers/start'; @@ -11,6 +12,7 @@ describe('IDE: User opens a file in the Web IDE', () => { let container; beforeEach(async () => { + stubPerformanceWebAPI(); setHTMLFixture('<div class="webide-container"></div>'); container = document.querySelector('.webide-container'); diff --git a/spec/frontend_integration/ide/user_opens_ide_spec.js b/spec/frontend_integration/ide/user_opens_ide_spec.js index b2b85452451..552888f04a5 100644 --- a/spec/frontend_integration/ide/user_opens_ide_spec.js +++ b/spec/frontend_integration/ide/user_opens_ide_spec.js @@ -1,6 +1,7 @@ import { screen } from '@testing-library/dom'; import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; import { useOverclockTimers } from 'test_helpers/utils/overclock_timers'; +import { stubPerformanceWebAPI } from 'helpers/performance'; import * as ideHelper from './helpers/ide_helper'; import startWebIDE from './helpers/start'; @@ -11,6 +12,8 @@ describe('IDE: User opens IDE', () => { let container; beforeEach(() => { + stubPerformanceWebAPI(); + setHTMLFixture('<div class="webide-container"></div>'); container = document.querySelector('.webide-container'); }); diff --git a/spec/frontend_integration/ide/user_opens_mr_spec.js b/spec/frontend_integration/ide/user_opens_mr_spec.js index 084aae9f297..af0276a5055 100644 --- a/spec/frontend_integration/ide/user_opens_mr_spec.js +++ b/spec/frontend_integration/ide/user_opens_mr_spec.js @@ -2,6 +2,7 @@ import { basename } from 'path'; import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; import { getMergeRequests, getMergeRequestWithChanges } from 'test_helpers/fixtures'; import { useOverclockTimers } from 'test_helpers/utils/overclock_timers'; +import { stubPerformanceWebAPI } from 'helpers/performance'; import * as ideHelper from './helpers/ide_helper'; import startWebIDE from './helpers/start'; @@ -16,6 +17,8 @@ describe('IDE: User opens Merge Request', () => { let changes; beforeEach(async () => { + stubPerformanceWebAPI(); + const [{ iid: mrId }] = getMergeRequests(); changes = getRelevantChanges(); diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 42c97b61d19..b7cc8c217a4 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -969,6 +969,10 @@ RSpec.describe ProjectsHelper do containerRegistryAccessLevel: project.project_feature.container_registry_access_level ) end + + it 'includes membersPagePath' do + expect(subject).to include(membersPagePath: project_project_members_path(project)) + end end describe '#project_classes' do diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index e8973de5fd5..12053d56467 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -1041,6 +1041,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do 'CI_MERGE_REQUEST_PROJECT_PATH' => merge_request.project.full_path, 'CI_MERGE_REQUEST_PROJECT_URL' => merge_request.project.web_url, 'CI_MERGE_REQUEST_TARGET_BRANCH_NAME' => merge_request.target_branch.to_s, + 'CI_MERGE_REQUEST_TARGET_BRANCH_PROTECTED' => ProtectedBranch.protected?(merge_request.target_project, merge_request.target_branch).to_s, 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA' => '', 'CI_MERGE_REQUEST_SOURCE_PROJECT_ID' => merge_request.source_project.id.to_s, 'CI_MERGE_REQUEST_SOURCE_PROJECT_PATH' => merge_request.source_project.full_path, @@ -1137,6 +1138,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do 'CI_MERGE_REQUEST_PROJECT_PATH' => merge_request.project.full_path, 'CI_MERGE_REQUEST_PROJECT_URL' => merge_request.project.web_url, 'CI_MERGE_REQUEST_TARGET_BRANCH_NAME' => merge_request.target_branch.to_s, + 'CI_MERGE_REQUEST_TARGET_BRANCH_PROTECTED' => ProtectedBranch.protected?(merge_request.target_project, merge_request.target_branch).to_s, 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA' => merge_request.target_branch_sha, 'CI_MERGE_REQUEST_SOURCE_PROJECT_ID' => merge_request.source_project.id.to_s, 'CI_MERGE_REQUEST_SOURCE_PROJECT_PATH' => merge_request.source_project.full_path, @@ -4851,13 +4853,13 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do end end - describe '#has_expired_test_reports?' do - subject { pipeline.has_expired_test_reports? } + describe '#has_test_reports?' do + subject { pipeline.has_test_reports? } let(:pipeline) { create(:ci_pipeline, :success, :with_test_reports) } context 'when artifacts are not expired' do - it { is_expected.to be_falsey } + it { is_expected.to be_truthy } end context 'when artifacts are expired' do @@ -4868,6 +4870,14 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do it { is_expected.to be_truthy } end + context 'when artifacts are removed' do + before do + pipeline.job_artifacts.each(&:destroy) + end + + it { is_expected.to be_falsey } + end + context 'when the pipeline is still running' do let(:pipeline) { create(:ci_pipeline, :running) } diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb index d2d7859e726..51351c9fdd1 100644 --- a/spec/models/remote_mirror_spec.rb +++ b/spec/models/remote_mirror_spec.rb @@ -5,6 +5,10 @@ require 'spec_helper' RSpec.describe RemoteMirror, :mailer do include GitHelpers + before do + stub_feature_flags(remote_mirror_no_delay: false) + end + describe 'URL validation' do context 'with a valid URL' do it 'is valid' do @@ -343,6 +347,20 @@ RSpec.describe RemoteMirror, :mailer do remote_mirror.sync end + + context 'when remote_mirror_no_delay is enabled' do + before do + stub_feature_flags(remote_mirror_no_delay: true) + end + + it 'schedules a RepositoryUpdateRemoteMirrorWorker to run now' do + remote_mirror.last_update_started_at = Time.current - 30.seconds + + expect(RepositoryUpdateRemoteMirrorWorker).to receive(:perform_async).with(remote_mirror.id, Time.current) + + remote_mirror.sync + end + end end end end diff --git a/spec/requests/api/protected_tags_spec.rb b/spec/requests/api/protected_tags_spec.rb index cc7261dafc9..84b7df86f31 100644 --- a/spec/requests/api/protected_tags_spec.rb +++ b/spec/requests/api/protected_tags_spec.rb @@ -3,9 +3,10 @@ require 'spec_helper' RSpec.describe API::ProtectedTags do - let(:user) { create(:user) } - let!(:project) { create(:project, :repository) } - let(:project2) { create(:project, path: 'project2', namespace: user.namespace) } + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :repository) } + let_it_be(:project2) { create(:project, path: 'project2', namespace: user.namespace) } + let(:protected_name) { 'feature' } let(:tag_name) { protected_name } let!(:protected_tag) do diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index b1b45e89c1b..48756cf774b 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -68,6 +68,9 @@ RSpec.describe Projects::ForkService do it { expect(to_project.avatar.file).to be_exists } it { expect(to_project.ci_config_path).to eq(@from_project.ci_config_path) } it { expect(to_project.external_authorization_classification_label).to eq(@from_project.external_authorization_classification_label) } + it { expect(to_project.suggestion_commit_message).to eq(@from_project.suggestion_commit_message) } + it { expect(to_project.merge_commit_template).to eq(@from_project.merge_commit_template) } + it { expect(to_project.squash_commit_template).to eq(@from_project.squash_commit_template) } # This test is here because we had a bug where the from-project lost its # avatar after being forked. diff --git a/spec/views/devise/sessions/new.html.haml_spec.rb b/spec/views/devise/sessions/new.html.haml_spec.rb index e8232a2c067..b3cd1493149 100644 --- a/spec/views/devise/sessions/new.html.haml_spec.rb +++ b/spec/views/devise/sessions/new.html.haml_spec.rb @@ -63,6 +63,36 @@ RSpec.describe 'devise/sessions/new' do end end + describe 'Google Tag Manager' do + let!(:gtm_id) { 'GTM-WWKMTWS'} + + subject { rendered } + + before do + stub_devise + disable_captcha + stub_config(extra: { google_tag_manager_id: gtm_id, google_tag_manager_nonce_id: gtm_id }) + end + + describe 'when Google Tag Manager is enabled' do + before do + enable_gtm + render + end + + it { is_expected.to match /www.googletagmanager.com/ } + end + + describe 'when Google Tag Manager is disabled' do + before do + disable_gtm + render + end + + it { is_expected.not_to match /www.googletagmanager.com/ } + end + end + def disable_other_signin_methods allow(view).to receive(:password_authentication_enabled_for_web?).and_return(false) allow(view).to receive(:omniauth_enabled?).and_return(false) @@ -94,4 +124,12 @@ RSpec.describe 'devise/sessions/new' do allow(view).to receive(:captcha_enabled?).and_return(false) allow(view).to receive(:captcha_on_login_required?).and_return(false) end + + def disable_gtm + allow(view).to receive(:google_tag_manager_enabled?).and_return(false) + end + + def enable_gtm + allow(view).to receive(:google_tag_manager_enabled?).and_return(true) + end end |