diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-12-01 21:14:38 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-12-01 21:14:38 +0300 |
commit | 07cbb41fee42601767b3aea2979d6fa6d990ce5b (patch) | |
tree | 00ba0463347c4e2951660c7236652bb24750976d | |
parent | c3ddbeb162e4261f4ce3df291909fadeba637995 (diff) |
Add latest changes from gitlab-org/gitlab@master
53 files changed, 845 insertions, 226 deletions
diff --git a/.rubocop_todo/style/redundant_return.yml b/.rubocop_todo/style/redundant_return.yml index 344dbbbc4cd..86f8bb37d67 100644 --- a/.rubocop_todo/style/redundant_return.yml +++ b/.rubocop_todo/style/redundant_return.yml @@ -73,7 +73,6 @@ Style/RedundantReturn: - 'ee/app/serializers/vulnerabilities/merge_request_link_entity.rb' - 'ee/app/services/audit_events/streaming/headers/base.rb' - 'ee/app/services/ee/post_receive_service.rb' - - 'ee/app/services/gitlab_subscriptions/user_add_on_assignments/create_service.rb' - 'ee/app/services/security/orchestration/assign_service.rb' - 'ee/app/services/vulnerabilities/manually_create_service.rb' - 'ee/app/workers/ee/repository_check/batch_worker.rb' diff --git a/app/assets/javascripts/content_editor/extensions/reference.js b/app/assets/javascripts/content_editor/extensions/reference.js index fd248709b5a..0c385481ac5 100644 --- a/app/assets/javascripts/content_editor/extensions/reference.js +++ b/app/assets/javascripts/content_editor/extensions/reference.js @@ -72,11 +72,20 @@ export default Node.create({ addInputRules() { const { editor } = this; const { assetResolver } = this.options; - const referenceInputRegex = /(?:^|\s)([\w/]*([!&#])\d+(\+?s?))(?:\s|\n)/m; + const referenceInputRegex = /(?:^|\s)([\w/]*([#!&%$@~]|\[vulnerability:)[\w.]+(\+?s?\]?))(?:\s|\n)/m; const referenceTypes = { '#': 'issue', '!': 'merge_request', '&': 'epic', + '%': 'milestone', + $: 'snippet', + '@': 'user', + '~': 'label', + '[vulnerability:': 'vulnerability', + }; + const nodeTypes = { + label: editor.schema.nodes.referenceLabel, + default: editor.schema.nodes.reference, }; return [ @@ -91,22 +100,26 @@ export default Node.create({ text, expandedText, fullyExpandedText, + backgroundColor, } = await assetResolver.resolveReference(referenceId); if (!text) return; let referenceText = text; - if (expansionType === '+') referenceText = expandedText; - if (expansionType === '+s') referenceText = fullyExpandedText; + if (expansionType === '+') referenceText = expandedText || text; + if (expansionType === '+s') referenceText = fullyExpandedText || text; const position = findReference(editor, referenceId); if (!position) return; + const nodeType = nodeTypes[referenceType] || nodeTypes.default; + editor.view.dispatch( editor.state.tr.replaceWith(position, position + referenceId.length, [ - this.type.create({ + nodeType.create({ referenceType, originalText: referenceId, + color: backgroundColor, href, text: referenceText, }), diff --git a/app/assets/javascripts/content_editor/services/asset_resolver.js b/app/assets/javascripts/content_editor/services/asset_resolver.js index 0d4396fc176..07a69db7428 100644 --- a/app/assets/javascripts/content_editor/services/asset_resolver.js +++ b/app/assets/javascripts/content_editor/services/asset_resolver.js @@ -27,10 +27,11 @@ export default class AssetResolver { if (!a.length) return {}; return { - href: a[0].getAttribute('href'), - text: a[0].textContent, - expandedText: a[1].textContent, - fullyExpandedText: a[2].textContent, + href: a[0]?.getAttribute('href'), + text: a[0]?.textContent, + expandedText: a[1]?.textContent, + fullyExpandedText: a[2]?.textContent, + backgroundColor: a[0]?.firstElementChild?.style.backgroundColor, }; }); diff --git a/app/controllers/concerns/import/github_oauth.rb b/app/controllers/concerns/import/github_oauth.rb index ae5a0401155..fa6162254dc 100644 --- a/app/controllers/concerns/import/github_oauth.rb +++ b/app/controllers/concerns/import/github_oauth.rb @@ -56,7 +56,11 @@ module Import session[:auth_on_failure_path] = "#{new_project_path}#import_project" oauth_client.auth_code.authorize_url( redirect_uri: callback_import_url, - scope: 'repo, user, user:email', + # read:org only required for collaborator import, which is optional, + # but at the time of this OAuth request we do not know which optional + # configuration the user will select because the options are only shown + # after authenticating + scope: 'repo, read:org', state: state ) end diff --git a/app/models/ability.rb b/app/models/ability.rb index 6ea48f2d668..f4654f52f7a 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -79,10 +79,10 @@ class Ability policy = policy_for(user, subject, **opts.slice(:cache)) # https://gitlab.com/gitlab-org/gitlab/-/issues/421150#note_1638311666 - if ability == :read_namespace && Feature.enabled?(:log_read_namespace_usages, Feature.current_request) + if ability.to_sym == :read_namespace && Feature.enabled?(:log_read_namespace_usages, Feature.current_request) Gitlab::AppLogger.info( message: 'Ability is in use', - ability: ability, + ability: ability.to_sym, caller_locations: caller_locations(1, 5).map(&:to_s) ) end diff --git a/app/models/ci/build_dependencies.rb b/app/models/ci/build_dependencies.rb index c4a04d42a1e..e1521e2f5fa 100644 --- a/app/models/ci/build_dependencies.rb +++ b/app/models/ci/build_dependencies.rb @@ -36,7 +36,7 @@ module Ci next [] if no_local_dependencies_specified? next [] unless processable.pipeline_id # we don't have any dependency when creating the pipeline - deps = model_class.where(pipeline_id: processable.pipeline_id).latest + deps = model_class.where(pipeline_id: processable.pipeline_id, partition_id: processable.partition_id).latest deps = find_dependencies(processable, deps) from_dependencies(deps).to_a diff --git a/app/models/concerns/enums/sbom.rb b/app/models/concerns/enums/sbom.rb index 9c3bbc92e86..4e54e48e667 100644 --- a/app/models/concerns/enums/sbom.rb +++ b/app/models/concerns/enums/sbom.rb @@ -22,10 +22,37 @@ module Enums wolfi: 13 }.with_indifferent_access.freeze + DEPENDENCY_SCANNING_PURL_TYPES = %w[ + composer + conan + gem + golang + maven + npm + nuget + pypi + ].freeze + + CONTAINER_SCANNING_PURL_TYPES = %w[ + apk + rpm + deb + cbl-mariner + wolfi + ].freeze + def self.component_types COMPONENT_TYPES end + def self.dependency_scanning_purl_type?(purl_type) + DEPENDENCY_SCANNING_PURL_TYPES.include?(purl_type) + end + + def self.container_scanning_purl_type?(purl_type) + CONTAINER_SCANNING_PURL_TYPES.include?(purl_type) + end + def self.purl_types # return 0 by default if the purl_type is not found, to prevent # consumers from producing invalid SQL caused by null entries diff --git a/app/models/repository.rb b/app/models/repository.rb index 72a40ce5f01..5ab35ed1ef9 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1278,15 +1278,6 @@ class Repository .map(&:to_h) end - def filter_generated_files(revision, paths) - # NOTE: We should still support linguist-generated override for GitHub compatibility, - # but `gitlab-*` prefixed overrides would give us a better control moving forward. - generated_files = get_file_attributes(revision, paths, %w[gitlab-generated linguist-generated]) - .pluck(:path) - - paths - generated_files - end - def object_format return unless exists? diff --git a/app/models/users/callout.rb b/app/models/users/callout.rb index a9880e56e8c..924e130aab5 100644 --- a/app/models/users/callout.rb +++ b/app/models/users/callout.rb @@ -77,7 +77,8 @@ module Users vsd_feedback_banner: 75, # EE-only security_policy_protected_branch_modification: 76, # EE-only vulnerability_report_grouping: 77, # EE-only - new_nav_for_everyone_callout: 78 + new_nav_for_everyone_callout: 78, + duo_chat_callout: 79 # EE-only } validates :feature_name, diff --git a/app/services/ml/create_model_version_service.rb b/app/services/ml/create_model_version_service.rb index ff27e78221f..e7a59210107 100644 --- a/app/services/ml/create_model_version_service.rb +++ b/app/services/ml/create_model_version_service.rb @@ -7,19 +7,38 @@ module Ml @version = params[:version] @package = params[:package] @description = params[:description] + @user = params[:user] end def execute - @version ||= Ml::IncrementVersionService.new(@model.latest_version.try(:version)).execute + ApplicationRecord.transaction do + @version ||= Ml::IncrementVersionService.new(@model.latest_version.try(:version)).execute - model_version = Ml::ModelVersion.find_or_create!(@model, @version, @package, @description) + package = @package || find_or_create_package(@model.name, @version) - model_version.candidate = ::Ml::CreateCandidateService.new( - @model.default_experiment, - { model_version: model_version } - ).execute + model_version = Ml::ModelVersion.create!(model: @model, project: @model.project, version: @version, + package: package, description: @description) - model_version + model_version.candidate = ::Ml::CreateCandidateService.new( + @model.default_experiment, + { model_version: model_version } + ).execute + + model_version + end + end + + private + + def find_or_create_package(model_name, model_version) + package_params = { + name: model_name, + version: model_version + } + + ::Packages::MlModel::FindOrCreatePackageService + .new(@model.project, @user, package_params) + .execute end end end diff --git a/db/docs/batched_background_migrations/backfill_finding_id_in_vulnerabilities.yml b/db/docs/batched_background_migrations/backfill_finding_id_in_vulnerabilities.yml index 84febc79e6f..8bc99c1ca2a 100644 --- a/db/docs/batched_background_migrations/backfill_finding_id_in_vulnerabilities.yml +++ b/db/docs/batched_background_migrations/backfill_finding_id_in_vulnerabilities.yml @@ -2,7 +2,7 @@ migration_job_name: BackfillFindingIdInVulnerabilities description: Backfills finding_id column on vulnerabilities table for a proper 1:1 relation feature_category: vulnerability_management -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/418971 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/130058 queued_migration_version: 20231116105945 milestone: 16.4 finalize_after: '2023-12-15' diff --git a/doc/.vale/gitlab/SubstitutionWarning.yml b/doc/.vale/gitlab/SubstitutionWarning.yml index 6860b308b46..d4bbe9fd83b 100644 --- a/doc/.vale/gitlab/SubstitutionWarning.yml +++ b/doc/.vale/gitlab/SubstitutionWarning.yml @@ -28,7 +28,7 @@ swap: e-mail: "email" emojis: "emoji" ex: "for example" - file name: "filename" + filename: "file name" filesystem: "file system" info: "information" installation from source: self-compiled installation diff --git a/doc/administration/settings/usage_statistics.md b/doc/administration/settings/usage_statistics.md index 51a6fa153d1..e86493590ab 100644 --- a/doc/administration/settings/usage_statistics.md +++ b/doc/administration/settings/usage_statistics.md @@ -74,6 +74,7 @@ In the following table, you can see: | [Сross-project pipelines with artifacts dependencies](../../ci/yaml/index.md#needsproject) | GitLab 16.7 and later | | [Feature flag related issues](../../operations/feature_flags.md#feature-flag-related-issues) | GitLab 16.7 and later | | [Merged results pipelines](../../ci/pipelines/merged_results_pipelines.md) | GitLab 16.7 and later | +| [CI/CD for GitHub](../../ci/ci_cd_for_external_repos/github_integration.md) | GitLab 16.7 and later | ### Enable registration features diff --git a/doc/api/commits.md b/doc/api/commits.md index 94cdaaa191d..931893669fa 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -65,7 +65,9 @@ Example response: "parent_ids": [ "6104942438c14ec7bd21c6cd5bd995272b3faff6" ], - "web_url": "https://gitlab.example.com/janedoe/gitlab-foss/-/commit/ed899a2f4b50b4370feeea94676502b42383c746" + "web_url": "https://gitlab.example.com/janedoe/gitlab-foss/-/commit/ed899a2f4b50b4370feeea94676502b42383c746", + "trailers": {}, + "extended_trailers": {} }, { "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6", @@ -76,11 +78,13 @@ Example response: "committer_name": "ExampleName", "committer_email": "user@example.com", "created_at": "2021-09-20T09:06:12.201+00:00", - "message": "Sanitize for network graph", + "message": "Sanitize for network graph\nCc: John Doe <johndoe@gitlab.com>\nCc: Jane Doe <janedoe@gitlab.com>", "parent_ids": [ "ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba" ], - "web_url": "https://gitlab.example.com/janedoe/gitlab-foss/-/commit/ed899a2f4b50b4370feeea94676502b42383c746" + "web_url": "https://gitlab.example.com/janedoe/gitlab-foss/-/commit/ed899a2f4b50b4370feeea94676502b42383c746", + "trailers": { "Cc": "Jane Doe <janedoe@gitlab.com>" }, + "extended_trailers": { "Cc": ["John Doe <johndoe@gitlab.com>", "Jane Doe <janedoe@gitlab.com>"] } } ] ``` diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index bf25ee8182b..3703d8e1560 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -9513,6 +9513,30 @@ The edge type for [`CiStage`](#cistage). | <a id="cistageedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. | | <a id="cistageedgenode"></a>`node` | [`CiStage`](#cistage) | The item at the end of the edge. | +#### `CiSubscriptionsProjectConnection` + +The connection type for [`CiSubscriptionsProject`](#cisubscriptionsproject). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="cisubscriptionsprojectconnectioncount"></a>`count` | [`Int!`](#int) | Total count of collection. | +| <a id="cisubscriptionsprojectconnectionedges"></a>`edges` | [`[CiSubscriptionsProjectEdge]`](#cisubscriptionsprojectedge) | A list of edges. | +| <a id="cisubscriptionsprojectconnectionnodes"></a>`nodes` | [`[CiSubscriptionsProject]`](#cisubscriptionsproject) | A list of nodes. | +| <a id="cisubscriptionsprojectconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | + +#### `CiSubscriptionsProjectEdge` + +The edge type for [`CiSubscriptionsProject`](#cisubscriptionsproject). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="cisubscriptionsprojectedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. | +| <a id="cisubscriptionsprojectedgenode"></a>`node` | [`CiSubscriptionsProject`](#cisubscriptionsproject) | The item at the end of the edge. | + #### `ClusterAgentActivityEventConnection` The connection type for [`ClusterAgentActivityEvent`](#clusteragentactivityevent). @@ -23718,6 +23742,8 @@ Represents vulnerability finding of a security report on the pipeline. | <a id="projectcicdsettings"></a>`ciCdSettings` | [`ProjectCiCdSetting`](#projectcicdsetting) | CI/CD settings for the project. | | <a id="projectciconfigpathordefault"></a>`ciConfigPathOrDefault` | [`String!`](#string) | Path of the CI configuration file. | | <a id="projectcijobtokenscope"></a>`ciJobTokenScope` | [`CiJobTokenScopeType`](#cijobtokenscopetype) | The CI Job Tokens scope of access. | +| <a id="projectcisubscribedprojects"></a>`ciSubscribedProjects` | [`CiSubscriptionsProjectConnection`](#cisubscriptionsprojectconnection) | Triggers a new pipeline in the downstream project when a pipeline successfullycompletes on the(upstream) project. (see [Connections](#connections)) | +| <a id="projectcisubscriptionsprojects"></a>`ciSubscriptionsProjects` | [`CiSubscriptionsProjectConnection`](#cisubscriptionsprojectconnection) | Triggers a new pipeline in the(downstream) project when a pipeline successfullycompletes on the upstream project. (see [Connections](#connections)) | | <a id="projectcodecoveragesummary"></a>`codeCoverageSummary` | [`CodeCoverageSummary`](#codecoveragesummary) | Code coverage summary associated with the project. | | <a id="projectcomplianceframeworks"></a>`complianceFrameworks` | [`ComplianceFrameworkConnection`](#complianceframeworkconnection) | Compliance frameworks associated with the project. (see [Connections](#connections)) | | <a id="projectcontainerexpirationpolicy"></a>`containerExpirationPolicy` | [`ContainerExpirationPolicy`](#containerexpirationpolicy) | Container expiration policy of the project. | @@ -31056,6 +31082,7 @@ Name of the feature that the callout is for. | <a id="usercalloutfeaturenameenumci_deprecation_warning_for_types_keyword"></a>`CI_DEPRECATION_WARNING_FOR_TYPES_KEYWORD` | Callout feature name for ci_deprecation_warning_for_types_keyword. | | <a id="usercalloutfeaturenameenumcloud_licensing_subscription_activation_banner"></a>`CLOUD_LICENSING_SUBSCRIPTION_ACTIVATION_BANNER` | Callout feature name for cloud_licensing_subscription_activation_banner. | | <a id="usercalloutfeaturenameenumcluster_security_warning"></a>`CLUSTER_SECURITY_WARNING` | Callout feature name for cluster_security_warning. | +| <a id="usercalloutfeaturenameenumduo_chat_callout"></a>`DUO_CHAT_CALLOUT` | Callout feature name for duo_chat_callout. | | <a id="usercalloutfeaturenameenumeoa_bronze_plan_banner"></a>`EOA_BRONZE_PLAN_BANNER` | Callout feature name for eoa_bronze_plan_banner. | | <a id="usercalloutfeaturenameenumfeature_flags_new_version"></a>`FEATURE_FLAGS_NEW_VERSION` | Callout feature name for feature_flags_new_version. | | <a id="usercalloutfeaturenameenumgcp_signup_offer"></a>`GCP_SIGNUP_OFFER` | Callout feature name for gcp_signup_offer. | diff --git a/doc/architecture/blueprints/cells/index.md b/doc/architecture/blueprints/cells/index.md index d4213706189..d2578072800 100644 --- a/doc/architecture/blueprints/cells/index.md +++ b/doc/architecture/blueprints/cells/index.md @@ -108,9 +108,9 @@ The first 2-3 quarters are required to define a general split of data, and build The purpose is to perform a targeted decomposition of `users` and `projects`, because `projects` will be stored locally in the Cell. -1. **User can create files in repository** +1. **User can create Project with a README file** - The purpose is to allow `users` to create files in a repository. + The purpose is to allow `users` to create README files in a project. 1. **User can change profile avatar that is shared in cluster.** @@ -284,7 +284,7 @@ It is expected that initial iterations will be rather slow, because they require - Data access layer: Data access layer. - Routing: User can use single domain to interact with many Cells. - Cell deployment: Extend GitLab Dedicated to support GCP. -- Essential workflows: User can create files in repository. +- Essential workflows: User can create Project with a README file. - Essential workflows: User can push to Git repository. - Essential workflows: User can run CI pipeline. - Essential workflows: Instance-wide settings are shared across cluster. diff --git a/doc/architecture/blueprints/new_diffs/index.md b/doc/architecture/blueprints/new_diffs/index.md index 2aabfe4c618..836bb8e89e6 100644 --- a/doc/architecture/blueprints/new_diffs/index.md +++ b/doc/architecture/blueprints/new_diffs/index.md @@ -289,7 +289,7 @@ Each file table should include a short summary of changes that will read out: The summary of the table content can be placed either within `<caption>` element, or before the table within an element referred as `aria-describedby`. See <abbr>WAI</abbr> (Web Accessibility Initiative) for more information on both approaches: -- [Nesting summary inside the <caption> element](https://www.w3.org/WAI/tutorials/tables/caption-summary/#nesting-summary-inside-the-caption-element) +- [Nesting summary inside the `<caption>` element](https://www.w3.org/WAI/tutorials/tables/caption-summary/#nesting-summary-inside-the-caption-element) - [Using aria-describedby to provide a table summary](https://www.w3.org/WAI/tutorials/tables/caption-summary/#using-aria-describedby-to-provide-a-table-summary) However, if such a structure will compromise other functional aspects of displaying a diff, diff --git a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md index 1820cf77841..afbeff412a9 100644 --- a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md +++ b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md @@ -15,9 +15,8 @@ GitLab CI/CD can be used with Bitbucket Cloud by: To use GitLab CI/CD with a Bitbucket Cloud repository: 1. In GitLab, create a project: - 1. On the left sidebar, select **Search or go to**. - 1. Select **View all my projects**. - 1. On the right of the page, select **New project**. + + 1. On the left sidebar, at the top, select **Create new** (**{plus}**) and **New project/repository**. 1. Select **Run CI/CD for external repository**. 1. Select **Repository by URL**. 1. Fill in the fields with information from the repository in Bitbucket: diff --git a/doc/ci/ci_cd_for_external_repos/github_integration.md b/doc/ci/ci_cd_for_external_repos/github_integration.md index bc61990fcd8..0cb5ab68efd 100644 --- a/doc/ci/ci_cd_for_external_repos/github_integration.md +++ b/doc/ci/ci_cd_for_external_repos/github_integration.md @@ -34,9 +34,7 @@ repositories: `repo` and `admin:repo_hook` so that GitLab can access your project, update commit statuses, and create a web hook to notify GitLab of new commits. 1. In GitLab, create a project: - 1. On the left sidebar, select **Search or go to**. - 1. Select **View all my projects**. - 1. On the right of the page, select **New project**. + 1. On the left sidebar, at the top, select **Create new** (**{plus}**) and **New project/repository**. 1. Select **Run CI/CD for external repository**. 1. Select **GitHub**. 1. For **Personal access token**, paste the token. @@ -63,9 +61,7 @@ To manually enable GitLab CI/CD for your repository: 1. Enter a **Token description** and update the scope to allow `repo` so that GitLab can access your project and update commit statuses. 1. In GitLab, create a project: - 1. On the left sidebar, select **Search or go to**. - 1. Select **View all my projects**. - 1. Select **New project**. + 1. On the left sidebar, at the top, select **Create new** (**{plus}**) and **New project/repository**. 1. Select **Run CI/CD for external repository** and **Repository by URL**. 1. In the **Git repository URL** field, enter the HTTPS URL for your GitHub repository. If your project is private, use the personal access token you just created for authentication. diff --git a/doc/ci/ci_cd_for_external_repos/index.md b/doc/ci/ci_cd_for_external_repos/index.md index 4ba87c8e6a1..3460b014ec6 100644 --- a/doc/ci/ci_cd_for_external_repos/index.md +++ b/doc/ci/ci_cd_for_external_repos/index.md @@ -24,9 +24,7 @@ snippets disabled. These features To connect to an external repository: -1. On the left sidebar, select **Search or go to**. -1. Select **View all my projects**. -1. Select **New project**. +1. On the left sidebar, at the top, select **Create new** (**{plus}**) and **New project/repository**. 1. Select **Run CI/CD for external repository**. 1. Select **GitHub** or **Repository by URL**. 1. Complete the fields. diff --git a/doc/ci/cloud_deployment/ecs/deploy_to_aws_ecs.md b/doc/ci/cloud_deployment/ecs/deploy_to_aws_ecs.md index 4ccf8e5d276..6aee673e382 100644 --- a/doc/ci/cloud_deployment/ecs/deploy_to_aws_ecs.md +++ b/doc/ci/cloud_deployment/ecs/deploy_to_aws_ecs.md @@ -39,18 +39,12 @@ For the first step here, you create a demo application from a project template. Use a GitLab project template to get started. As the name suggests, these projects provide a bare-bones application built on some well-known frameworks. -1. In GitLab, select the plus icon (**{plus-square}**) at the top of the navigation bar, and select - **New project**. - +1. In GitLab on the left sidebar, at the top, select **Create new** (**{plus}**) and **New project/repository**. 1. Select **Create from template**, where you can choose from a Ruby on Rails, Spring, or NodeJS Express project. For this guide, use the Ruby on Rails template. - - ![Select project template](img/rails-template.png) - 1. Give your project a name. In this example, it's named `ecs-demo`. Make it public so that you can take advantage of the features available in the [GitLab Ultimate plan](https://about.gitlab.com/pricing/). - 1. Select **Create project**. Now that you created a demo project, you must containerize the application and push it to the @@ -63,8 +57,8 @@ provide a containerized application image during the infrastructure build. To do GitLab [Auto Build](../../../topics/autodevops/stages.md#auto-build) and [Container Registry](../../../user/packages/container_registry/index.md). -1. Go to **ecs-demo** project on GitLab. -1. Select **Setup up CI/CD**. It brings you to a `.gitlab-ci.yml` +1. On the left sidebar, select **Search or go to** and find your `ecs-demo` project. +1. Select **Set up CI/CD**. It brings you to a `.gitlab-ci.yml` creation form. 1. Copy and paste the following content into the empty `.gitlab-ci.yml`. This defines a pipeline for continuous deployment to ECS. @@ -184,7 +178,7 @@ Now, the demo application is accessible from the internet. In this guide, HTTPS/SSL is **not** configured. You can access to the application through HTTP only (for example, `http://<ec2-ipv4-address>`). -## Setup Continuous Deployment from GitLab +## Set up Continuous Deployment from GitLab Now that you have an application running on ECS, you can set up continuous deployment from GitLab. @@ -213,7 +207,7 @@ Do not share the secret access key in a public place. You must save it in a secu You can register the access information in [GitLab CI/CD Variables](../../variables/index.md). These variables are injected into the pipeline jobs and can access the ECS API. -1. Go to **ecs-demo** project on GitLab. +1. On the left sidebar, select **Search or go to** and find your `ecs-demo` project. 1. Go to **Settings > CI/CD > Variables**. 1. Select **Add Variable** and set the following key-value pairs. @@ -230,8 +224,8 @@ These variables are injected into the pipeline jobs and can access the ECS API. Change a file in the project and see if it's reflected in the demo application on ECS: -1. Go to **ecs-demo** project on GitLab. -1. Open the file at **app > views > welcome > `index.html.erb`**. +1. On the left sidebar, select **Search or go to** and find your `ecs-demo` project. +1. Open the `app/views/welcome/index.html.erb` file. 1. Select **Edit**. 1. Change the text to `You're on ECS!`. 1. Select **Commit Changes**. This automatically triggers a new pipeline. Wait until it finishes. diff --git a/doc/ci/cloud_deployment/ecs/img/rails-template.png b/doc/ci/cloud_deployment/ecs/img/rails-template.png Binary files differdeleted file mode 100644 index 02c67f8dd21..00000000000 --- a/doc/ci/cloud_deployment/ecs/img/rails-template.png +++ /dev/null diff --git a/doc/ci/components/index.md b/doc/ci/components/index.md index b6975280915..3b26d8ffe2c 100644 --- a/doc/ci/components/index.md +++ b/doc/ci/components/index.md @@ -313,13 +313,13 @@ ensure-job-added: exit 1 fi -# If we are tagging a release with a specific convention ("v" + number) and all -# previous checks succeeded, we proceed with creating a release automatically. +# If we are tagging a release with a semantic version and all previous checks succeeded, +# we proceed with creating a release automatically. create-release: stage: release image: registry.gitlab.com/gitlab-org/release-cli:latest rules: - - if: $CI_COMMIT_TAG =~ /^v\d+/ + - if: $CI_COMMIT_TAG =~ /\d+/ script: echo "Creating release $CI_COMMIT_TAG" release: tag_name: $CI_COMMIT_TAG diff --git a/doc/ci/examples/index.md b/doc/ci/examples/index.md index ea3cb9fa1f7..93ac30c0171 100644 --- a/doc/ci/examples/index.md +++ b/doc/ci/examples/index.md @@ -150,7 +150,7 @@ For examples of others who have implemented GitLab CI/CD, see: - [Test all the things in GitLab CI with Docker by example](https://about.gitlab.com/blog/2018/02/05/test-all-the-things-gitlab-ci-docker-examples/) - [A Craftsman looks at continuous integration](https://about.gitlab.com/blog/2018/01/17/craftsman-looks-at-continuous-integration/) - [Go tools and GitLab: How to do continuous integration like a boss](https://about.gitlab.com/blog/2017/11/27/go-tools-and-gitlab-how-to-do-continuous-integration-like-a-boss/) -- [GitBot – automating boring Git operations with CI](https://about.gitlab.com/blog/2017/11/02/automating-boring-git-operations-gitlab-ci/) +- [GitBot - automating boring Git operations with CI](https://about.gitlab.com/blog/2017/11/02/automating-boring-git-operations-gitlab-ci/) - [How to use GitLab CI for Vue.js](https://about.gitlab.com/blog/2017/09/12/vuejs-app-gitlab/) - Video: [GitLab CI/CD Deep Dive](https://youtu.be/pBe4t1CD8Fc?t=195) - [Dockerizing GitLab Review Apps](https://about.gitlab.com/blog/2017/07/11/dockerizing-review-apps/) diff --git a/doc/ci/pipeline_editor/index.md b/doc/ci/pipeline_editor/index.md index 1dc6bc05e71..c7bd402bd23 100644 --- a/doc/ci/pipeline_editor/index.md +++ b/doc/ci/pipeline_editor/index.md @@ -45,7 +45,11 @@ The **Lint** tab is replaced with the **Validate** tab in GitLab 15.3. The lint in a successful [pipeline simulation](#simulate-a-cicd-pipeline). To test the validity of your GitLab CI/CD configuration before committing the changes, -you can use the CI lint tool. To access it, go to **Build > Pipeline editor** and select the **Lint** tab. +you can use the CI lint tool: + +1. On the left sidebar, select **Search or go to** and find your project. +1. Select **Build > Pipeline editor**. +1. Select the **Lint** tab. This tool checks for syntax and logical errors but goes into more detail than the automatic [validation](#validate-ci-configuration) in the editor. diff --git a/doc/ci/pipelines/downstream_pipelines.md b/doc/ci/pipelines/downstream_pipelines.md index 21ed66ef939..ec1b6a4dba3 100644 --- a/doc/ci/pipelines/downstream_pipelines.md +++ b/doc/ci/pipelines/downstream_pipelines.md @@ -671,9 +671,7 @@ the ones defined in the upstream project take precedence. ### Pass dotenv variables created in a job **(PREMIUM ALL)** -You can pass variables to a downstream job with [`dotenv` variable inheritance](../variables/index.md#pass-an-environment-variable-to-another-job) -and [`needs:project`](../yaml/index.md#needsproject). These variables are only available in -the script of the job and can't be used to configure it, for example with `rules` or `artifact:paths`. +You can pass variables to a downstream pipeline with [`dotenv` variable inheritance](../variables/index.md#pass-an-environment-variable-to-another-job). For example, in a [multi-project pipeline](#multi-project-pipelines): diff --git a/doc/ci/testing/browser_performance_testing.md b/doc/ci/testing/browser_performance_testing.md index 23e8cf80d74..28d3c97166f 100644 --- a/doc/ci/testing/browser_performance_testing.md +++ b/doc/ci/testing/browser_performance_testing.md @@ -135,11 +135,11 @@ be extended for dynamic environments, but a few extra steps are required: 1. The `browser_performance` job should run after the dynamic environment has started. 1. In the `review` job: - 1. Generate a URL list file with the dynamic URL. - 1. Save the file as an artifact, for example with `echo $CI_ENVIRONMENT_URL > environment_url.txt` - in your job's `script`. - 1. Pass the list as the URL environment variable (which can be a URL or a file containing URLs) - to the `browser_performance` job. + 1. Generate a URL list file with the dynamic URL. + 1. Save the file as an artifact, for example with `echo $CI_ENVIRONMENT_URL > environment_url.txt` + in your job's `script`. + 1. Pass the list as the URL environment variable (which can be a URL or a file containing URLs) + to the `browser_performance` job. 1. You can now run the sitespeed.io container against the desired hostname and paths. diff --git a/doc/ci/testing/code_quality.md b/doc/ci/testing/code_quality.md index 1819361ea86..fd42496902a 100644 --- a/doc/ci/testing/code_quality.md +++ b/doc/ci/testing/code_quality.md @@ -520,15 +520,15 @@ Code Quality functionality can be extended by using Code Climate For example, to use the [SonarJava analyzer](https://docs.codeclimate.com/docs/sonar-java): 1. Add a file named `.codeclimate.yml` to the root of your repository -1. Add to the `.codeclimate.yml` the [enablement code](https://docs.codeclimate.com/docs/sonar-java#enable-the-plugin) - for the plugin to the root of your repository: +1. Add the [enablement code](https://docs.codeclimate.com/docs/sonar-java#enable-the-plugin) + for the plugin to the root of your repository to the `.codeclimate.yml` file: - ```yaml - version: "2" - plugins: - sonar-java: - enabled: true - ``` + ```yaml + version: "2" + plugins: + sonar-java: + enabled: true + ``` This adds SonarJava to the `plugins:` section of the [default `.codeclimate.yml`](https://gitlab.com/gitlab-org/ci-cd/codequality/-/blob/master/codeclimate_defaults/.codeclimate.yml.template) diff --git a/doc/development/documentation/styleguide/word_list.md b/doc/development/documentation/styleguide/word_list.md index ec7c3dc270c..a8f7759741d 100644 --- a/doc/development/documentation/styleguide/word_list.md +++ b/doc/development/documentation/styleguide/word_list.md @@ -294,9 +294,18 @@ Use **check out** as a verb. For the Git command, use `checkout`. - Use `git checkout` to check out a branch locally. - Check out the files you want to edit. +## CI, CD + +When talking about GitLab features, use **CI/CD**. Do not use **CI** or **CD** alone. + ## CI/CD -CI/CD is always uppercase. No need to spell it out on first use. +**CI/CD** is always uppercase. No need to spell it out on first use. + +You can omit **CI/CD** when the context is clear, especially after the first use. For example: + +- Test your code in a **CI/CD pipeline**. Configure the **pipeline** to run for merge requests. +- Store the value in a **CI/CD variable**. Set the **variable** to masked. ## CI/CD minutes @@ -651,6 +660,12 @@ of the fields at once. For example: Learn more about [documenting multiple fields at once](index.md#documenting-multiple-fields-at-once). +## file name + +Use two words for **file name**. + +([Vale](../testing.md#vale) rule: [`SubstitutionWarning.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/SubstitutionWarning.yml)) + ## filter When you are viewing a list of items, like issues or merge requests, you filter the list by diff --git a/doc/gitlab-basics/start-using-git.md b/doc/gitlab-basics/start-using-git.md index 588c9125485..8a222087914 100644 --- a/doc/gitlab-basics/start-using-git.md +++ b/doc/gitlab-basics/start-using-git.md @@ -62,13 +62,13 @@ If your computer doesn't recognize `git` as a command, you must [install Git](.. ## Configure Git To start using Git from your computer, you must enter your credentials -to identify yourself as the author of your work. The username and email address +to identify yourself as the author of your work. The full name and email address should match the ones you use in GitLab. -1. In your shell, add your user name: +1. In your shell, add your full name: ```shell - git config --global user.name "your_username" + git config --global user.name "John Doe" ``` 1. Add your email address: diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md index e29b33469ab..5539165d285 100644 --- a/doc/user/group/saml_sso/scim_setup.md +++ b/doc/user/group/saml_sso/scim_setup.md @@ -19,6 +19,8 @@ When SCIM is enabled for a GitLab group, membership of that group is synchronize The [internal GitLab group SCIM API](../../../development/internal_api/index.md#group-scim-api) implements part of [the RFC7644 protocol](https://www.rfc-editor.org/rfc/rfc7644). Identity providers can use the [internal GitLab group SCIM API](../../../development/internal_api/index.md#group-scim-api) to develop a SCIM app. +To set up SCIM on GitLab self-managed, see [Configure SCIM for self-managed GitLab instances](../../../administration/settings/scim_setup.md). + ## Configure GitLab Prerequisites: diff --git a/lib/gitlab/ci/reports/sbom/source.rb b/lib/gitlab/ci/reports/sbom/source.rb index b7af6ea17c3..7d284b5babf 100644 --- a/lib/gitlab/ci/reports/sbom/source.rb +++ b/lib/gitlab/ci/reports/sbom/source.rb @@ -5,28 +5,14 @@ module Gitlab module Reports module Sbom class Source + include SourceHelper + attr_reader :source_type, :data def initialize(type:, data:) @source_type = type @data = data end - - def source_file_path - data.dig('source_file', 'path') - end - - def input_file_path - data.dig('input_file', 'path') - end - - def packager - data.dig('package_manager', 'name') - end - - def language - data.dig('language', 'name') - end end end end diff --git a/lib/gitlab/ci/reports/sbom/source_helper.rb b/lib/gitlab/ci/reports/sbom/source_helper.rb new file mode 100644 index 00000000000..49b606f658b --- /dev/null +++ b/lib/gitlab/ci/reports/sbom/source_helper.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Gitlab + module Ci + module Reports + module Sbom + module SourceHelper + def source_file_path + data.dig('source_file', 'path') + end + + def input_file_path + data.dig('input_file', 'path') + end + + def packager + data.dig('package_manager', 'name') + end + + def language + data.dig('language', 'name') + end + + def image_name + data.dig('image', 'name') + end + + def image_tag + data.dig('image', 'tag') + end + + def operating_system_name + data.dig('operating_system', 'name') + end + + def operating_system_version + data.dig('operating_system', 'version') + end + end + end + end + end +end diff --git a/lib/gitlab/ci/variables/downstream/generator.rb b/lib/gitlab/ci/variables/downstream/generator.rb index 350d29958cf..b0e45ac0413 100644 --- a/lib/gitlab/ci/variables/downstream/generator.rb +++ b/lib/gitlab/ci/variables/downstream/generator.rb @@ -33,6 +33,7 @@ module Gitlab # The order of this list refers to the priority of the variables # The variables added later takes priority. downstream_yaml_variables + + downstream_pipeline_dotenv_variables + downstream_pipeline_variables + downstream_pipeline_schedule_variables end @@ -57,6 +58,13 @@ module Gitlab build_downstream_variables_from(pipeline_schedule_variables) end + def downstream_pipeline_dotenv_variables + return [] unless bridge.forward_pipeline_variables? + + pipeline_dotenv_variables = bridge.dependency_variables.to_a + build_downstream_variables_from(pipeline_dotenv_variables) + end + def build_downstream_variables_from(variables) Gitlab::Ci::Variables::Collection.fabricate(variables).flat_map do |item| if item.raw? diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb index 37f593ed551..8cbd1a4ce72 100644 --- a/lib/gitlab/git.rb +++ b/lib/gitlab/git.rb @@ -12,6 +12,13 @@ module Gitlab TAG_REF_PREFIX = "refs/tags/" BRANCH_REF_PREFIX = "refs/heads/" + # NOTE: We don't use linguist anymore, but we'd still want to support it + # to be backward/GitHub compatible. Using `gitlab-*` prefixed overrides + # going forward would give us a better control and flexibility. + ATTRIBUTE_OVERRIDES = { + generated: %w[gitlab-generated linguist-generated] + }.freeze + CommandError = Class.new(BaseError) CommitError = Class.new(BaseError) OSError = Class.new(BaseError) diff --git a/lib/gitlab/git/compare.rb b/lib/gitlab/git/compare.rb index ab5245ba7cb..c6d678c9432 100644 --- a/lib/gitlab/git/compare.rb +++ b/lib/gitlab/git/compare.rb @@ -42,6 +42,16 @@ module Gitlab options[:straight] = @straight Gitlab::Git::Diff.between(@repository, @head.id, @base.id, options, *paths) end + + def generated_files + return Set.new unless @base && @head + + changed_paths = @repository + .find_changed_paths([Gitlab::Git::DiffTree.new(@base.id, @head.id)]) + .map(&:path) + + @repository.detect_generated_files(@base.id, changed_paths) + end end end end diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 5b41461c918..848705028ca 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -1207,6 +1207,7 @@ module Gitlab gitaly_repository_client .get_file_attributes(revision, file_paths, attributes) .attribute_infos + .map(&:to_h) end end @@ -1216,6 +1217,16 @@ module Gitlab end end + # rubocop: disable CodeReuse/ActiveRecord -- not an active record operation + def detect_generated_files(revision, paths) + return Set.new if paths.blank? + + get_file_attributes(revision, paths, Gitlab::Git::ATTRIBUTE_OVERRIDES[:generated]) + .pluck(:path) + .to_set + end + # rubocop: enable CodeReuse/ActiveRecord + private def repository_info_size_megabytes diff --git a/locale/gitlab.pot b/locale/gitlab.pot index dbdb65531de..d1dcf5af2ce 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -6690,6 +6690,9 @@ msgstr "" msgid "AsanaService|User Personal Access Token. User must have access to the task. All comments are attributed to this user." msgstr "" +msgid "Ask GitLab Duo" +msgstr "" + msgid "Ask a maintainer to check the import status for more details." msgstr "" @@ -47769,14 +47772,23 @@ msgstr "" msgid "TanukiBot|Give feedback" msgstr "" +msgid "TanukiBot|How to use GitLab" +msgstr "" + msgid "TanukiBot|Source" msgid_plural "TanukiBot|Sources" msgstr[0] "" msgstr[1] "" +msgid "TanukiBot|The issue, epic, or code you're viewing" +msgstr "" + msgid "TanukiBot|There was an error communicating with GitLab Duo Chat. Please try again later." msgstr "" +msgid "TanukiBot|Use AI to answer questions about things like:" +msgstr "" + msgid "TanukiBot|What is a fork?" msgstr "" diff --git a/rubocop/cop/background_migration/dictionary_file.rb b/rubocop/cop/background_migration/dictionary_file.rb index 9ae47260e79..90a42135c41 100644 --- a/rubocop/cop/background_migration/dictionary_file.rb +++ b/rubocop/cop/background_migration/dictionary_file.rb @@ -3,6 +3,8 @@ require_relative '../../migration_helpers' require_relative '../../batched_background_migrations_dictionary' +URL_PATTERN = %r{\Ahttps://gitlab\.com/gitlab-org/gitlab/-/merge_requests/\d+\z} + module RuboCop module Cop module BackgroundMigration @@ -11,6 +13,7 @@ module RuboCop include MigrationHelpers MSG = { + invalid_url: "Invalid `%{key}` url for the dictionary. Please use the following format: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/XXX", missing_key: "Mandatory key '%{key}' is missing from the dictionary. Please add with an appropriate value.", missing_dictionary: <<-MESSAGE.delete("\n").squeeze(' ').strip Missing %{file_name}. @@ -49,6 +52,10 @@ module RuboCop private + def valid_url?(url) + url.match?(URL_PATTERN) + end + def dictionary_file?(migration_class_name) File.exist?(dictionary_file_path(migration_class_name)) end @@ -67,6 +74,8 @@ module RuboCop return [:missing_key, { key: :finalize_after }] unless bbm_dictionary.finalize_after.present? return [:missing_key, { key: :introduced_by_url }] unless bbm_dictionary.introduced_by_url.present? + + return [:invalid_url, { key: :introduced_by_url }] unless valid_url?(bbm_dictionary.introduced_by_url) end def rails_root diff --git a/spec/factories/ci/reports/sbom/sources.rb b/spec/factories/ci/reports/sbom/sources.rb index 688c0250b5f..a82dac1d7e2 100644 --- a/spec/factories/ci/reports/sbom/sources.rb +++ b/spec/factories/ci/reports/sbom/sources.rb @@ -2,21 +2,50 @@ FactoryBot.define do factory :ci_reports_sbom_source, class: '::Gitlab::Ci::Reports::Sbom::Source' do - type { :dependency_scanning } + dependency_scanning - transient do - sequence(:input_file_path) { |n| "subproject-#{n}/package-lock.json" } - sequence(:source_file_path) { |n| "subproject-#{n}/package.json" } + trait :dependency_scanning do + type { :dependency_scanning } + + transient do + sequence(:input_file_path) { |n| "subproject-#{n}/package-lock.json" } + sequence(:source_file_path) { |n| "subproject-#{n}/package.json" } + end + + data do + { + 'category' => 'development', + 'input_file' => { 'path' => input_file_path }, + 'source_file' => { 'path' => source_file_path }, + 'package_manager' => { 'name' => 'npm' }, + 'language' => { 'name' => 'JavaScript' } + } + end end - data do - { - 'category' => 'development', - 'input_file' => { 'path' => input_file_path }, - 'source_file' => { 'path' => source_file_path }, - 'package_manager' => { 'name' => 'npm' }, - 'language' => { 'name' => 'JavaScript' } - } + trait :container_scanning do + type { :container_scanning } + + transient do + image_name { 'photon' } + sequence(:image_tag) { |n| "5.#{n}-12345678" } + operating_system_name { 'Photon OS' } + sequence(:operating_system_version) { |n| "5.#{n}" } + end + + data do + { + 'category' => 'development', + 'image' => { + 'name' => image_name, + 'tag' => image_tag + }, + 'operating_system' => { + 'name' => operating_system_name, + 'version' => operating_system_version + } + } + end end skip_create diff --git a/spec/features/projects/settings/webhooks_settings_spec.rb b/spec/features/projects/settings/webhooks_settings_spec.rb index af7c790c692..506f7666286 100644 --- a/spec/features/projects/settings/webhooks_settings_spec.rb +++ b/spec/features/projects/settings/webhooks_settings_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Projects > Settings > Webhook Settings', feature_category: :groups_and_projects do +RSpec.describe 'Projects > Settings > Webhook Settings', feature_category: :webhooks do let(:project) { create(:project) } let(:user) { create(:user) } let(:webhooks_path) { project_hooks_path(project) } diff --git a/spec/frontend/content_editor/extensions/reference_spec.js b/spec/frontend/content_editor/extensions/reference_spec.js index c25c7c41d75..d4b07d5127e 100644 --- a/spec/frontend/content_editor/extensions/reference_spec.js +++ b/spec/frontend/content_editor/extensions/reference_spec.js @@ -1,9 +1,15 @@ import Reference from '~/content_editor/extensions/reference'; +import ReferenceLabel from '~/content_editor/extensions/reference_label'; import AssetResolver from '~/content_editor/services/asset_resolver'; import { RESOLVED_ISSUE_HTML, RESOLVED_MERGE_REQUEST_HTML, RESOLVED_EPIC_HTML, + RESOLVED_LABEL_HTML, + RESOLVED_SNIPPET_HTML, + RESOLVED_MILESTONE_HTML, + RESOLVED_USER_HTML, + RESOLVED_VULNERABILITY_HTML, } from '../test_constants'; import { createTestEditor, @@ -17,6 +23,7 @@ describe('content_editor/extensions/reference', () => { let doc; let p; let reference; + let referenceLabel; let renderMarkdown; let assetResolver; @@ -25,33 +32,54 @@ describe('content_editor/extensions/reference', () => { assetResolver = new AssetResolver({ renderMarkdown }); tiptapEditor = createTestEditor({ - extensions: [Reference.configure({ assetResolver })], + extensions: [Reference.configure({ assetResolver }), ReferenceLabel], }); ({ - builders: { doc, p, reference }, + builders: { doc, p, reference, referenceLabel }, } = createDocBuilder({ tiptapEditor, names: { reference: { nodeType: Reference.name }, + referenceLabel: { nodeType: ReferenceLabel.name }, }, })); }); describe('when typing a valid reference input rule', () => { - const buildExpectedDoc = (href, originalText, referenceType, text) => + const buildExpectedDoc = (href, originalText, referenceType, text = originalText) => doc(p(reference({ className: null, href, originalText, referenceType, text }), ' ')); + const buildExpectedDocForLabel = (href, originalText, text, color) => + doc( + p( + referenceLabel({ + className: null, + referenceType: 'label', + href, + originalText, + text, + color, + }), + ' ', + ), + ); + it.each` - inputRuleText | mockReferenceHtml | expectedDoc - ${'#1 '} | ${RESOLVED_ISSUE_HTML} | ${() => buildExpectedDoc('/gitlab-org/gitlab/-/issues/1', '#1', 'issue', '#1 (closed)')} - ${'#1+ '} | ${RESOLVED_ISSUE_HTML} | ${() => buildExpectedDoc('/gitlab-org/gitlab/-/issues/1', '#1+', 'issue', '500 error on MR approvers edit page (#1 - closed)')} - ${'#1+s '} | ${RESOLVED_ISSUE_HTML} | ${() => buildExpectedDoc('/gitlab-org/gitlab/-/issues/1', '#1+s', 'issue', '500 error on MR approvers edit page (#1 - closed) • Unassigned')} - ${'!1 '} | ${RESOLVED_MERGE_REQUEST_HTML} | ${() => buildExpectedDoc('/gitlab-org/gitlab/-/merge_requests/1', '!1', 'merge_request', '!1 (merged)')} - ${'!1+ '} | ${RESOLVED_MERGE_REQUEST_HTML} | ${() => buildExpectedDoc('/gitlab-org/gitlab/-/merge_requests/1', '!1+', 'merge_request', 'Enhance the LDAP group synchronization (!1 - merged)')} - ${'!1+s '} | ${RESOLVED_MERGE_REQUEST_HTML} | ${() => buildExpectedDoc('/gitlab-org/gitlab/-/merge_requests/1', '!1+s', 'merge_request', 'Enhance the LDAP group synchronization (!1 - merged) • John Doe')} - ${'&1 '} | ${RESOLVED_EPIC_HTML} | ${() => buildExpectedDoc('/groups/gitlab-org/-/epics/1', '&1', 'epic', '&1')} - ${'&1+ '} | ${RESOLVED_EPIC_HTML} | ${() => buildExpectedDoc('/groups/gitlab-org/-/epics/1', '&1+', 'epic', 'Approvals in merge request list (&1)')} + inputRuleText | mockReferenceHtml | expectedDoc + ${'#1'} | ${RESOLVED_ISSUE_HTML} | ${() => buildExpectedDoc('/gitlab-org/gitlab/-/issues/1', '#1', 'issue', '#1 (closed)')} + ${'#1+'} | ${RESOLVED_ISSUE_HTML} | ${() => buildExpectedDoc('/gitlab-org/gitlab/-/issues/1', '#1+', 'issue', '500 error on MR approvers edit page (#1 - closed)')} + ${'#1+s'} | ${RESOLVED_ISSUE_HTML} | ${() => buildExpectedDoc('/gitlab-org/gitlab/-/issues/1', '#1+s', 'issue', '500 error on MR approvers edit page (#1 - closed) • Unassigned')} + ${'!1'} | ${RESOLVED_MERGE_REQUEST_HTML} | ${() => buildExpectedDoc('/gitlab-org/gitlab/-/merge_requests/1', '!1', 'merge_request', '!1 (merged)')} + ${'!1+'} | ${RESOLVED_MERGE_REQUEST_HTML} | ${() => buildExpectedDoc('/gitlab-org/gitlab/-/merge_requests/1', '!1+', 'merge_request', 'Enhance the LDAP group synchronization (!1 - merged)')} + ${'!1+s'} | ${RESOLVED_MERGE_REQUEST_HTML} | ${() => buildExpectedDoc('/gitlab-org/gitlab/-/merge_requests/1', '!1+s', 'merge_request', 'Enhance the LDAP group synchronization (!1 - merged) • John Doe')} + ${'&1'} | ${RESOLVED_EPIC_HTML} | ${() => buildExpectedDoc('/groups/gitlab-org/-/epics/1', '&1', 'epic', '&1')} + ${'&1+'} | ${RESOLVED_EPIC_HTML} | ${() => buildExpectedDoc('/groups/gitlab-org/-/epics/1', '&1+', 'epic', 'Approvals in merge request list (&1)')} + ${'@root'} | ${RESOLVED_USER_HTML} | ${() => buildExpectedDoc('/root', '@root', 'user')} + ${'~Aquanix'} | ${RESOLVED_LABEL_HTML} | ${() => buildExpectedDocForLabel('/gitlab-org/gitlab-shell/-/issues?label_name=Aquanix', '~Aquanix', 'Aquanix', 'rgb(230, 84, 49)')} + ${'%v4.0'} | ${RESOLVED_MILESTONE_HTML} | ${() => buildExpectedDoc('/gitlab-org/gitlab-shell/-/milestones/5', '%v4.0', 'milestone')} + ${'$25'} | ${RESOLVED_SNIPPET_HTML} | ${() => buildExpectedDoc('/gitlab-org/gitlab-shell/-/snippets/25', '$25', 'snippet')} + ${'[vulnerability:1]'} | ${RESOLVED_VULNERABILITY_HTML} | ${() => buildExpectedDoc('/gitlab-org/gitlab-shell/-/security/vulnerabilities/1', '[vulnerability:1]', 'vulnerability')} `( 'replaces the input rule ($inputRuleText) with a reference node', async ({ inputRuleText, mockReferenceHtml, expectedDoc }) => { @@ -61,8 +89,8 @@ describe('content_editor/extensions/reference', () => { action() { renderMarkdown.mockResolvedValueOnce(mockReferenceHtml); - tiptapEditor.commands.insertContent({ type: 'text', text: inputRuleText }); - triggerNodeInputRule({ tiptapEditor, inputRuleText }); + tiptapEditor.commands.insertContent({ type: 'text', text: `${inputRuleText} ` }); + triggerNodeInputRule({ tiptapEditor, inputRuleText: `${inputRuleText} ` }); }, }); diff --git a/spec/frontend/content_editor/services/asset_resolver_spec.js b/spec/frontend/content_editor/services/asset_resolver_spec.js index 292eec6db77..b0135a6bc9f 100644 --- a/spec/frontend/content_editor/services/asset_resolver_spec.js +++ b/spec/frontend/content_editor/services/asset_resolver_spec.js @@ -3,6 +3,11 @@ import { RESOLVED_ISSUE_HTML, RESOLVED_MERGE_REQUEST_HTML, RESOLVED_EPIC_HTML, + RESOLVED_LABEL_HTML, + RESOLVED_SNIPPET_HTML, + RESOLVED_MILESTONE_HTML, + RESOLVED_USER_HTML, + RESOLVED_VULNERABILITY_HTML, } from '../test_constants'; describe('content_editor/services/asset_resolver', () => { @@ -48,6 +53,32 @@ describe('content_editor/services/asset_resolver', () => { text: '!1 (merged)', }; + const resolvedLabel = { + backgroundColor: 'rgb(230, 84, 49)', + href: '/gitlab-org/gitlab-shell/-/issues?label_name=Aquanix', + text: 'Aquanix', + }; + + const resolvedSnippet = { + href: '/gitlab-org/gitlab-shell/-/snippets/25', + text: '$25', + }; + + const resolvedMilestone = { + href: '/gitlab-org/gitlab-shell/-/milestones/5', + text: '%v4.0', + }; + + const resolvedUser = { + href: '/root', + text: '@root', + }; + + const resolvedVulnerability = { + href: '/gitlab-org/gitlab-shell/-/security/vulnerabilities/1', + text: '[vulnerability:1]', + }; + describe.each` referenceType | referenceId | sentMarkdown | returnedHtml | resolvedReference ${'issue'} | ${'#1'} | ${'#1 #1+ #1+s'} | ${RESOLVED_ISSUE_HTML} | ${resolvedIssue} @@ -59,7 +90,9 @@ describe('content_editor/services/asset_resolver', () => { it(`resolves ${referenceType} reference to href, text, title and summary`, async () => { renderMarkdown.mockResolvedValue(returnedHtml); - expect(await assetResolver.resolveReference(referenceId)).toEqual(resolvedReference); + expect(await assetResolver.resolveReference(referenceId)).toMatchObject( + resolvedReference, + ); }); it.each` @@ -74,6 +107,26 @@ describe('content_editor/services/asset_resolver', () => { }, ); + describe.each` + referenceType | referenceId | returnedHtml | resolvedReference + ${'label'} | ${'~Aquanix'} | ${RESOLVED_LABEL_HTML} | ${resolvedLabel} + ${'snippet'} | ${'$25'} | ${RESOLVED_SNIPPET_HTML} | ${resolvedSnippet} + ${'milestone'} | ${'%v4.0'} | ${RESOLVED_MILESTONE_HTML} | ${resolvedMilestone} + ${'user'} | ${'@root'} | ${RESOLVED_USER_HTML} | ${resolvedUser} + ${'vulnerability'} | ${'[vulnerability:1]'} | ${RESOLVED_VULNERABILITY_HTML} | ${resolvedVulnerability} + `( + 'for reference type $referenceType', + ({ referenceType, referenceId, returnedHtml, resolvedReference }) => { + it(`resolves ${referenceType} reference to href, text and additional props (if any)`, async () => { + renderMarkdown.mockResolvedValue(returnedHtml); + + expect(await assetResolver.resolveReference(referenceId)).toMatchObject( + resolvedReference, + ); + }); + }, + ); + it.each` case | sentMarkdown | returnedHtml ${'no html is returned'} | ${''} | ${''} diff --git a/spec/frontend/content_editor/test_constants.js b/spec/frontend/content_editor/test_constants.js index cbd4f555e97..255a7104eaf 100644 --- a/spec/frontend/content_editor/test_constants.js +++ b/spec/frontend/content_editor/test_constants.js @@ -44,3 +44,18 @@ export const RESOLVED_MERGE_REQUEST_HTML = export const RESOLVED_EPIC_HTML = '<p data-sourcepos="1:1-1:11" dir="auto"><a href="/groups/gitlab-org/-/epics/1" data-reference-type="epic" data-original="&amp;1" data-link="false" data-link-reference="false" data-group="9970" data-epic="1" data-container="body" data-placement="top" title="Approvals in merge request list" class="gfm gfm-epic has-tooltip">&1</a> <a href="/groups/gitlab-org/-/epics/1" data-reference-type="epic" data-original="&amp;1+" data-link="false" data-link-reference="false" data-group="9970" data-epic="1" data-reference-format="+" data-container="body" data-placement="top" title="Approvals in merge request list" class="gfm gfm-epic has-tooltip">Approvals in merge request list (&1)</a> <a href="/groups/gitlab-org/-/epics/1" data-reference-type="epic" data-original="&amp;1+s" data-link="false" data-link-reference="false" data-group="9970" data-epic="1" data-reference-format="+s" data-container="body" data-placement="top" title="Approvals in merge request list" class="gfm gfm-epic has-tooltip">Approvals in merge request list (&1)</a></p>'; + +export const RESOLVED_LABEL_HTML = + '<p data-sourcepos="1:1-1:29" dir="auto"><span class="gl-label gl-label-sm"><a href="/gitlab-org/gitlab-shell/-/issues?label_name=Aquanix" data-reference-type="label" data-original="~Aquanix" data-link="false" data-link-reference="false" data-project="2" data-label="5" data-container="body" data-placement="top" title="" class="gfm gfm-label has-tooltip gl-link gl-label-link"><span class="gl-label-text gl-label-text-light" data-container="body" data-html="true" style="background-color: #e65431">Aquanix</span></a></span> <span class="gl-label gl-label-sm"><a href="/gitlab-org/gitlab-shell/-/issues?label_name=Aquanix" data-reference-type="label" data-original="~Aquanix" data-link="false" data-link-reference="false" data-project="2" data-label="5" data-container="body" data-placement="top" title="" class="gfm gfm-label has-tooltip gl-link gl-label-link"><span class="gl-label-text gl-label-text-light" data-container="body" data-html="true" style="background-color: #e65431">Aquanix</span></a></span>+ <span class="gl-label gl-label-sm"><a href="/gitlab-org/gitlab-shell/-/issues?label_name=Aquanix" data-reference-type="label" data-original="~Aquanix" data-link="false" data-link-reference="false" data-project="2" data-label="5" data-container="body" data-placement="top" title="" class="gfm gfm-label has-tooltip gl-link gl-label-link"><span class="gl-label-text gl-label-text-light" data-container="body" data-html="true" style="background-color: #e65431">Aquanix</span></a></span>+s</p>'; + +export const RESOLVED_SNIPPET_HTML = + '<p data-sourcepos="1:1-1:14" dir="auto"><a href="/gitlab-org/gitlab-shell/-/snippets/25" data-reference-type="snippet" data-original="$25" data-link="false" data-link-reference="false" data-project="2" data-snippet="25" data-container="body" data-placement="top" title="test" class="gfm gfm-snippet has-tooltip">$25</a> <a href="/gitlab-org/gitlab-shell/-/snippets/25" data-reference-type="snippet" data-original="$25" data-link="false" data-link-reference="false" data-project="2" data-snippet="25" data-container="body" data-placement="top" title="test" class="gfm gfm-snippet has-tooltip">$25</a>+ <a href="/gitlab-org/gitlab-shell/-/snippets/25" data-reference-type="snippet" data-original="$25" data-link="false" data-link-reference="false" data-project="2" data-snippet="25" data-container="body" data-placement="top" title="test" class="gfm gfm-snippet has-tooltip">$25</a>+s</p>'; + +export const RESOLVED_MILESTONE_HTML = + '<p data-sourcepos="1:1-1:20" dir="auto"><a href="/gitlab-org/gitlab-shell/-/milestones/5" data-reference-type="milestone" data-original="%v4.0" data-link="false" data-link-reference="false" data-project="2" data-milestone="10" data-container="body" data-placement="top" title="" class="gfm gfm-milestone has-tooltip">%v4.0</a> <a href="/gitlab-org/gitlab-shell/-/milestones/5" data-reference-type="milestone" data-original="%v4.0" data-link="false" data-link-reference="false" data-project="2" data-milestone="10" data-container="body" data-placement="top" title="" class="gfm gfm-milestone has-tooltip">%v4.0</a>+ %v4.0+s</p>'; + +export const RESOLVED_USER_HTML = + '<p data-sourcepos="1:1-1:20" dir="auto"><a href="/root" data-reference-type="user" data-user="1" data-container="body" data-placement="top" class="gfm gfm-project_member js-user-link" title="Administrator">@root</a> <a href="/root" data-reference-type="user" data-user="1" data-container="body" data-placement="top" class="gfm gfm-project_member js-user-link" title="Administrator">@root</a>+ <a href="/root" data-reference-type="user" data-user="1" data-container="body" data-placement="top" class="gfm gfm-project_member js-user-link" title="Administrator">@root</a>+s</p>'; + +export const RESOLVED_VULNERABILITY_HTML = + '<p data-sourcepos="1:1-1:56" dir="auto"><a href="/gitlab-org/gitlab-shell/-/security/vulnerabilities/1" data-reference-type="vulnerability" data-original="[vulnerability:1]" data-link="false" data-link-reference="false" data-project="2" data-vulnerability="1" data-container="body" data-placement="top" title="oh no!" class="gfm gfm-vulnerability has-tooltip">[vulnerability:1]</a> <a href="/gitlab-org/gitlab-shell/-/security/vulnerabilities/1" data-reference-type="vulnerability" data-original="[vulnerability:1]" data-link="false" data-link-reference="false" data-project="2" data-vulnerability="1" data-container="body" data-placement="top" title="oh no!" class="gfm gfm-vulnerability has-tooltip">[vulnerability:1]</a>+ <a href="/gitlab-org/gitlab-shell/-/security/vulnerabilities/1" data-reference-type="vulnerability" data-original="[vulnerability:1]" data-link="false" data-link-reference="false" data-project="2" data-vulnerability="1" data-container="body" data-placement="top" title="oh no!" class="gfm gfm-vulnerability has-tooltip">[vulnerability:1]</a>+s</p>'; diff --git a/spec/lib/gitlab/ci/reports/sbom/source_spec.rb b/spec/lib/gitlab/ci/reports/sbom/source_spec.rb index c1eaea511b7..09a601833ad 100644 --- a/spec/lib/gitlab/ci/reports/sbom/source_spec.rb +++ b/spec/lib/gitlab/ci/reports/sbom/source_spec.rb @@ -5,47 +5,93 @@ require 'fast_spec_helper' RSpec.describe Gitlab::Ci::Reports::Sbom::Source, feature_category: :dependency_management do let(:attributes) do { - type: :dependency_scanning, - data: { - 'category' => 'development', - 'input_file' => { 'path' => 'package-lock.json' }, - 'source_file' => { 'path' => 'package.json' }, - 'package_manager' => { 'name' => 'npm' }, - 'language' => { 'name' => 'JavaScript' } - } + type: type, + data: { 'category' => 'development', + 'package_manager' => { 'name' => 'npm' }, + 'language' => { 'name' => 'JavaScript' } }.merge(extra_attributes) } end - subject { described_class.new(**attributes) } + subject(:source) { described_class.new(**attributes) } - it 'has correct attributes' do - expect(subject).to have_attributes( - source_type: attributes[:type], - data: attributes[:data] - ) - end + shared_examples_for 'it has correct common attributes' do + it 'has correct type and data' do + expect(subject).to have_attributes( + source_type: type, + data: attributes[:data] + ) + end - describe '#source_file_path' do - it 'returns the correct source_file_path' do - expect(subject.source_file_path).to eq('package.json') + describe '#packager' do + it 'returns the correct package manager name' do + expect(subject.packager).to eq("npm") + end end - end - describe '#input_file_path' do - it 'returns the correct input_file_path' do - expect(subject.input_file_path).to eq("package-lock.json") + describe '#language' do + it 'returns the correct language' do + expect(subject.language).to eq("JavaScript") + end end end - describe '#packager' do - it 'returns the correct package manager name' do - expect(subject.packager).to eq("npm") + context 'when dependency scanning' do + let(:type) { :dependency_scanning } + let(:extra_attributes) do + { + 'input_file' => { 'path' => 'package-lock.json' }, + 'source_file' => { 'path' => 'package.json' } + } + end + + it_behaves_like 'it has correct common attributes' + + describe '#source_file_path' do + it 'returns the correct source_file_path' do + expect(subject.source_file_path).to eq('package.json') + end + end + + describe '#input_file_path' do + it 'returns the correct input_file_path' do + expect(subject.input_file_path).to eq("package-lock.json") + end end end - describe '#language' do - it 'returns the correct langauge' do - expect(subject.language).to eq("JavaScript") + context 'when container scanning' do + let(:type) { :container_scanning } + let(:extra_attributes) do + { + "image" => { "name" => "rhel", "tag" => "7.1" }, + "operating_system" => { "name" => "Red Hat Enterprise Linux", "version" => "7" } + } + end + + it_behaves_like 'it has correct common attributes' + + describe "#image_name" do + subject { source.image_name } + + it { is_expected.to eq("rhel") } + end + + describe "#image_tag" do + subject { source.image_tag } + + it { is_expected.to eq("7.1") } + end + + describe "#operating_system_name" do + subject { source.operating_system_name } + + it { is_expected.to eq("Red Hat Enterprise Linux") } + end + + describe "#operating_system_version" do + subject { source.operating_system_version } + + it { is_expected.to eq("7") } end end end diff --git a/spec/lib/gitlab/ci/variables/downstream/generator_spec.rb b/spec/lib/gitlab/ci/variables/downstream/generator_spec.rb index cd68b0cdf2b..f5845e492bc 100644 --- a/spec/lib/gitlab/ci/variables/downstream/generator_spec.rb +++ b/spec/lib/gitlab/ci/variables/downstream/generator_spec.rb @@ -39,6 +39,15 @@ RSpec.describe Gitlab::Ci::Variables::Downstream::Generator, feature_category: : ] end + let(:pipeline_dotenv_variables) do + [ + { key: 'PIPELINE_DOTENV_VAR1', value: 'variable 1' }, + { key: 'PIPELINE_DOTENV_VAR2', value: 'variable 2' }, + { key: 'PIPELINE_DOTENV_RAW_VAR3', value: '$REF1', raw: true }, + { key: 'PIPELINE_DOTENV_INTERPOLATION_VAR4', value: 'interpolate $REF1 $REF2' } + ] + end + let(:bridge) do instance_double( 'Ci::Bridge', @@ -48,7 +57,8 @@ RSpec.describe Gitlab::Ci::Variables::Downstream::Generator, feature_category: : expand_file_refs?: false, yaml_variables: yaml_variables, pipeline_variables: pipeline_variables, - pipeline_schedule_variables: pipeline_schedule_variables + pipeline_schedule_variables: pipeline_schedule_variables, + dependency_variables: pipeline_dotenv_variables ) end @@ -69,7 +79,12 @@ RSpec.describe Gitlab::Ci::Variables::Downstream::Generator, feature_category: : { key: 'PIPELINE_SCHEDULE_VAR1', value: 'variable 1' }, { key: 'PIPELINE_SCHEDULE_VAR2', value: 'variable 2' }, { key: 'PIPELINE_SCHEDULE_RAW_VAR3', value: '$REF1', raw: true }, - { key: 'PIPELINE_SCHEDULE_INTERPOLATION_VAR4', value: 'interpolate ref 1 ref 2' } + { key: 'PIPELINE_SCHEDULE_INTERPOLATION_VAR4', value: 'interpolate ref 1 ref 2' }, + { key: 'PIPELINE_DOTENV_VAR1', value: 'variable 1' }, + { key: 'PIPELINE_DOTENV_VAR2', value: 'variable 2' }, + { key: 'PIPELINE_DOTENV_RAW_VAR3', value: '$REF1', raw: true }, + { key: 'PIPELINE_DOTENV_INTERPOLATION_VAR4', value: 'interpolate ref 1 ref 2' } + ] expect(generator.calculate).to contain_exactly(*expected) @@ -79,6 +94,7 @@ RSpec.describe Gitlab::Ci::Variables::Downstream::Generator, feature_category: : allow(bridge).to receive(:yaml_variables).and_return([]) allow(bridge).to receive(:pipeline_variables).and_return([]) allow(bridge).to receive(:pipeline_schedule_variables).and_return([]) + allow(bridge).to receive(:dependency_variables).and_return([]) expect(generator.calculate).to be_empty end @@ -105,6 +121,10 @@ RSpec.describe Gitlab::Ci::Variables::Downstream::Generator, feature_category: : [{ key: 'PIPELINE_SCHEDULE_INTERPOLATION_VAR', value: 'interpolate $REF1 $REF2 $FILE_REF3 $FILE_REF4' }] end + let(:pipeline_dotenv_variables) do + [{ key: 'PIPELINE_DOTENV_INTERPOLATION_VAR', value: 'interpolate $REF1 $REF2 $FILE_REF3 $FILE_REF4' }] + end + context 'when expand_file_refs is true' do before do allow(bridge).to receive(:expand_file_refs?).and_return(true) @@ -114,7 +134,8 @@ RSpec.describe Gitlab::Ci::Variables::Downstream::Generator, feature_category: : expected = [ { key: 'INTERPOLATION_VAR', value: 'interpolate ref 1 ref 3 ' }, { key: 'PIPELINE_INTERPOLATION_VAR', value: 'interpolate ref 1 ref 3 ' }, - { key: 'PIPELINE_SCHEDULE_INTERPOLATION_VAR', value: 'interpolate ref 1 ref 3 ' } + { key: 'PIPELINE_SCHEDULE_INTERPOLATION_VAR', value: 'interpolate ref 1 ref 3 ' }, + { key: 'PIPELINE_DOTENV_INTERPOLATION_VAR', value: 'interpolate ref 1 ref 3 ' } ] expect(generator.calculate).to contain_exactly(*expected) @@ -131,6 +152,7 @@ RSpec.describe Gitlab::Ci::Variables::Downstream::Generator, feature_category: : { key: 'INTERPOLATION_VAR', value: 'interpolate ref 1 $FILE_REF3 ' }, { key: 'PIPELINE_INTERPOLATION_VAR', value: 'interpolate ref 1 $FILE_REF3 ' }, { key: 'PIPELINE_SCHEDULE_INTERPOLATION_VAR', value: 'interpolate ref 1 $FILE_REF3 ' }, + { key: 'PIPELINE_DOTENV_INTERPOLATION_VAR', value: 'interpolate ref 1 $FILE_REF3 ' }, { key: 'FILE_REF3', value: 'ref 3', variable_type: :file } ] diff --git a/spec/lib/gitlab/git/compare_spec.rb b/spec/lib/gitlab/git/compare_spec.rb index 01079203c1f..5ee5e18d5af 100644 --- a/spec/lib/gitlab/git/compare_spec.rb +++ b/spec/lib/gitlab/git/compare_spec.rb @@ -3,10 +3,13 @@ require "spec_helper" RSpec.describe Gitlab::Git::Compare, feature_category: :source_code_management do - let_it_be(:repository) { create(:project, :repository).repository.raw } + let_it_be(:project) { create(:project, :repository) } + let_it_be(:repository) { project.repository.raw } - let(:compare) { described_class.new(repository, SeedRepo::BigCommit::ID, SeedRepo::Commit::ID, straight: false) } - let(:compare_straight) { described_class.new(repository, SeedRepo::BigCommit::ID, SeedRepo::Commit::ID, straight: true) } + let(:compare) { described_class.new(repository, base, head, straight: false) } + let(:compare_straight) { described_class.new(repository, base, head, straight: true) } + let(:base) { SeedRepo::BigCommit::ID } + let(:head) { SeedRepo::Commit::ID } describe '#commits' do subject do @@ -109,4 +112,103 @@ RSpec.describe Gitlab::Git::Compare, feature_category: :source_code_management d it { is_expected.to include('files/ruby/popen.rb') } it { is_expected.not_to include('LICENSE') } end + + describe '#generated_files' do + subject(:generated_files) { compare.generated_files } + + context 'with a detected generated file' do + let_it_be(:project) { create(:project, :repository) } + let_it_be(:repository) { project.repository.raw } + let_it_be(:branch) { 'generated-file-test' } + let_it_be(:base) do + project + .repository + .create_file( + project.creator, + '.gitattributes', + "*.txt gitlab-generated\n", + branch_name: branch, + message: 'Add .gitattributes file') + end + + let_it_be(:head) do + project + .repository + .create_file( + project.creator, + 'file1.rb', + "some content\n", + branch_name: branch, + message: 'Add file1') + project + .repository + .create_file( + project.creator, + 'file1.txt', + "some content\n", + branch_name: branch, + message: 'Add file2') + end + + it 'sets the diff as generated' do + expect(generated_files).to eq Set.new(['file1.txt']) + end + + context 'when base is nil' do + let(:base) { nil } + + it 'does not try to detect generated files' do + expect(repository).not_to receive(:detect_generated_files) + expect(repository).not_to receive(:find_changed_paths) + expect(generated_files).to eq Set.new + end + end + + context 'when head is nil' do + let(:head) { nil } + + it 'does not try to detect generated files' do + expect(repository).not_to receive(:detect_generated_files) + expect(repository).not_to receive(:find_changed_paths) + expect(generated_files).to eq Set.new + end + end + end + + context 'with updated .gitattributes in the HEAD' do + let_it_be(:project) { create(:project, :repository) } + let_it_be(:repository) { project.repository.raw } + let_it_be(:branch) { 'generated-file-test' } + let_it_be(:head) do + project + .repository + .create_file( + project.creator, + '.gitattributes', + "*.txt gitlab-generated\n", + branch_name: branch, + message: 'Add .gitattributes file') + project + .repository + .create_file( + project.creator, + 'file1.rb', + "some content\n", + branch_name: branch, + message: 'Add file1') + project + .repository + .create_file( + project.creator, + 'file1.txt', + "some content\n", + branch_name: branch, + message: 'Add file2') + end + + it 'does not set any files as generated' do + expect(generated_files).to eq Set.new + end + end + end end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 40c9bbeda77..cc07a16d362 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -2812,4 +2812,69 @@ RSpec.describe Gitlab::Git::Repository, feature_category: :source_code_managemen subject { repository.get_file_attributes(rev, paths, attrs) } end end + + describe '#detect_generated_files' do + let(:project) do + create(:project, :custom_repo, files: { + '.gitattributes' => gitattr_content, + 'file1.txt' => 'first file', + 'file2.txt' => 'second file' + }) + end + + let(:repository) { project.repository.raw } + let(:rev) { 'master' } + let(:paths) { ['file1.txt', 'file2.txt'] } + + subject(:generated_files) { repository.detect_generated_files(rev, paths) } + + context 'when the linguist-generated attribute is used' do + let(:gitattr_content) { "*.txt text\nfile1.txt linguist-generated\n" } + + it 'returns generated files only' do + expect(generated_files).to contain_exactly('file1.txt') + end + end + + context 'when the gitlab-generated attribute is used' do + let(:gitattr_content) { "*.txt text\nfile1.txt gitlab-generated\n" } + + it 'returns generated files only' do + expect(generated_files).to contain_exactly('file1.txt') + end + end + + context 'when both linguist-generated and gitlab-generated attribute are used' do + let(:gitattr_content) { "*.txt text\nfile1.txt linguist-generated gitlab-generated\n" } + + it 'returns generated files only' do + expect(generated_files).to contain_exactly('file1.txt') + end + end + + context 'when the all files are generated' do + let(:gitattr_content) { "*.txt gitlab-generated\n" } + + it 'returns all generated files' do + expect(generated_files).to eq paths.to_set + end + end + + context 'when empty paths are given' do + let(:paths) { [] } + let(:gitattr_content) { "*.txt gitlab-generated\n" } + + it 'returns an empty set' do + expect(generated_files).to eq Set.new + end + end + + context 'when no generated overrides are used' do + let(:gitattr_content) { "*.txt text\n" } + + it 'returns an empty set' do + expect(generated_files).to eq Set.new + end + end + end end diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index 726c98ed704..4ddd17c6bae 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -518,6 +518,27 @@ RSpec.describe Ability do end describe '.allowed?' do + context "when used with 'read_namespace'" do + subject(:allowed?) { described_class.allowed?(nil, 'read_namespace') } + + before do + allow(Gitlab::AppLogger).to receive(:info) + end + + it 'logs the usage', :aggregate_failures do + allowed? + + expect(Gitlab::AppLogger).to have_received(:info) do |args| + expect(args[:message]).to eq('Ability is in use') + expect(args[:ability]).to eq(:read_namespace) + expect(args[:caller_locations].first).to( + match(%r{spec/models/ability_spec.rb}) + ) + expect(args[:caller_locations].length).to eq(5) + end + end + end + context 'when used with :read_namespace' do subject(:allowed?) { described_class.allowed?(nil, :read_namespace) } @@ -532,7 +553,7 @@ RSpec.describe Ability do expect(args[:message]).to eq('Ability is in use') expect(args[:ability]).to eq(:read_namespace) expect(args[:caller_locations].first).to( - match(%r{/spec/models/ability_spec.rb:\d+:in `block \(4 levels\) in <top \(required\)>}) + match(%r{spec/models/ability_spec.rb}) ) expect(args[:caller_locations].length).to eq(5) end diff --git a/spec/models/concerns/enums/sbom_spec.rb b/spec/models/concerns/enums/sbom_spec.rb index e2f56cc637d..3bbdf619a8c 100644 --- a/spec/models/concerns/enums/sbom_spec.rb +++ b/spec/models/concerns/enums/sbom_spec.rb @@ -3,9 +3,9 @@ require "spec_helper" RSpec.describe Enums::Sbom, feature_category: :dependency_management do - describe '.purl_types' do - using RSpec::Parameterized::TableSyntax + using RSpec::Parameterized::TableSyntax + describe '.purl_types' do subject(:actual_purl_type) { described_class.purl_types[package_manager] } where(:given_package_manager, :expected_purl_type) do @@ -35,5 +35,63 @@ RSpec.describe Enums::Sbom, feature_category: :dependency_management do expect(actual_purl_type).to eql(expected_purl_type) end end + + it 'contains all of the dependency scanning and container scanning purl types' do + expect(described_class::DEPENDENCY_SCANNING_PURL_TYPES + described_class::CONTAINER_SCANNING_PURL_TYPES) + .to eql(described_class::PURL_TYPES.keys) + end + end + + describe '.dependency_scanning_purl_type?' do + where(:purl_type, :expected) do + :composer | false + 'composer' | true + 'conan' | true + 'gem' | true + 'golang' | true + 'maven' | true + 'npm' | true + 'nuget' | true + 'pypi' | true + 'unknown' | false + 'apk' | false + 'rpm' | false + 'deb' | false + 'wolfi' | false + end + + with_them do + it 'returns true if the purl_type is for dependency_scanning' do + actual = described_class.dependency_scanning_purl_type?(purl_type) + expect(actual).to eql(expected) + end + end + end + + describe '.container_scanning_purl_type?' do + where(:purl_type, :expected) do + 'composer' | false + 'conan' | false + 'gem' | false + 'golang' | false + 'maven' | false + 'npm' | false + 'nuget' | false + 'pypi' | false + 'unknown' | false + :apk | false + 'apk' | true + 'rpm' | true + 'deb' | true + 'cbl-mariner' | true + 'wolfi' | true + end + + with_them do + it 'returns true if the purl_type is for container_scanning' do + actual = described_class.container_scanning_purl_type?(purl_type) + expect(actual).to eql(expected) + end + end end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 2c70b80074b..08679626c21 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -4052,59 +4052,4 @@ RSpec.describe Repository, feature_category: :source_code_management do it { expect { file_attributes }.to raise_error(ArgumentError) } end end - - describe '#filter_generated_files' do - let(:project) do - create(:project, :custom_repo, files: { - '.gitattributes' => gitattr_content, - 'file1.txt' => 'first file', - 'file2.txt' => 'second file' - }) - end - - let(:rev) { 'master' } - let(:paths) { ['file1.txt', 'file2.txt'] } - - subject(:generated_files) { repository.filter_generated_files(rev, paths) } - - context 'when the linguist-generated attribute is used' do - let(:gitattr_content) { "*.txt text\nfile1.txt linguist-generated\n" } - - it 'returns generated files only' do - expect(generated_files).to contain_exactly('file2.txt') - end - end - - context 'when the gitlab-generated attribute is used' do - let(:gitattr_content) { "*.txt text\nfile1.txt gitlab-generated\n" } - - it 'returns generated files only' do - expect(generated_files).to contain_exactly('file2.txt') - end - end - - context 'when both linguist-generated and gitlab-generated attribute are used' do - let(:gitattr_content) { "*.txt text\nfile1.txt linguist-generated gitlab-generated\n" } - - it 'returns generated files only' do - expect(generated_files).to contain_exactly('file2.txt') - end - end - - context 'when no generated overrides are used' do - let(:gitattr_content) { "*.txt text\n" } - - it 'returns the original paths' do - expect(generated_files).to eq paths - end - end - - context 'when the given files are generated' do - let(:gitattr_content) { "*.txt gitlab-generated\n" } - - it 'returns an empty array' do - expect(generated_files).to eq [] - end - end - end end diff --git a/spec/rubocop/cop/background_migration/dictionary_file_spec.rb b/spec/rubocop/cop/background_migration/dictionary_file_spec.rb index 7becf9c09a4..4fa7dede093 100644 --- a/spec/rubocop/cop/background_migration/dictionary_file_spec.rb +++ b/spec/rubocop/cop/background_migration/dictionary_file_spec.rb @@ -134,7 +134,7 @@ RSpec.describe RuboCop::Cop::BackgroundMigration::DictionaryFile, feature_catego end context 'with dictionary file' do - let(:introduced_by_url) { 'https://test_url' } + let(:introduced_by_url) { 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132639' } let(:finalize_after) { '20230507160251' } before do @@ -158,6 +158,25 @@ RSpec.describe RuboCop::Cop::BackgroundMigration::DictionaryFile, feature_catego end end + context 'when the `introduced_by_url` is not correct' do + let(:introduced_by_url) { 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132639/invalid' } + + it 'throws offense on having a correct url' do + expect_offense(<<~RUBY) + class QueueMyMigration < Gitlab::Database::Migration[2.1] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{format('Invalid `introduced_by_url` url for the dictionary. Please use the following format: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/XXX')} + def up + queue_batched_background_migration( + 'MyMigration', + :users, + :id + ) + end + end + RUBY + end + end + context 'with required dictionary keys' do it 'does not throw offense with appropriate dictionary file' do expect_no_offenses(<<~RUBY) diff --git a/spec/services/ml/create_model_version_service_spec.rb b/spec/services/ml/create_model_version_service_spec.rb index ea3e2f654d4..4e71c538f7e 100644 --- a/spec/services/ml/create_model_version_service_spec.rb +++ b/spec/services/ml/create_model_version_service_spec.rb @@ -25,4 +25,41 @@ RSpec.describe ::Ml::CreateModelVersionService, feature_category: :mlops do expect(model.reload.latest_version.version).to eq('4.0.0') end end + + context 'when a version is created' do + it 'creates a package' do + expect { service }.to change { Ml::ModelVersion.count }.by(1).and change { + Packages::MlModel::Package.count + }.by(1) + expect(model.reload.latest_version.package.name).to eq(model.name) + expect(model.latest_version.package.version).to eq(model.latest_version.version) + end + end + + context 'when a version is created and the package already exists' do + it 'does not creates a package' do + next_version = Ml::IncrementVersionService.new(model.latest_version.try(:version)).execute + create(:ml_model_package, name: model.name, version: next_version, project: model.project) + + expect { service }.to change { Ml::ModelVersion.count }.by(1).and not_change { + Packages::MlModel::Package.count + } + expect(model.reload.latest_version.package.name).to eq(model.name) + expect(model.latest_version.package.version).to eq(model.latest_version.version) + end + end + + context 'when a version is created and an existing package supplied' do + it 'does not creates a package' do + next_version = Ml::IncrementVersionService.new(model.latest_version.try(:version)).execute + package = create(:ml_model_package, name: model.name, version: next_version, project: model.project) + service = described_class.new(model, { package: package }) + + expect { service.execute }.to change { Ml::ModelVersion.count }.by(1).and not_change { + Packages::MlModel::Package.count + } + expect(model.reload.latest_version.package.name).to eq(model.name) + expect(model.latest_version.package.version).to eq(model.latest_version.version) + end + end end |