From 23f57fb31fe83ea8666753f94118f6d834517dfb Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 2 Nov 2021 18:12:13 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .rubocop_manual_todo.yml | 2 +- .../jobs/components/manual_variables_form.vue | 231 +++++++-------- app/controllers/admin/hook_logs_controller.rb | 2 +- app/controllers/admin/hooks_controller.rb | 2 +- .../admin/integrations_controller.rb | 2 +- .../actions.rb} | 2 +- .../{ => integrations}/hooks_execution.rb | 2 +- .../explore/projects_controller.rb | 13 + .../settings/integrations_controller.rb | 2 +- .../projects/hook_logs_controller.rb | 2 +- app/controllers/projects/hooks_controller.rb | 2 +- .../security/latest_pipeline_information.rb | 36 +++ app/views/explore/projects/topic.html.haml | 31 ++ app/views/shared/projects/_topics.html.haml | 4 +- config/routes/explore.rb | 1 + config/routes/project.rb | 4 +- doc/administration/instance_limits.md | 1 + doc/api/graphql/reference/index.md | 21 ++ doc/ci/yaml/index.md | 40 ++- .../documentation/feature_flags.md | 7 + doc/security/rate_limits.md | 2 + .../settings/account_and_limit_settings.md | 2 +- doc/user/gitlab_com/index.md | 2 +- doc/user/group/custom_project_templates.md | 2 +- doc/user/group/import/index.md | 32 +- doc/user/group/settings/import_export.md | 4 +- doc/user/packages/package_registry/index.md | 14 +- .../external_pull_requests_pipeline.rb | 15 + lib/bulk_imports/projects/stage.rb | 4 + lib/gitlab/ci/config.rb | 31 +- .../ci/pipeline/chain/config/process.rb | 2 +- .../group/relation_tree_restorer.rb | 274 +++++++++++++++++ .../project/relation_tree_restorer.rb | 27 ++ .../project/sample/relation_tree_restorer.rb | 6 +- .../import_export/relation_tree_restorer.rb | 280 ------------------ locale/gitlab.pot | 9 +- package.json | 6 +- .../admin/integrations_controller_spec.rb | 2 +- .../explore/projects_controller_spec.rb | 22 ++ .../settings/integrations_controller_spec.rb | 2 +- .../projects/services_controller_spec.rb | 2 +- spec/features/dashboard/projects_spec.rb | 2 +- ...triggers_manual_job_with_variables_spec.rb | 34 +++ spec/features/projects_spec.rb | 8 +- spec/features/topic_show_spec.rb | 48 +++ .../custom_metrics_form_fields_spec.js | 10 +- .../components/manual_variables_form_spec.js | 160 ++++++---- .../external_pull_requests_pipeline_spec.rb | 66 +++++ spec/lib/bulk_imports/projects/stage_spec.rb | 1 + spec/lib/gitlab/ci/config_spec.rb | 36 ++- .../group/relation_tree_restorer_spec.rb | 88 ++++++ .../project/relation_tree_restorer_spec.rb | 150 ++++++++++ .../sample/relation_tree_restorer_spec.rb | 50 ++-- .../relation_tree_restorer_spec.rb | 184 ------------ .../create_pipeline_service/include_spec.rb | 89 +++++- .../integrations_actions_shared_examples.rb | 2 +- yarn.lock | 63 ++-- 57 files changed, 1315 insertions(+), 823 deletions(-) rename app/controllers/concerns/{integrations_actions.rb => integrations/actions.rb} (98%) rename app/controllers/concerns/{ => integrations}/hooks_execution.rb (97%) create mode 100644 app/models/concerns/security/latest_pipeline_information.rb create mode 100644 app/views/explore/projects/topic.html.haml create mode 100644 lib/bulk_imports/projects/pipelines/external_pull_requests_pipeline.rb create mode 100644 lib/gitlab/import_export/group/relation_tree_restorer.rb create mode 100644 lib/gitlab/import_export/project/relation_tree_restorer.rb delete mode 100644 lib/gitlab/import_export/relation_tree_restorer.rb create mode 100644 spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb create mode 100644 spec/features/topic_show_spec.rb create mode 100644 spec/lib/bulk_imports/projects/pipelines/external_pull_requests_pipeline_spec.rb create mode 100644 spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb create mode 100644 spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb delete mode 100644 spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb rename spec/support/shared_examples/controllers/concerns/{ => integrations}/integrations_actions_shared_examples.rb (97%) diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index d1c2fff0528..eaab807cd88 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -2455,7 +2455,7 @@ Database/MultipleDatabases: - 'lib/gitlab/gitlab_import/importer.rb' - 'lib/gitlab/health_checks/db_check.rb' - 'lib/gitlab/import_export/base/relation_factory.rb' - - 'lib/gitlab/import_export/relation_tree_restorer.rb' + - 'lib/gitlab/import_export/group/relation_tree_restorer.rb' - 'lib/gitlab/legacy_github_import/importer.rb' - 'lib/gitlab/metrics/samplers/database_sampler.rb' - 'lib/gitlab/seeder.rb' diff --git a/app/assets/javascripts/jobs/components/manual_variables_form.vue b/app/assets/javascripts/jobs/components/manual_variables_form.vue index 269551ff9aa..7a52a1b0d6b 100644 --- a/app/assets/javascripts/jobs/components/manual_variables_form.vue +++ b/app/assets/javascripts/jobs/components/manual_variables_form.vue @@ -1,5 +1,12 @@ diff --git a/app/controllers/admin/hook_logs_controller.rb b/app/controllers/admin/hook_logs_controller.rb index 4cebfb485e5..aa13673095d 100644 --- a/app/controllers/admin/hook_logs_controller.rb +++ b/app/controllers/admin/hook_logs_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Admin::HookLogsController < Admin::ApplicationController - include HooksExecution + include ::Integrations::HooksExecution before_action :hook, only: [:show, :retry] before_action :hook_log, only: [:show, :retry] diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb index fb397e7c8c7..6f5475a4a78 100644 --- a/app/controllers/admin/hooks_controller.rb +++ b/app/controllers/admin/hooks_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Admin::HooksController < Admin::ApplicationController - include HooksExecution + include ::Integrations::HooksExecution before_action :hook_logs, only: :edit diff --git a/app/controllers/admin/integrations_controller.rb b/app/controllers/admin/integrations_controller.rb index a3eb24b9b6f..ad0ee0b2cef 100644 --- a/app/controllers/admin/integrations_controller.rb +++ b/app/controllers/admin/integrations_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Admin::IntegrationsController < Admin::ApplicationController - include IntegrationsActions + include ::Integrations::Actions before_action :not_found, unless: -> { instance_level_integrations? } diff --git a/app/controllers/concerns/integrations_actions.rb b/app/controllers/concerns/integrations/actions.rb similarity index 98% rename from app/controllers/concerns/integrations_actions.rb rename to app/controllers/concerns/integrations/actions.rb index 47d09d7e375..6490742c0f8 100644 --- a/app/controllers/concerns/integrations_actions.rb +++ b/app/controllers/concerns/integrations/actions.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module IntegrationsActions +module Integrations::Actions extend ActiveSupport::Concern included do diff --git a/app/controllers/concerns/hooks_execution.rb b/app/controllers/concerns/integrations/hooks_execution.rb similarity index 97% rename from app/controllers/concerns/hooks_execution.rb rename to app/controllers/concerns/integrations/hooks_execution.rb index 87d215f50e7..af039057a9c 100644 --- a/app/controllers/concerns/hooks_execution.rb +++ b/app/controllers/concerns/integrations/hooks_execution.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module HooksExecution +module Integrations::HooksExecution extend ActiveSupport::Concern private diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index 3dc6a16cbc1..81e1643a8f8 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -68,6 +68,15 @@ class Explore::ProjectsController < Explore::ApplicationController end # rubocop: enable CodeReuse/ActiveRecord + def topic + load_topic + + return render_404 unless @topic + + params[:topic] = @topic.name + @projects = load_projects + end + private def load_project_counts @@ -86,6 +95,10 @@ class Explore::ProjectsController < Explore::ApplicationController prepare_projects_for_rendering(projects) end + def load_topic + @topic = Projects::Topic.find_by_name(params[:topic_name]) + end + # rubocop: disable CodeReuse/ActiveRecord def preload_associations(projects) projects.includes(:route, :creator, :group, :project_feature, :topics, namespace: [:route, :owner]) diff --git a/app/controllers/groups/settings/integrations_controller.rb b/app/controllers/groups/settings/integrations_controller.rb index a7a1de03224..0a63c3d304b 100644 --- a/app/controllers/groups/settings/integrations_controller.rb +++ b/app/controllers/groups/settings/integrations_controller.rb @@ -3,7 +3,7 @@ module Groups module Settings class IntegrationsController < Groups::ApplicationController - include IntegrationsActions + include ::Integrations::Actions before_action :authorize_admin_group! diff --git a/app/controllers/projects/hook_logs_controller.rb b/app/controllers/projects/hook_logs_controller.rb index 959fa97e562..0ca3d71f728 100644 --- a/app/controllers/projects/hook_logs_controller.rb +++ b/app/controllers/projects/hook_logs_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Projects::HookLogsController < Projects::ApplicationController - include HooksExecution + include ::Integrations::HooksExecution before_action :authorize_admin_project! diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index 4b4e6367a2a..c79e5a8cc85 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Projects::HooksController < Projects::ApplicationController - include HooksExecution + include ::Integrations::HooksExecution # Authorize before_action :authorize_admin_project! diff --git a/app/models/concerns/security/latest_pipeline_information.rb b/app/models/concerns/security/latest_pipeline_information.rb new file mode 100644 index 00000000000..87eae3cac68 --- /dev/null +++ b/app/models/concerns/security/latest_pipeline_information.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Security + module LatestPipelineInformation + private + + def scanner_enabled?(scan_type) + latest_builds_reports.include?(scan_type) + end + + def latest_builds_reports(only_successful_builds: false) + strong_memoize("latest_builds_reports_#{only_successful_builds}") do + builds = latest_security_builds + builds = builds.select { |build| build.status == 'success' } if only_successful_builds + builds.flat_map do |build| + build.options[:artifacts][:reports].keys + end + end + end + + def latest_security_builds + return [] unless latest_default_branch_pipeline + + ::Security::SecurityJobsFinder.new(pipeline: latest_default_branch_pipeline).execute + + ::Security::LicenseComplianceJobsFinder.new(pipeline: latest_default_branch_pipeline).execute + end + + def latest_default_branch_pipeline + strong_memoize(:pipeline) { latest_pipeline } + end + + def auto_devops_source? + latest_default_branch_pipeline&.auto_devops_source? + end + end +end diff --git a/app/views/explore/projects/topic.html.haml b/app/views/explore/projects/topic.html.haml new file mode 100644 index 00000000000..9b6e4acb02a --- /dev/null +++ b/app/views/explore/projects/topic.html.haml @@ -0,0 +1,31 @@ +- @hide_top_links = false +- @no_container = true +- page_title @topic.name, _("Topics") +- max_topic_name_length = 50 + += render_dashboard_ultimate_trial(current_user) + +.gl-text-center.gl-bg-gray-10.gl-pb-2.gl-pt-6 + .gl-pb-5.gl-align-items-center.gl-justify-content-center.gl-display-flex + .avatar-container.s60.gl-flex-shrink-0 + = topic_icon(@topic, alt: _('Topic avatar'), class: 'avatar topic-avatar s60') + - if @topic.name.length > max_topic_name_length + %h1.gl-mt-3.str-truncated.has-tooltip{ title: @topic.name } + = truncate(@topic.name, length: max_topic_name_length) + - else + %h1.gl-mt-3 + = @topic.name + - if @topic.description.present? + .topic-description.gl-ml-4.gl-mr-4 + = markdown(@topic.description) + +%div{ class: container_class } + .gl-py-5.gl-border-gray-100.gl-border-b-solid.gl-border-b-1 + %h3.gl-m-0= _('Projects with this topic') + .top-area.gl-pt-2.gl-pb-2 + .nav-controls + = render 'shared/projects/search_form' + = render 'shared/projects/dropdown' + = render 'filter' + + = render 'projects', projects: @projects diff --git a/app/views/shared/projects/_topics.html.haml b/app/views/shared/projects/_topics.html.haml index a7429483da1..e303f05c5df 100644 --- a/app/views/shared/projects/_topics.html.haml +++ b/app/views/shared/projects/_topics.html.haml @@ -8,7 +8,7 @@ = sprite_icon('tag', css_class: 'icon gl-relative gl-mr-2') - project.topics_to_show.each do |topic| - - explore_project_topic_path = explore_projects_path(topic: topic) + - explore_project_topic_path = topic_explore_projects_path(topic_name: topic) - if topic.length > max_project_topic_length %a{ class: "#{ project_topics_classes } str-truncated-30 has-tooltip", data: { container: "body" }, title: topic, href: explore_project_topic_path, itemprop: 'keywords' } = truncate(topic, length: max_project_topic_length) @@ -21,7 +21,7 @@ - content = capture do %span.gl-display-inline-flex.gl-flex-wrap - project.topics_not_shown.each do |topic| - - explore_project_topic_path = explore_projects_path(topic: topic) + - explore_project_topic_path = topic_explore_projects_path(topic_name: topic) - if topic.length > max_project_topic_length %a{ class: "#{ project_topics_classes } gl-mb-3 str-truncated has-tooltip", data: { container: "body" }, title: topic, href: explore_project_topic_path, itemprop: 'keywords' } = truncate(topic, length: max_project_topic_length) diff --git a/config/routes/explore.rb b/config/routes/explore.rb index c6bf98b6fb3..946b76cc485 100644 --- a/config/routes/explore.rb +++ b/config/routes/explore.rb @@ -5,6 +5,7 @@ namespace :explore do collection do get :trending get :starred + get 'topics/:topic_name', action: :topic, as: :topic, constraints: { topic_name: /.+/ } end end diff --git a/config/routes/project.rb b/config/routes/project.rb index 496d63785e6..446ecc4159b 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -38,7 +38,9 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do end namespace :security do - resource :configuration, only: [:show], controller: :configuration + resource :configuration, only: [:show], controller: :configuration do + resource :sast, only: [:show], controller: :sast_configuration + end end resources :artifacts, only: [:index, :destroy] diff --git a/doc/administration/instance_limits.md b/doc/administration/instance_limits.md index 4f9f08dab28..c7481abc84e 100644 --- a/doc/administration/instance_limits.md +++ b/doc/administration/instance_limits.md @@ -533,6 +533,7 @@ Plan.default.actual_limits.update!(pages_file_entries: 100) The total number of registered runners is limited at the group and project levels. Each time a new runner is registered, GitLab checks these limits against runners that have been active in the last 3 months. A runner's registration fails if it exceeds the limit for the scope determined by the runner registration token. +If the limit value is set to zero, the limit is disabled. - GitLab SaaS subscribers have different limits defined per plan, affecting all projects using that plan. - Self-managed GitLab Premium and Ultimate limits are defined by a default plan that affects all projects: diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index b62d46b4a03..ea40b5b1d50 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -1034,6 +1034,27 @@ Input type: `ConfigureSecretDetectionInput` | `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | `successPath` | [`String`](#string) | Redirect path to use when the response is successful. | +### `Mutation.corpusCreate` + +Available only when feature flag `corpus_management` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. + +Input type: `CorpusCreateInput` + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| `fullPath` | [`ID!`](#id) | Project the corpus belongs to. | +| `packageId` | [`PackagesPackageID!`](#packagespackageid) | ID of the corpus package. | + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | + ### `Mutation.createAlertIssue` Input type: `CreateAlertIssueInput` diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index 4a7fddc85cb..22d69cafa83 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -422,22 +422,36 @@ configurations. Local configurations in the `.gitlab-ci.yml` file override inclu > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/284883) in GitLab 13.8. > - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/294294) in GitLab 13.9. > - [Support for project, group, and instance variables added](https://gitlab.com/gitlab-org/gitlab/-/issues/219065) in GitLab 14.2. +> - [Support for pipeline variables added](https://gitlab.com/gitlab-org/gitlab/-/issues/337633) in GitLab 14.5. In `include` sections in your `.gitlab-ci.yml` file, you can use: -- `$CI_COMMIT_REF_NAME` [predefined variable](../variables/predefined_variables.md) in GitLab 14.2 - and later. - - When used in `include`, the `CI_COMMIT_REF_NAME` variable returns the full - ref path, like `refs/heads/branch-name`. In other cases, this variable returns only - the branch name, like `branch-name`. - - To use `CI_COMMIT_REF_NAME` in `include:rules`, you might need to use `if: $CI_COMMIT_REF_NAME =~ /main/` - (not `== main`). [An issue exists](https://gitlab.com/gitlab-org/gitlab/-/issues/337633) - to align `CI_COMMIT_REF_NAME` behavior in all cases. - [Project variables](../variables/index.md#add-a-cicd-variable-to-a-project) - [Group variables](../variables/index.md#add-a-cicd-variable-to-a-group) - [Instance variables](../variables/index.md#add-a-cicd-variable-to-an-instance) -- Project [predefined variables](../variables/predefined_variables.md). +- Project [predefined variables](../variables/predefined_variables.md) +- In GitLab 14.2 and later, the `$CI_COMMIT_REF_NAME` [predefined variable](../variables/predefined_variables.md). + + When used in `include`, the `CI_COMMIT_REF_NAME` variable returns the full + ref path, like `refs/heads/branch-name`. In `include:rules`, you might need to use + `if: $CI_COMMIT_REF_NAME =~ /main/` (not `== main`). This behavior is resolved in GitLab 14.5. + +In GitLab 14.5 and later, you can also use: + +- [Trigger variables](../triggers/index.md#making-use-of-trigger-variables). +- [Scheduled pipeline variables](../pipelines/schedules.md#using-variables). +- [Manual pipeline run variables](../variables/index.md#override-a-variable-when-running-a-pipeline-manually). +- Pipeline [predefined variables](../variables/predefined_variables.md). + + YAML files are parsed before the pipeline is created, so the following pipeline predefined variables + are **not** available: + + - `CI_PIPELINE_ID` + - `CI_PIPELINE_URL` + - `CI_PIPELINE_IID` + - `CI_PIPELINE_CREATED_AT` + +For example: ```yaml include: @@ -448,9 +462,6 @@ include: For an example of how you can include these predefined variables, and the variables' impact on CI/CD jobs, see this [CI/CD variable demo](https://youtu.be/4XR8gw3Pkos). -There is a [related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/337633) -that proposes expanding this feature to support more variables. - #### `rules` with `include` > - Introduced in GitLab 14.2 [with a flag](../../administration/feature_flags.md) named `ci_include_rules`. Disabled by default. @@ -468,6 +479,9 @@ include: - local: builds.yml rules: - if: '$INCLUDE_BUILDS == "true"' + - local: deploys.yml + rules: + - if: $CI_COMMIT_BRANCH == "main" test: stage: test diff --git a/doc/development/documentation/feature_flags.md b/doc/development/documentation/feature_flags.md index 78cc4314d4f..f234ec6f2e2 100644 --- a/doc/development/documentation/feature_flags.md +++ b/doc/development/documentation/feature_flags.md @@ -74,6 +74,13 @@ Possible version history entries are: > - [Generally available](issue-link) in GitLab X.Y. [Feature flag ](issue-link) removed. ``` +You can combine entries if they happened in the same release: + +```markdown +> - Introduced in GitLab 14.2 [with a flag](../../administration/feature_flags.md) named `ci_include_rules`. Disabled by default. +> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/337507) in GitLab 14.3. +``` + ## Feature flag documentation examples The following examples show the progression of a feature flag. diff --git a/doc/security/rate_limits.md b/doc/security/rate_limits.md index 278be565971..3826c02a201 100644 --- a/doc/security/rate_limits.md +++ b/doc/security/rate_limits.md @@ -128,5 +128,7 @@ To remove a blocked IP: keys *rack::attack* ``` + By default, the [`keys` command is disabled](https://docs.gitlab.com/omnibus/settings/redis.html#renamed-commands). + 1. Optionally, add [the IP to the allowlist](https://docs.gitlab.com/omnibus/settings/configuration.html#configuring-rack-attack) to prevent it being denylisted again. diff --git a/doc/user/admin_area/settings/account_and_limit_settings.md b/doc/user/admin_area/settings/account_and_limit_settings.md index 81c1c6b8fd8..7ec17a4c5f0 100644 --- a/doc/user/admin_area/settings/account_and_limit_settings.md +++ b/doc/user/admin_area/settings/account_and_limit_settings.md @@ -125,7 +125,7 @@ is rejected. NOTE: The repository size limit includes repository files and LFS, but does not include artifacts, uploads, -wiki, packages, or snippets. +wiki, packages, or snippets. The repository size limit applies to both private and public projects. For details on manually purging files, see [reducing the repository size using Git](../../project/repository/reducing_the_repo_size_using_git.md). diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md index e0cc79fe0fb..93b5da1e680 100644 --- a/doc/user/gitlab_com/index.md +++ b/doc/user/gitlab_com/index.md @@ -158,7 +158,7 @@ If you are near or over the repository size limit, you can either NOTE: `git push` and GitLab project imports are limited to 5 GB per request through Cloudflare. Git LFS and imports other than a file upload are not affected by -this limit. +this limit. Repository limits apply to both public and private projects. ## IP range diff --git a/doc/user/group/custom_project_templates.md b/doc/user/group/custom_project_templates.md index a9c56139b4d..9378b3922b5 100644 --- a/doc/user/group/custom_project_templates.md +++ b/doc/user/group/custom_project_templates.md @@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Custom group-level project templates **(PREMIUM)** -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/6861) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.6. +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/6861) in GitLab 11.6. [Group owners](../permissions.md#group-members-permissions) can set a subgroup to be the source of project templates that are selectable when a new project is created diff --git a/doc/user/group/import/index.md b/doc/user/group/import/index.md index 1f5de36303e..3d91fe44cf8 100644 --- a/doc/user/group/import/index.md +++ b/doc/user/group/import/index.md @@ -19,23 +19,23 @@ Using GitLab Group Migration, you can migrate existing top-level groups from Git The following resources are migrated to the target instance: -- Groups ([Introduced in 13.7](https://gitlab.com/groups/gitlab-org/-/epics/4374)) +- Groups ([Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4374) in 13.7) - description - attributes - subgroups - - avatar ([Introduced in 14.0](https://gitlab.com/gitlab-org/gitlab/-/issues/322904)) -- Group Labels ([Introduced in 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/292429)) + - avatar ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/322904) in 14.0) +- Group Labels ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292429) in 13.9) - title - description - color - - created_at ([Introduced in 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/300007)) - - updated_at ([Introduced in 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/300007)) -- Members ([Introduced in 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/299415)) + - created_at ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/300007) in 13.10) + - updated_at ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/300007) in 13.10) +- Members ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/299415) in 13.9) Group members are associated with the imported group if: - The user already exists in the target GitLab instance and - The user has a public email in the source GitLab instance that matches a confirmed email in the target GitLab instance -- Epics ([Introduced in 13.7](https://gitlab.com/gitlab-org/gitlab/-/issues/250281)) +- Epics ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/250281) in 13.7) - title - description - state (open / closed) @@ -43,12 +43,12 @@ The following resources are migrated to the target instance: - due date - epic order on boards - confidentiality - - labels ([Introduced in 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/297460)) - - author ([Introduced in 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/298745)) - - parent epic ([Introduced in 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/297459)) - - emoji award ([Introduced in 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/297466)) - - events ([Introduced in 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/297465)) -- Milestones ([Introduced in 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/292427)) + - labels ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/297460) in 13.9) + - author ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/298745) in 13.9) + - parent epic ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/297459) in 13.9) + - emoji award ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/297466) in 13.9) + - events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/297465) in 13.10) +- Milestones ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292427) in 13.10) - title - description - state (active / closed) @@ -56,8 +56,8 @@ The following resources are migrated to the target instance: - due date - created at - updated at - - iid ([Introduced in 13.11](https://gitlab.com/gitlab-org/gitlab/-/issues/326157)) -- Iterations ([Introduced in 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/292428)) + - iid ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/326157) in 13.11) +- Iterations ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292428) in 13.10) - iid - title - description @@ -66,7 +66,7 @@ The following resources are migrated to the target instance: - due date - created at - updated at -- Badges ([Introduced in 13.11](https://gitlab.com/gitlab-org/gitlab/-/issues/292431)) +- Badges ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292431) in 13.11) - name - link URL - image URL diff --git a/doc/user/group/settings/import_export.md b/doc/user/group/settings/import_export.md index ce916fb33ae..c370221b57b 100644 --- a/doc/user/group/settings/import_export.md +++ b/doc/user/group/settings/import_export.md @@ -51,8 +51,8 @@ The following items are exported: - Subgroups (including all the aforementioned data) - Epics - Events -- [Wikis](../../project/wiki/group.md) **(PREMIUM SELF)** - (Introduced in [GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53247)) +- [Wikis](../../project/wiki/group.md) + ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53247) in GitLab 13.9) The following items are **not** exported: diff --git a/doc/user/packages/package_registry/index.md b/doc/user/packages/package_registry/index.md index 3933175a40f..49442ddca51 100644 --- a/doc/user/packages/package_registry/index.md +++ b/doc/user/packages/package_registry/index.md @@ -135,11 +135,11 @@ The Package Registry supports the following formats: | Package type | GitLab version | Status | | ------------ | -------------- |------- | -| [Maven](../maven_repository/index.md) | 11.3+ | Stable | -| [npm](../npm_registry/index.md) | 11.7+ | Stable | -| [NuGet](../nuget_repository/index.md) | 12.8+ | Stable | -| [PyPI](../pypi_repository/index.md) | 12.10+ | Stable | -| [Generic packages](../generic_packages/index.md) | 13.5+ | Stable | +| [Maven](../maven_repository/index.md) | 11.3+ | GA | +| [npm](../npm_registry/index.md) | 11.7+ | GA | +| [NuGet](../nuget_repository/index.md) | 12.8+ | GA | +| [PyPI](../pypi_repository/index.md) | 12.10+ | GA | +| [Generic packages](../generic_packages/index.md) | 13.5+ | GA | | [Composer](../composer_repository/index.md) | 13.2+ | [Beta](https://gitlab.com/groups/gitlab-org/-/epics/6817) | | [Conan](../conan_repository/index.md) | 12.6+ | [Beta](https://gitlab.com/groups/gitlab-org/-/epics/6816) | | [Helm](../helm_repository/index.md) | 14.1+ | [Beta](https://gitlab.com/groups/gitlab-org/-/epics/6366) | @@ -147,11 +147,11 @@ The Package Registry supports the following formats: | [Go](../go_proxy/index.md) | 13.1+ | [Alpha](https://gitlab.com/groups/gitlab-org/-/epics/3043) | | [Ruby gems](../rubygems_registry/index.md) | 13.10+ | [Alpha](https://gitlab.com/groups/gitlab-org/-/epics/3200) | -Status: +[Status](https://about.gitlab.com/handbook/product/gitlab-the-product/#generally-available-ga): - Alpha: behind a feature flag and not officially supported. - Beta: several known issues that may prevent expected use. -- Stable: ready for production use. +- GA (Generally Available): ready for production use at any scale. You can also use the [API](../../../api/packages.md) to administer the Package Registry. diff --git a/lib/bulk_imports/projects/pipelines/external_pull_requests_pipeline.rb b/lib/bulk_imports/projects/pipelines/external_pull_requests_pipeline.rb new file mode 100644 index 00000000000..1f720596c8f --- /dev/null +++ b/lib/bulk_imports/projects/pipelines/external_pull_requests_pipeline.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module BulkImports + module Projects + module Pipelines + class ExternalPullRequestsPipeline + include NdjsonPipeline + + relation_name 'external_pull_requests' + + extractor ::BulkImports::Common::Extractors::NdjsonExtractor, relation: relation + end + end + end +end diff --git a/lib/bulk_imports/projects/stage.rb b/lib/bulk_imports/projects/stage.rb index 83f7fddb81d..3576b7794d2 100644 --- a/lib/bulk_imports/projects/stage.rb +++ b/lib/bulk_imports/projects/stage.rb @@ -31,6 +31,10 @@ module BulkImports pipeline: BulkImports::Projects::Pipelines::MergeRequestsPipeline, stage: 4 }, + external_pull_requests: { + pipeline: BulkImports::Projects::Pipelines::ExternalPullRequestsPipeline, + stage: 4 + }, uploads: { pipeline: BulkImports::Common::Pipelines::UploadsPipeline, stage: 5 diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb index aceaf012f7e..6f149385969 100644 --- a/lib/gitlab/ci/config.rb +++ b/lib/gitlab/ci/config.rb @@ -19,11 +19,12 @@ module Gitlab attr_reader :root, :context, :source_ref_path, :source - def initialize(config, project: nil, sha: nil, user: nil, parent_pipeline: nil, source_ref_path: nil, source: nil) - @context = build_context(project: project, sha: sha, user: user, parent_pipeline: parent_pipeline, ref: source_ref_path) + def initialize(config, project: nil, pipeline: nil, sha: nil, user: nil, parent_pipeline: nil, source: nil) + @source_ref_path = pipeline&.source_ref_path + + @context = build_context(project: project, pipeline: pipeline, sha: sha, user: user, parent_pipeline: parent_pipeline) @context.set_deadline(TIMEOUT_SECONDS) - @source_ref_path = source_ref_path @source = source @config = expand_config(config) @@ -108,16 +109,16 @@ module Gitlab end end - def build_context(project:, sha:, user:, parent_pipeline:, ref:) + def build_context(project:, pipeline:, sha:, user:, parent_pipeline:) Config::External::Context.new( project: project, sha: sha || find_sha(project), user: user, parent_pipeline: parent_pipeline, - variables: build_variables(project: project, ref: ref)) + variables: build_variables(project: project, pipeline: pipeline)) end - def build_variables(project:, ref:) + def build_variables(project:, pipeline:) Gitlab::Ci::Variables::Collection.new.tap do |variables| break variables unless project @@ -126,18 +127,12 @@ module Gitlab # # See more detail in the docs: https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence variables.concat(project.predefined_variables) - variables.concat(pipeline_predefined_variables(ref: ref)) - variables.concat(project.ci_instance_variables_for(ref: ref)) - variables.concat(project.group.ci_variables_for(ref, project)) if project.group - variables.concat(project.ci_variables_for(ref: ref)) - end - end - - # https://gitlab.com/gitlab-org/gitlab/-/issues/337633 aims to add all predefined variables - # to this list, but only CI_COMMIT_REF_NAME is available right now to support compliance pipelines. - def pipeline_predefined_variables(ref:) - Gitlab::Ci::Variables::Collection.new.tap do |v| - v.append(key: 'CI_COMMIT_REF_NAME', value: ref) + variables.concat(pipeline.predefined_variables) if pipeline + variables.concat(project.ci_instance_variables_for(ref: source_ref_path)) + variables.concat(project.group.ci_variables_for(source_ref_path, project)) if project.group + variables.concat(project.ci_variables_for(ref: source_ref_path)) + variables.concat(pipeline.variables) if pipeline + variables.concat(pipeline.pipeline_schedule.job_variables) if pipeline&.pipeline_schedule end end diff --git a/lib/gitlab/ci/pipeline/chain/config/process.rb b/lib/gitlab/ci/pipeline/chain/config/process.rb index 5251dd3d40a..f3c937ddd28 100644 --- a/lib/gitlab/ci/pipeline/chain/config/process.rb +++ b/lib/gitlab/ci/pipeline/chain/config/process.rb @@ -14,7 +14,7 @@ module Gitlab result = ::Gitlab::Ci::YamlProcessor.new( @command.config_content, { project: project, - source_ref_path: @pipeline.source_ref_path, + pipeline: @pipeline, sha: @pipeline.sha, source: @pipeline.source, user: current_user, diff --git a/lib/gitlab/import_export/group/relation_tree_restorer.rb b/lib/gitlab/import_export/group/relation_tree_restorer.rb new file mode 100644 index 00000000000..f3c392b8c20 --- /dev/null +++ b/lib/gitlab/import_export/group/relation_tree_restorer.rb @@ -0,0 +1,274 @@ +# frozen_string_literal: true + +module Gitlab + module ImportExport + module Group + class RelationTreeRestorer + def initialize( # rubocop:disable Metrics/ParameterLists + user:, + shared:, + relation_reader:, + members_mapper:, + object_builder:, + relation_factory:, + reader:, + importable:, + importable_attributes:, + importable_path: + ) + @user = user + @shared = shared + @importable = importable + @relation_reader = relation_reader + @members_mapper = members_mapper + @object_builder = object_builder + @relation_factory = relation_factory + @reader = reader + @importable_attributes = importable_attributes + @importable_path = importable_path + end + + def restore + ActiveRecord::Base.uncached do + ActiveRecord::Base.no_touching do + update_params! + + BulkInsertableAssociations.with_bulk_insert(enabled: bulk_insert_enabled) do + fix_ci_pipelines_not_sorted_on_legacy_project_json! + create_relations! + end + end + end + + # ensure that we have latest version of the restore + @importable.reload # rubocop:disable Cop/ActiveRecordAssociationReload + + true + rescue StandardError => e + @shared.error(e) + false + end + + private + + def bulk_insert_enabled + false + end + + # Loops through the tree of models defined in import_export.yml and + # finds them in the imported JSON so they can be instantiated and saved + # in the DB. The structure and relationships between models are guessed from + # the configuration yaml file too. + # Finally, it updates each attribute in the newly imported project/group. + def create_relations! + relations.each do |relation_key, relation_definition| + process_relation!(relation_key, relation_definition) + end + end + + def process_relation!(relation_key, relation_definition) + @relation_reader.consume_relation(@importable_path, relation_key).each do |data_hash, relation_index| + process_relation_item!(relation_key, relation_definition, relation_index, data_hash) + end + end + + def process_relation_item!(relation_key, relation_definition, relation_index, data_hash) + relation_object = build_relation(relation_key, relation_definition, relation_index, data_hash) + return unless relation_object + return if relation_invalid_for_importable?(relation_object) + + relation_object.assign_attributes(importable_class_sym => @importable) + + import_failure_service.with_retry(action: 'relation_object.save!', relation_key: relation_key, relation_index: relation_index) do + relation_object.save! + log_relation_creation(@importable, relation_key, relation_object) + end + rescue StandardError => e + import_failure_service.log_import_failure( + source: 'process_relation_item!', + relation_key: relation_key, + relation_index: relation_index, + exception: e) + end + + def import_failure_service + @import_failure_service ||= ImportFailureService.new(@importable) + end + + def relations + @relations ||= + @reader + .attributes_finder + .find_relations_tree(importable_class_sym) + .deep_stringify_keys + end + + def update_params! + params = @importable_attributes.except(*relations.keys.map(&:to_s)) + params = params.merge(present_override_params) + + # Cleaning all imported and overridden params + params = Gitlab::ImportExport::AttributeCleaner.clean( + relation_hash: params, + relation_class: importable_class, + excluded_keys: excluded_keys_for_relation(importable_class_sym)) + + @importable.assign_attributes(params) + + modify_attributes + + Gitlab::Timeless.timeless(@importable) do + @importable.save! + end + end + + def present_override_params + # we filter out the empty strings from the overrides + # keeping the default values configured + override_params&.transform_values do |value| + value.is_a?(String) ? value.presence : value + end&.compact + end + + def override_params + @importable_override_params ||= importable_override_params + end + + def importable_override_params + if @importable.respond_to?(:import_data) + @importable.import_data&.data&.fetch('override_params', nil) || {} + else + {} + end + end + + def modify_attributes + # no-op to be overridden on inheritance + end + + def build_relations(relation_key, relation_definition, relation_index, data_hashes) + data_hashes + .map { |data_hash| build_relation(relation_key, relation_definition, relation_index, data_hash) } + .tap { |entries| entries.compact! } + end + + def build_relation(relation_key, relation_definition, relation_index, data_hash) + # TODO: This is hack to not create relation for the author + # Rather make `RelationFactory#set_note_author` to take care of that + return data_hash if relation_key == 'author' || already_restored?(data_hash) + + # create relation objects recursively for all sub-objects + relation_definition.each do |sub_relation_key, sub_relation_definition| + transform_sub_relations!(data_hash, sub_relation_key, sub_relation_definition, relation_index) + end + + relation = @relation_factory.create(**relation_factory_params(relation_key, relation_index, data_hash)) + + if relation && !relation.valid? + @shared.logger.warn( + message: "[Project/Group Import] Invalid object relation built", + relation_key: relation_key, + relation_index: relation_index, + relation_class: relation.class.name, + error_messages: relation.errors.full_messages.join(". ") + ) + end + + relation + end + + # Since we update the data hash in place as we restore relation items, + # and since we also de-duplicate items, we might encounter items that + # have already been restored in a previous iteration. + def already_restored?(relation_item) + !relation_item.is_a?(Hash) + end + + def transform_sub_relations!(data_hash, sub_relation_key, sub_relation_definition, relation_index) + sub_data_hash = data_hash[sub_relation_key] + return unless sub_data_hash + + # if object is a hash we can create simple object + # as it means that this is 1-to-1 vs 1-to-many + current_item = + if sub_data_hash.is_a?(Array) + build_relations( + sub_relation_key, + sub_relation_definition, + relation_index, + sub_data_hash).presence + else + build_relation( + sub_relation_key, + sub_relation_definition, + relation_index, + sub_data_hash) + end + + if current_item + data_hash[sub_relation_key] = current_item + else + data_hash.delete(sub_relation_key) + end + end + + def relation_invalid_for_importable?(_relation_object) + false + end + + def excluded_keys_for_relation(relation) + @reader.attributes_finder.find_excluded_keys(relation) + end + + def importable_class + @importable.class + end + + def importable_class_sym + importable_class.to_s.downcase.to_sym + end + + def relation_factory_params(relation_key, relation_index, data_hash) + { + relation_index: relation_index, + relation_sym: relation_key.to_sym, + relation_hash: data_hash, + importable: @importable, + members_mapper: @members_mapper, + object_builder: @object_builder, + user: @user, + excluded_keys: excluded_keys_for_relation(relation_key) + } + end + + # Temporary fix for https://gitlab.com/gitlab-org/gitlab/-/issues/27883 when import from legacy project.json + # This should be removed once legacy JSON format is deprecated. + # Ndjson export file will fix the order during project export. + def fix_ci_pipelines_not_sorted_on_legacy_project_json! + return unless @relation_reader.legacy? + + @relation_reader.sort_ci_pipelines_by_id + end + + # Enable logging of each top-level relation creation when Importing + # into a Group if feature flag is enabled + def log_relation_creation(importable, relation_key, relation_object) + root_ancestor_group = importable.try(:root_ancestor) + + return unless root_ancestor_group + return unless root_ancestor_group.instance_of?(::Group) + return unless Feature.enabled?(:log_import_export_relation_creation, root_ancestor_group) + + @shared.logger.info( + importable_type: importable.class.to_s, + importable_id: importable.id, + relation_key: relation_key, + relation_id: relation_object.id, + author_id: relation_object.try(:author_id), + message: '[Project/Group Import] Created new object relation' + ) + end + end + end + end +end diff --git a/lib/gitlab/import_export/project/relation_tree_restorer.rb b/lib/gitlab/import_export/project/relation_tree_restorer.rb new file mode 100644 index 00000000000..6e9548f393a --- /dev/null +++ b/lib/gitlab/import_export/project/relation_tree_restorer.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Gitlab + module ImportExport + module Project + class RelationTreeRestorer < ImportExport::Group::RelationTreeRestorer + # Relations which cannot be saved at project level (and have a group assigned) + GROUP_MODELS = [GroupLabel, Milestone, Epic].freeze + + private + + def bulk_insert_enabled + true + end + + def modify_attributes + @importable.reconcile_shared_runners_setting! + @importable.drop_visibility_level! + end + + def relation_invalid_for_importable?(relation_object) + GROUP_MODELS.include?(relation_object.class) && relation_object.group_id + end + end + end + end +end diff --git a/lib/gitlab/import_export/project/sample/relation_tree_restorer.rb b/lib/gitlab/import_export/project/sample/relation_tree_restorer.rb index 4db92b12968..034122a9f14 100644 --- a/lib/gitlab/import_export/project/sample/relation_tree_restorer.rb +++ b/lib/gitlab/import_export/project/sample/relation_tree_restorer.rb @@ -4,7 +4,7 @@ module Gitlab module ImportExport module Project module Sample - class RelationTreeRestorer < ImportExport::RelationTreeRestorer + class RelationTreeRestorer < ImportExport::Project::RelationTreeRestorer def initialize(...) super(...) @@ -18,10 +18,10 @@ module Gitlab end def dates - return [] if relation_reader.legacy? + return [] if @relation_reader.legacy? RelationFactory::DATE_MODELS.flat_map do |tag| - relation_reader.consume_relation(@importable_path, tag, mark_as_consumed: false).map do |model| + @relation_reader.consume_relation(@importable_path, tag, mark_as_consumed: false).map do |model| model.first['due_date'] end end diff --git a/lib/gitlab/import_export/relation_tree_restorer.rb b/lib/gitlab/import_export/relation_tree_restorer.rb deleted file mode 100644 index 1eeacafef53..00000000000 --- a/lib/gitlab/import_export/relation_tree_restorer.rb +++ /dev/null @@ -1,280 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module ImportExport - class RelationTreeRestorer - # Relations which cannot be saved at project level (and have a group assigned) - GROUP_MODELS = [GroupLabel, Milestone, Epic].freeze - - attr_reader :user - attr_reader :shared - attr_reader :importable - attr_reader :relation_reader - - def initialize( # rubocop:disable Metrics/ParameterLists - user:, shared:, relation_reader:, - members_mapper:, object_builder:, - relation_factory:, - reader:, - importable:, - importable_attributes:, - importable_path: - ) - @user = user - @shared = shared - @importable = importable - @relation_reader = relation_reader - @members_mapper = members_mapper - @object_builder = object_builder - @relation_factory = relation_factory - @reader = reader - @importable_attributes = importable_attributes - @importable_path = importable_path - end - - def restore - ActiveRecord::Base.uncached do - ActiveRecord::Base.no_touching do - update_params! - - BulkInsertableAssociations.with_bulk_insert(enabled: project?) do - fix_ci_pipelines_not_sorted_on_legacy_project_json! - create_relations! - end - end - end - - # ensure that we have latest version of the restore - @importable.reload # rubocop:disable Cop/ActiveRecordAssociationReload - - true - rescue StandardError => e - @shared.error(e) - false - end - - private - - def project? - @importable.instance_of?(::Project) - end - - # Loops through the tree of models defined in import_export.yml and - # finds them in the imported JSON so they can be instantiated and saved - # in the DB. The structure and relationships between models are guessed from - # the configuration yaml file too. - # Finally, it updates each attribute in the newly imported project/group. - def create_relations! - relations.each do |relation_key, relation_definition| - process_relation!(relation_key, relation_definition) - end - end - - def process_relation!(relation_key, relation_definition) - @relation_reader.consume_relation(@importable_path, relation_key).each do |data_hash, relation_index| - process_relation_item!(relation_key, relation_definition, relation_index, data_hash) - end - end - - def process_relation_item!(relation_key, relation_definition, relation_index, data_hash) - relation_object = build_relation(relation_key, relation_definition, relation_index, data_hash) - return unless relation_object - return if project? && group_model?(relation_object) - - relation_object.assign_attributes(importable_class_sym => @importable) - - import_failure_service.with_retry(action: 'relation_object.save!', relation_key: relation_key, relation_index: relation_index) do - relation_object.save! - log_relation_creation(@importable, relation_key, relation_object) - end - rescue StandardError => e - import_failure_service.log_import_failure( - source: 'process_relation_item!', - relation_key: relation_key, - relation_index: relation_index, - exception: e) - end - - def import_failure_service - @import_failure_service ||= ImportFailureService.new(@importable) - end - - def relations - @relations ||= - @reader - .attributes_finder - .find_relations_tree(importable_class_sym) - .deep_stringify_keys - end - - def update_params! - params = @importable_attributes.except(*relations.keys.map(&:to_s)) - params = params.merge(present_override_params) - - # Cleaning all imported and overridden params - params = Gitlab::ImportExport::AttributeCleaner.clean( - relation_hash: params, - relation_class: importable_class, - excluded_keys: excluded_keys_for_relation(importable_class_sym)) - - @importable.assign_attributes(params) - - modify_attributes - - Gitlab::Timeless.timeless(@importable) do - @importable.save! - end - end - - def present_override_params - # we filter out the empty strings from the overrides - # keeping the default values configured - override_params&.transform_values do |value| - value.is_a?(String) ? value.presence : value - end&.compact - end - - def override_params - @importable_override_params ||= importable_override_params - end - - def importable_override_params - if @importable.respond_to?(:import_data) - @importable.import_data&.data&.fetch('override_params', nil) || {} - else - {} - end - end - - def modify_attributes - return unless project? - - @importable.reconcile_shared_runners_setting! - @importable.drop_visibility_level! - end - - def build_relations(relation_key, relation_definition, relation_index, data_hashes) - data_hashes - .map { |data_hash| build_relation(relation_key, relation_definition, relation_index, data_hash) } - .tap { |entries| entries.compact! } - end - - def build_relation(relation_key, relation_definition, relation_index, data_hash) - # TODO: This is hack to not create relation for the author - # Rather make `RelationFactory#set_note_author` to take care of that - return data_hash if relation_key == 'author' || already_restored?(data_hash) - - # create relation objects recursively for all sub-objects - relation_definition.each do |sub_relation_key, sub_relation_definition| - transform_sub_relations!(data_hash, sub_relation_key, sub_relation_definition, relation_index) - end - - relation = @relation_factory.create(**relation_factory_params(relation_key, relation_index, data_hash)) - - if relation && !relation.valid? - @shared.logger.warn( - message: "[Project/Group Import] Invalid object relation built", - relation_key: relation_key, - relation_index: relation_index, - relation_class: relation.class.name, - error_messages: relation.errors.full_messages.join(". ") - ) - end - - relation - end - - # Since we update the data hash in place as we restore relation items, - # and since we also de-duplicate items, we might encounter items that - # have already been restored in a previous iteration. - def already_restored?(relation_item) - !relation_item.is_a?(Hash) - end - - def transform_sub_relations!(data_hash, sub_relation_key, sub_relation_definition, relation_index) - sub_data_hash = data_hash[sub_relation_key] - return unless sub_data_hash - - # if object is a hash we can create simple object - # as it means that this is 1-to-1 vs 1-to-many - current_item = - if sub_data_hash.is_a?(Array) - build_relations( - sub_relation_key, - sub_relation_definition, - relation_index, - sub_data_hash).presence - else - build_relation( - sub_relation_key, - sub_relation_definition, - relation_index, - sub_data_hash) - end - - if current_item - data_hash[sub_relation_key] = current_item - else - data_hash.delete(sub_relation_key) - end - end - - def group_model?(relation_object) - GROUP_MODELS.include?(relation_object.class) && relation_object.group_id - end - - def excluded_keys_for_relation(relation) - @reader.attributes_finder.find_excluded_keys(relation) - end - - def importable_class - @importable.class - end - - def importable_class_sym - importable_class.to_s.downcase.to_sym - end - - def relation_factory_params(relation_key, relation_index, data_hash) - { - relation_index: relation_index, - relation_sym: relation_key.to_sym, - relation_hash: data_hash, - importable: @importable, - members_mapper: @members_mapper, - object_builder: @object_builder, - user: @user, - excluded_keys: excluded_keys_for_relation(relation_key) - } - end - - # Temporary fix for https://gitlab.com/gitlab-org/gitlab/-/issues/27883 when import from legacy project.json - # This should be removed once legacy JSON format is deprecated. - # Ndjson export file will fix the order during project export. - def fix_ci_pipelines_not_sorted_on_legacy_project_json! - return unless relation_reader.legacy? - - relation_reader.sort_ci_pipelines_by_id - end - - # Enable logging of each top-level relation creation when Importing - # into a Group if feature flag is enabled - def log_relation_creation(importable, relation_key, relation_object) - root_ancestor_group = importable.try(:root_ancestor) - - return unless root_ancestor_group - return unless root_ancestor_group.instance_of?(::Group) - return unless Feature.enabled?(:log_import_export_relation_creation, root_ancestor_group) - - @shared.logger.info( - importable_type: importable.class.to_s, - importable_id: importable.id, - relation_key: relation_key, - relation_id: relation_object.id, - author_id: relation_object.try(:author_id), - message: '[Project/Group Import] Created new object relation' - ) - end - end - end -end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 81a293ac667..658c60aac03 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -25525,9 +25525,6 @@ msgstr "" msgid "Pipeline|In progress" msgstr "" -msgid "Pipeline|Key" -msgstr "" - msgid "Pipeline|Manual" msgstr "" @@ -25618,9 +25615,6 @@ msgstr "" msgid "Pipeline|Triggerer" msgstr "" -msgid "Pipeline|Value" -msgstr "" - msgid "Pipeline|Variables" msgstr "" @@ -27391,6 +27385,9 @@ msgstr "" msgid "Projects with no vulnerabilities and security scanning enabled" msgstr "" +msgid "Projects with this topic" +msgstr "" + msgid "Projects with write access" msgstr "" diff --git a/package.json b/package.json index 778da296e37..b777366412e 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "apollo-upload-client": "^13.0.0", "autosize": "^5.0.1", "aws-sdk": "^2.637.0", - "axios": "^0.20.0", + "axios": "^0.24.0", "babel-loader": "^8.2.2", "babel-plugin-lodash": "^3.3.4", "bootstrap": "4.5.3", @@ -219,8 +219,8 @@ "docdash": "^1.0.2", "eslint": "7.32.0", "eslint-import-resolver-jest": "3.0.2", - "eslint-import-resolver-webpack": "0.13.1", - "eslint-plugin-no-jquery": "2.6.0", + "eslint-import-resolver-webpack": "0.13.2", + "eslint-plugin-no-jquery": "2.7.0", "gettext-extractor": "^3.5.3", "gettext-extractor-vue": "^5.0.0", "glob": "^7.1.6", diff --git a/spec/controllers/admin/integrations_controller_spec.rb b/spec/controllers/admin/integrations_controller_spec.rb index 1793b3a86d1..cf6a6385425 100644 --- a/spec/controllers/admin/integrations_controller_spec.rb +++ b/spec/controllers/admin/integrations_controller_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Admin::IntegrationsController do sign_in(admin) end - it_behaves_like IntegrationsActions do + it_behaves_like Integrations::Actions do let(:integration_attributes) { { instance: true, project: nil } } let(:routing_params) do diff --git a/spec/controllers/explore/projects_controller_spec.rb b/spec/controllers/explore/projects_controller_spec.rb index 2297198878d..f2328303102 100644 --- a/spec/controllers/explore/projects_controller_spec.rb +++ b/spec/controllers/explore/projects_controller_spec.rb @@ -74,6 +74,28 @@ RSpec.describe Explore::ProjectsController do end end end + + describe 'GET #topic' do + context 'when topic does not exist' do + it 'renders a 404 error' do + get :topic, params: { topic_name: 'topic1' } + + expect(response).to have_gitlab_http_status(:not_found) + end + end + context 'when topic exists' do + before do + create(:topic, name: 'topic1') + end + + it 'renders the template' do + get :topic, params: { topic_name: 'topic1' } + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template('topic') + end + end + end end shared_examples "blocks high page numbers" do diff --git a/spec/controllers/groups/settings/integrations_controller_spec.rb b/spec/controllers/groups/settings/integrations_controller_spec.rb index 31d1946652d..9ede6d0b9e9 100644 --- a/spec/controllers/groups/settings/integrations_controller_spec.rb +++ b/spec/controllers/groups/settings/integrations_controller_spec.rb @@ -10,7 +10,7 @@ RSpec.describe Groups::Settings::IntegrationsController do sign_in(user) end - it_behaves_like IntegrationsActions do + it_behaves_like Integrations::Actions do let(:integration_attributes) { { group: group, project: nil } } let(:routing_params) do diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb index 482ba552f8f..79784bad67e 100644 --- a/spec/controllers/projects/services_controller_spec.rb +++ b/spec/controllers/projects/services_controller_spec.rb @@ -18,7 +18,7 @@ RSpec.describe Projects::ServicesController do project.add_maintainer(user) end - it_behaves_like IntegrationsActions do + it_behaves_like Integrations::Actions do let(:integration_attributes) { { project: project } } let(:routing_params) do diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb index 27419479479..3b0c88687d8 100644 --- a/spec/features/dashboard/projects_spec.rb +++ b/spec/features/dashboard/projects_spec.rb @@ -204,7 +204,7 @@ RSpec.describe 'Dashboard Projects' do visit dashboard_projects_path expect(page).to have_selector('[data-testid="project_topic_list"]') - expect(page).to have_link('topic1', href: explore_projects_path(topic: 'topic1')) + expect(page).to have_link('topic1', href: topic_explore_projects_path(topic_name: 'topic1')) end end diff --git a/spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb b/spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb new file mode 100644 index 00000000000..e8a14694d88 --- /dev/null +++ b/spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'User triggers manual job with variables', :js do + let(:user) { create(:user) } + let(:user_access_level) { :developer } + let(:project) { create(:project, :repository, namespace: user.namespace) } + let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.sha, ref: 'master') } + let!(:build) { create(:ci_build, :manual, pipeline: pipeline) } + + before do + project.add_maintainer(user) + project.enable_ci + + sign_in(user) + + visit(project_job_path(project, build)) + end + + it 'passes values correctly' do + page.within(find("[data-testid='ci-variable-row']")) do + find("[data-testid='ci-variable-key']").set('key_name') + find("[data-testid='ci-variable-value']").set('key_value') + end + + find("[data-testid='trigger-manual-job-btn']").click + + wait_for_requests + + expect(build.job_variables.as_json).to contain_exactly( + hash_including('key' => 'key_name', 'value' => 'key_value')) + end +end diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index 707769d9f25..fec2873e4fd 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -133,7 +133,7 @@ RSpec.describe 'Project' do visit path expect(page).to have_selector('[data-testid="project_topic_list"]') - expect(page).to have_link('topic1', href: explore_projects_path(topic: 'topic1')) + expect(page).to have_link('topic1', href: topic_explore_projects_path(topic_name: 'topic1')) end it 'shows up to 3 project topics' do @@ -142,9 +142,9 @@ RSpec.describe 'Project' do visit path expect(page).to have_selector('[data-testid="project_topic_list"]') - expect(page).to have_link('topic1', href: explore_projects_path(topic: 'topic1')) - expect(page).to have_link('topic2', href: explore_projects_path(topic: 'topic2')) - expect(page).to have_link('topic3', href: explore_projects_path(topic: 'topic3')) + expect(page).to have_link('topic1', href: topic_explore_projects_path(topic_name: 'topic1')) + expect(page).to have_link('topic2', href: topic_explore_projects_path(topic_name: 'topic2')) + expect(page).to have_link('topic3', href: topic_explore_projects_path(topic_name: 'topic3')) expect(page).to have_content('+ 1 more') end end diff --git a/spec/features/topic_show_spec.rb b/spec/features/topic_show_spec.rb new file mode 100644 index 00000000000..3a9865a6503 --- /dev/null +++ b/spec/features/topic_show_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Topic show page' do + let_it_be(:topic) { create(:topic, name: 'my-topic', description: 'This is **my** topic https://google.com/ :poop: ```\ncode\n```', avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) } + + context 'when topic does not exist' do + let(:path) { topic_explore_projects_path(topic_name: 'non-existing') } + + it 'renders 404' do + visit path + + expect(status_code).to eq(404) + end + end + + context 'when topic exists' do + before do + visit topic_explore_projects_path(topic_name: topic.name) + end + + it 'shows name, avatar and description as markdown' do + expect(page).to have_content(topic.name) + expect(page).to have_selector('.avatar-container > img.topic-avatar') + expect(find('.topic-description')).to have_selector('p > strong') + expect(find('.topic-description')).to have_selector('p > a[rel]') + expect(find('.topic-description')).to have_selector('p > gl-emoji') + expect(find('.topic-description')).to have_selector('p > code') + end + + context 'with associated projects' do + let!(:project) { create(:project, :public, topic_list: topic.name) } + + it 'shows project list' do + visit topic_explore_projects_path(topic_name: topic.name) + + expect(find('.projects-list .project-name')).to have_content(project.name) + end + end + + context 'without associated projects' do + it 'shows correct empty state message' do + expect(page).to have_content('Explore public groups to find projects to contribute to.') + end + end + end +end diff --git a/spec/frontend/custom_metrics/components/custom_metrics_form_fields_spec.js b/spec/frontend/custom_metrics/components/custom_metrics_form_fields_spec.js index c41adf523f8..2001f5c1441 100644 --- a/spec/frontend/custom_metrics/components/custom_metrics_form_fields_spec.js +++ b/spec/frontend/custom_metrics/components/custom_metrics_form_fields_spec.js @@ -4,8 +4,6 @@ import { TEST_HOST } from 'helpers/test_constants'; import CustomMetricsFormFields from '~/custom_metrics/components/custom_metrics_form_fields.vue'; import axios from '~/lib/utils/axios_utils'; -const { CancelToken } = axios; - describe('custom metrics form fields component', () => { let wrapper; let mockAxios; @@ -116,14 +114,14 @@ describe('custom metrics form fields component', () => { it('receives and validates a persisted value', () => { const query = 'persistedQuery'; - const axiosPost = jest.spyOn(axios, 'post'); - const source = CancelToken.source(); + jest.spyOn(axios, 'post'); + mountComponent({ metricPersisted: true, ...makeFormData({ query }) }); - expect(axiosPost).toHaveBeenCalledWith( + expect(axios.post).toHaveBeenCalledWith( validateQueryPath, { query }, - { cancelToken: source.token }, + expect.objectContaining({ cancelToken: expect.anything() }), ); expect(getNamedInput(queryInputName).value).toBe(query); jest.runAllTimers(); diff --git a/spec/frontend/jobs/components/manual_variables_form_spec.js b/spec/frontend/jobs/components/manual_variables_form_spec.js index 7e42ee957d3..a5278af8e33 100644 --- a/spec/frontend/jobs/components/manual_variables_form_spec.js +++ b/spec/frontend/jobs/components/manual_variables_form_spec.js @@ -1,9 +1,9 @@ import { GlSprintf, GlLink } from '@gitlab/ui'; -import { createLocalVue, mount, shallowMount } from '@vue/test-utils'; -import Vue from 'vue'; +import { createLocalVue, mount } from '@vue/test-utils'; +import Vue, { nextTick } from 'vue'; import Vuex from 'vuex'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; -import Form from '~/jobs/components/manual_variables_form.vue'; +import ManualVariablesForm from '~/jobs/components/manual_variables_form.vue'; const localVue = createLocalVue(); @@ -21,7 +21,7 @@ describe('Manual Variables Form', () => { }, }; - const createComponent = ({ props = {}, mountFn = shallowMount } = {}) => { + const createComponent = (props = {}) => { store = new Vuex.Store({ actions: { triggerManualJob: jest.fn(), @@ -29,7 +29,7 @@ describe('Manual Variables Form', () => { }); wrapper = extendedWrapper( - mountFn(localVue.extend(Form), { + mount(localVue.extend(ManualVariablesForm), { propsData: { ...requiredProps, ...props }, localVue, store, @@ -40,88 +40,120 @@ describe('Manual Variables Form', () => { ); }; - const findInputKey = () => wrapper.findComponent({ ref: 'inputKey' }); - const findInputValue = () => wrapper.findComponent({ ref: 'inputSecretValue' }); const findHelpText = () => wrapper.findComponent(GlSprintf); const findHelpLink = () => wrapper.findComponent(GlLink); const findTriggerBtn = () => wrapper.findByTestId('trigger-manual-job-btn'); const findDeleteVarBtn = () => wrapper.findByTestId('delete-variable-btn'); + const findAllDeleteVarBtns = () => wrapper.findAllByTestId('delete-variable-btn'); + const findDeleteVarBtnPlaceholder = () => wrapper.findByTestId('delete-variable-btn-placeholder'); const findCiVariableKey = () => wrapper.findByTestId('ci-variable-key'); + const findAllCiVariableKeys = () => wrapper.findAllByTestId('ci-variable-key'); const findCiVariableValue = () => wrapper.findByTestId('ci-variable-value'); const findAllVariables = () => wrapper.findAllByTestId('ci-variable-row'); + const setCiVariableKey = () => { + findCiVariableKey().setValue('new key'); + findCiVariableKey().vm.$emit('change'); + nextTick(); + }; + + const setCiVariableKeyByPosition = (position, value) => { + findAllCiVariableKeys().at(position).setValue(value); + findAllCiVariableKeys().at(position).vm.$emit('change'); + nextTick(); + }; + + beforeEach(() => { + createComponent(); + }); + afterEach(() => { wrapper.destroy(); }); - describe('shallowMount', () => { - beforeEach(() => { - createComponent(); - }); + it('creates a new variable when user enters a new key value', async () => { + expect(findAllVariables()).toHaveLength(1); - it('renders empty form with correct placeholders', () => { - expect(findInputKey().attributes('placeholder')).toBe('Input variable key'); - expect(findInputValue().attributes('placeholder')).toBe('Input variable value'); - }); + await setCiVariableKey(); - it('renders help text with provided link', () => { - expect(findHelpText().exists()).toBe(true); - expect(findHelpLink().attributes('href')).toBe( - '/help/ci/variables/index#add-a-cicd-variable-to-a-project', - ); - }); - - describe('when adding a new variable', () => { - it('creates a new variable when user types a new key and resets the form', async () => { - await findInputKey().setValue('new key'); - - expect(findAllVariables()).toHaveLength(1); - expect(findCiVariableKey().element.value).toBe('new key'); - expect(findInputKey().attributes('value')).toBe(undefined); - }); - - it('creates a new variable when user types a new value and resets the form', async () => { - await findInputValue().setValue('new value'); - - expect(findAllVariables()).toHaveLength(1); - expect(findCiVariableValue().element.value).toBe('new value'); - expect(findInputValue().attributes('value')).toBe(undefined); - }); - }); + expect(findAllVariables()).toHaveLength(2); }); - describe('mount', () => { - beforeEach(() => { - createComponent({ mountFn: mount }); - }); + it('does not create extra empty variables', async () => { + expect(findAllVariables()).toHaveLength(1); - describe('when deleting a variable', () => { - it('removes the variable row', async () => { - await wrapper.setData({ - variables: [ - { - key: 'new key', - secret_value: 'value', - id: '1', - }, - ], - }); + await setCiVariableKey(); - findDeleteVarBtn().trigger('click'); + expect(findAllVariables()).toHaveLength(2); - await wrapper.vm.$nextTick(); + await setCiVariableKey(); - expect(findAllVariables()).toHaveLength(0); - }); - }); + expect(findAllVariables()).toHaveLength(2); + }); - it('trigger button is disabled after trigger action', async () => { - expect(findTriggerBtn().props('disabled')).toBe(false); + it('removes the correct variable row', async () => { + const variableKeyNameOne = 'key-one'; + const variableKeyNameThree = 'key-three'; - await findTriggerBtn().trigger('click'); + await setCiVariableKeyByPosition(0, variableKeyNameOne); - expect(findTriggerBtn().props('disabled')).toBe(true); - }); + await setCiVariableKeyByPosition(1, 'key-two'); + + await setCiVariableKeyByPosition(2, variableKeyNameThree); + + expect(findAllVariables()).toHaveLength(4); + + await findAllDeleteVarBtns().at(1).trigger('click'); + + expect(findAllVariables()).toHaveLength(3); + + expect(findAllCiVariableKeys().at(0).element.value).toBe(variableKeyNameOne); + expect(findAllCiVariableKeys().at(1).element.value).toBe(variableKeyNameThree); + expect(findAllCiVariableKeys().at(2).element.value).toBe(''); + }); + + it('trigger button is disabled after trigger action', async () => { + expect(findTriggerBtn().props('disabled')).toBe(false); + + await findTriggerBtn().trigger('click'); + + expect(findTriggerBtn().props('disabled')).toBe(true); + }); + + it('delete variable button should only show when there is more than one variable', async () => { + expect(findDeleteVarBtn().exists()).toBe(false); + + await setCiVariableKey(); + + expect(findDeleteVarBtn().exists()).toBe(true); + }); + + it('delete variable button placeholder should only exist when a user cannot remove', async () => { + expect(findDeleteVarBtnPlaceholder().exists()).toBe(true); + }); + + it('renders help text with provided link', () => { + expect(findHelpText().exists()).toBe(true); + expect(findHelpLink().attributes('href')).toBe( + '/help/ci/variables/index#add-a-cicd-variable-to-a-project', + ); + }); + + it('passes variables in correct format', async () => { + jest.spyOn(store, 'dispatch'); + + await setCiVariableKey(); + + await findCiVariableValue().setValue('new value'); + + await findTriggerBtn().trigger('click'); + + expect(store.dispatch).toHaveBeenCalledWith('triggerManualJob', [ + { + key: 'new key', + secret_value: 'new value', + }, + ]); }); }); diff --git a/spec/lib/bulk_imports/projects/pipelines/external_pull_requests_pipeline_spec.rb b/spec/lib/bulk_imports/projects/pipelines/external_pull_requests_pipeline_spec.rb new file mode 100644 index 00000000000..8f610fcc2ae --- /dev/null +++ b/spec/lib/bulk_imports/projects/pipelines/external_pull_requests_pipeline_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe BulkImports::Projects::Pipelines::ExternalPullRequestsPipeline do + let_it_be(:project) { create(:project) } + let_it_be(:bulk_import) { create(:bulk_import) } + let_it_be(:entity) { create(:bulk_import_entity, :project_entity, project: project, bulk_import: bulk_import) } + let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) } + let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) } + + let(:attributes) { {} } + let(:external_pr) { project.external_pull_requests.last } + let(:external_pull_request) do + { + 'pull_request_iid' => 4, + 'source_branch' => 'feature', + 'target_branch' => 'main', + 'source_repository' => 'repository', + 'target_repository' => 'repository', + 'source_sha' => 'abc', + 'target_sha' => 'xyz', + 'status' => 'open', + 'created_at' => '2019-12-24T14:04:50.053Z', + 'updated_at' => '2019-12-24T14:05:18.138Z' + }.merge(attributes) + end + + subject(:pipeline) { described_class.new(context) } + + describe '#run' do + before do + allow_next_instance_of(BulkImports::Common::Extractors::NdjsonExtractor) do |extractor| + allow(extractor).to receive(:remove_tmp_dir) + allow(extractor).to receive(:extract).and_return(BulkImports::Pipeline::ExtractedData.new(data: [[external_pull_request, 0]])) + end + + pipeline.run + end + + it 'imports external pull request', :aggregate_failures do + expect(external_pr.pull_request_iid).to eq(external_pull_request['pull_request_iid']) + expect(external_pr.source_branch).to eq(external_pull_request['source_branch']) + expect(external_pr.target_branch).to eq(external_pull_request['target_branch']) + expect(external_pr.status).to eq(external_pull_request['status']) + expect(external_pr.created_at).to eq(external_pull_request['created_at']) + expect(external_pr.updated_at).to eq(external_pull_request['updated_at']) + end + + context 'when status is closed' do + let(:attributes) { { 'status' => 'closed' } } + + it 'imports closed external pull request' do + expect(external_pr.status).to eq(attributes['status']) + end + end + + context 'when from fork' do + let(:attributes) { { 'source_repository' => 'source' } } + + it 'does not create external pull request' do + expect(external_pr).to be_nil + end + end + end +end diff --git a/spec/lib/bulk_imports/projects/stage_spec.rb b/spec/lib/bulk_imports/projects/stage_spec.rb index 8b35ed8bc38..60c989d9896 100644 --- a/spec/lib/bulk_imports/projects/stage_spec.rb +++ b/spec/lib/bulk_imports/projects/stage_spec.rb @@ -11,6 +11,7 @@ RSpec.describe BulkImports::Projects::Stage do [3, BulkImports::Projects::Pipelines::IssuesPipeline], [4, BulkImports::Common::Pipelines::BoardsPipeline], [4, BulkImports::Projects::Pipelines::MergeRequestsPipeline], + [4, BulkImports::Projects::Pipelines::ExternalPullRequestsPipeline], [5, BulkImports::Common::Pipelines::UploadsPipeline], [6, BulkImports::Common::Pipelines::EntityFinisher] ] diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb index 3ec4519748f..ebfa2388152 100644 --- a/spec/lib/gitlab/ci/config_spec.rb +++ b/spec/lib/gitlab/ci/config_spec.rb @@ -14,7 +14,7 @@ RSpec.describe Gitlab::Ci::Config do end let(:config) do - described_class.new(yml, project: nil, sha: nil, user: nil) + described_class.new(yml, project: nil, pipeline: nil, sha: nil, user: nil) end context 'when config is valid' do @@ -286,9 +286,12 @@ RSpec.describe Gitlab::Ci::Config do end context "when using 'include' directive" do - let(:group) { create(:group) } + let_it_be(:group) { create(:group) } + let(:project) { create(:project, :repository, group: group) } let(:main_project) { create(:project, :repository, :public, group: group) } + let(:pipeline) { build(:ci_pipeline, project: project) } + let(:remote_location) { 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.gitlab-ci-1.yml' } let(:local_location) { 'spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml' } @@ -327,7 +330,7 @@ RSpec.describe Gitlab::Ci::Config do end let(:config) do - described_class.new(gitlab_ci_yml, project: project, sha: '12345', user: user) + described_class.new(gitlab_ci_yml, project: project, pipeline: pipeline, sha: '12345', user: user) end before do @@ -724,7 +727,7 @@ RSpec.describe Gitlab::Ci::Config do end end - context "when an 'include' has rules" do + context "when an 'include' has rules with a project variable" do let(:gitlab_ci_yml) do <<~HEREDOC include: @@ -751,5 +754,30 @@ RSpec.describe Gitlab::Ci::Config do end end end + + context "when an 'include' has rules with a pipeline variable" do + let(:gitlab_ci_yml) do + <<~HEREDOC + include: + - local: #{local_location} + rules: + - if: $CI_COMMIT_SHA == "#{project.commit.sha}" + HEREDOC + end + + context 'when a pipeline is passed' do + it 'includes the file' do + expect(config.to_hash).to include(local_location_hash) + end + end + + context 'when a pipeline is not passed' do + let(:pipeline) { nil } + + it 'does not include the file' do + expect(config.to_hash).not_to include(local_location_hash) + end + end + end end end diff --git a/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb new file mode 100644 index 00000000000..473dbf5ecc5 --- /dev/null +++ b/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +# This spec is a lightweight version of: +# * project/tree_restorer_spec.rb +# +# In depth testing is being done in the above specs. +# This spec tests that restore project works +# but does not have 100% relation coverage. + +require 'spec_helper' + +RSpec.describe Gitlab::ImportExport::Group::RelationTreeRestorer do + let_it_be(:group) { create(:group) } + let_it_be(:importable) { create(:group, parent: group) } + + include_context 'relation tree restorer shared context' do + let(:importable_name) { nil } + end + + let(:path) { 'spec/fixtures/lib/gitlab/import_export/group_exports/no_children/group.json' } + let(:relation_reader) do + Gitlab::ImportExport::Json::LegacyReader::File.new( + path, + relation_names: reader.group_relation_names) + end + + let(:reader) do + Gitlab::ImportExport::Reader.new( + shared: shared, + config: Gitlab::ImportExport::Config.new(config: Gitlab::ImportExport.legacy_group_config_file).to_h + ) + end + + let(:relation_tree_restorer) do + described_class.new( + user: user, + shared: shared, + relation_reader: relation_reader, + object_builder: Gitlab::ImportExport::Group::ObjectBuilder, + members_mapper: members_mapper, + relation_factory: Gitlab::ImportExport::Group::RelationFactory, + reader: reader, + importable: importable, + importable_path: nil, + importable_attributes: attributes + ) + end + + subject { relation_tree_restorer.restore } + + shared_examples 'logging of relations creation' do + context 'when log_import_export_relation_creation feature flag is enabled' do + before do + stub_feature_flags(log_import_export_relation_creation: group) + end + + it 'logs top-level relation creation' do + expect(shared.logger) + .to receive(:info) + .with(hash_including(message: '[Project/Group Import] Created new object relation')) + .at_least(:once) + + subject + end + end + + context 'when log_import_export_relation_creation feature flag is disabled' do + before do + stub_feature_flags(log_import_export_relation_creation: false) + end + + it 'does not log top-level relation creation' do + expect(shared.logger) + .to receive(:info) + .with(hash_including(message: '[Project/Group Import] Created new object relation')) + .never + + subject + end + end + end + + it 'restores group tree' do + expect(subject).to eq(true) + end + + include_examples 'logging of relations creation' +end diff --git a/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb new file mode 100644 index 00000000000..5ebace263ba --- /dev/null +++ b/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb @@ -0,0 +1,150 @@ +# frozen_string_literal: true + +# This spec is a lightweight version of: +# * project/tree_restorer_spec.rb +# +# In depth testing is being done in the above specs. +# This spec tests that restore project works +# but does not have 100% relation coverage. + +require 'spec_helper' + +RSpec.describe Gitlab::ImportExport::Project::RelationTreeRestorer do + let_it_be(:importable, reload: true) do + create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project') + end + + include_context 'relation tree restorer shared context' do + let(:importable_name) { 'project' } + end + + let(:reader) { Gitlab::ImportExport::Reader.new(shared: shared) } + let(:relation_tree_restorer) do + described_class.new( + user: user, + shared: shared, + relation_reader: relation_reader, + object_builder: Gitlab::ImportExport::Project::ObjectBuilder, + members_mapper: members_mapper, + relation_factory: Gitlab::ImportExport::Project::RelationFactory, + reader: reader, + importable: importable, + importable_path: 'project', + importable_attributes: attributes + ) + end + + subject { relation_tree_restorer.restore } + + shared_examples 'import project successfully' do + describe 'imported project' do + it 'has the project attributes and relations', :aggregate_failures do + expect(subject).to eq(true) + + project = Project.find_by_path('project') + + expect(project.description).to eq('Nisi et repellendus ut enim quo accusamus vel magnam.') + expect(project.labels.count).to eq(3) + expect(project.boards.count).to eq(1) + expect(project.project_feature).not_to be_nil + expect(project.custom_attributes.count).to eq(2) + expect(project.project_badges.count).to eq(2) + expect(project.snippets.count).to eq(1) + end + end + end + + shared_examples 'logging of relations creation' do + context 'when log_import_export_relation_creation feature flag is enabled' do + before do + stub_feature_flags(log_import_export_relation_creation: group) + end + + it 'logs top-level relation creation' do + expect(shared.logger) + .to receive(:info) + .with(hash_including(message: '[Project/Group Import] Created new object relation')) + .at_least(:once) + + subject + end + end + + context 'when log_import_export_relation_creation feature flag is disabled' do + before do + stub_feature_flags(log_import_export_relation_creation: false) + end + + it 'does not log top-level relation creation' do + expect(shared.logger) + .to receive(:info) + .with(hash_including(message: '[Project/Group Import] Created new object relation')) + .never + + subject + end + end + end + + context 'with legacy reader' do + let(:path) { 'spec/fixtures/lib/gitlab/import_export/complex/project.json' } + let(:relation_reader) do + Gitlab::ImportExport::Json::LegacyReader::File.new( + path, + relation_names: reader.project_relation_names, + allowed_path: 'project' + ) + end + + let(:attributes) { relation_reader.consume_attributes('project') } + + it_behaves_like 'import project successfully' + + context 'with logging of relations creation' do + let_it_be(:group) { create(:group) } + let_it_be(:importable) do + create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project', group: group) + end + + include_examples 'logging of relations creation' + end + end + + context 'with ndjson reader' do + let(:path) { 'spec/fixtures/lib/gitlab/import_export/complex/tree' } + let(:relation_reader) { Gitlab::ImportExport::Json::NdjsonReader.new(path) } + + it_behaves_like 'import project successfully' + + context 'when inside a group' do + let_it_be(:group) do + create(:group, :disabled_and_unoverridable) + end + + before do + importable.update!(shared_runners_enabled: false, group: group) + end + + it_behaves_like 'import project successfully' + end + end + + context 'with invalid relations' do + let(:path) { 'spec/fixtures/lib/gitlab/import_export/project_with_invalid_relations/tree' } + let(:relation_reader) { Gitlab::ImportExport::Json::NdjsonReader.new(path) } + + it 'logs the invalid relation and its errors' do + expect(shared.logger) + .to receive(:warn) + .with( + error_messages: "Title can't be blank. Title is invalid", + message: '[Project/Group Import] Invalid object relation built', + relation_class: 'ProjectLabel', + relation_index: 0, + relation_key: 'labels' + ).once + + relation_tree_restorer.restore + end + end +end diff --git a/spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb index f6a028383f2..3dab84af744 100644 --- a/spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb @@ -10,19 +10,26 @@ require 'spec_helper' RSpec.describe Gitlab::ImportExport::Project::Sample::RelationTreeRestorer do - include_context 'relation tree restorer shared context' + let_it_be(:importable) { create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project') } + include_context 'relation tree restorer shared context' do + let(:importable_name) { 'project' } + end + + let(:reader) { Gitlab::ImportExport::Reader.new(shared: shared) } + let(:path) { 'spec/fixtures/lib/gitlab/import_export/sample_data/tree' } + let(:relation_reader) { Gitlab::ImportExport::Json::NdjsonReader.new(path) } let(:sample_data_relation_tree_restorer) do described_class.new( user: user, shared: shared, relation_reader: relation_reader, - object_builder: object_builder, + object_builder: Gitlab::ImportExport::Project::ObjectBuilder, members_mapper: members_mapper, - relation_factory: relation_factory, + relation_factory: Gitlab::ImportExport::Project::Sample::RelationFactory, reader: reader, importable: importable, - importable_path: importable_path, + importable_path: 'project', importable_attributes: attributes ) end @@ -69,32 +76,21 @@ RSpec.describe Gitlab::ImportExport::Project::Sample::RelationTreeRestorer do end end - context 'when restoring a project' do - let(:importable) { create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project') } - let(:importable_name) { 'project' } - let(:importable_path) { 'project' } - let(:object_builder) { Gitlab::ImportExport::Project::ObjectBuilder } - let(:relation_factory) { Gitlab::ImportExport::Project::Sample::RelationFactory } - let(:reader) { Gitlab::ImportExport::Reader.new(shared: shared) } - let(:path) { 'spec/fixtures/lib/gitlab/import_export/sample_data/tree' } - let(:relation_reader) { Gitlab::ImportExport::Json::NdjsonReader.new(path) } + it 'initializes relation_factory with date_calculator as parameter' do + expect(Gitlab::ImportExport::Project::Sample::RelationFactory).to receive(:create).with(hash_including(:date_calculator)).at_least(:once).times - it 'initializes relation_factory with date_calculator as parameter' do - expect(Gitlab::ImportExport::Project::Sample::RelationFactory).to receive(:create).with(hash_including(:date_calculator)).at_least(:once).times + subject + end - subject - end + context 'when relation tree restorer is initialized' do + it 'initializes date calculator with due dates' do + expect(Gitlab::ImportExport::Project::Sample::DateCalculator).to receive(:new).with(Array) - context 'when relation tree restorer is initialized' do - it 'initializes date calculator with due dates' do - expect(Gitlab::ImportExport::Project::Sample::DateCalculator).to receive(:new).with(Array) - - sample_data_relation_tree_restorer - end - end - - context 'using ndjson reader' do - it_behaves_like 'import project successfully' + sample_data_relation_tree_restorer end end + + context 'using ndjson reader' do + it_behaves_like 'import project successfully' + end end diff --git a/spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb deleted file mode 100644 index 5e4075c2b59..00000000000 --- a/spec/lib/gitlab/import_export/relation_tree_restorer_spec.rb +++ /dev/null @@ -1,184 +0,0 @@ -# frozen_string_literal: true - -# This spec is a lightweight version of: -# * project/tree_restorer_spec.rb -# -# In depth testing is being done in the above specs. -# This spec tests that restore project works -# but does not have 100% relation coverage. - -require 'spec_helper' - -RSpec.describe Gitlab::ImportExport::RelationTreeRestorer do - include_context 'relation tree restorer shared context' - - let(:relation_tree_restorer) do - described_class.new( - user: user, - shared: shared, - relation_reader: relation_reader, - object_builder: object_builder, - members_mapper: members_mapper, - relation_factory: relation_factory, - reader: reader, - importable: importable, - importable_path: importable_path, - importable_attributes: attributes - ) - end - - subject { relation_tree_restorer.restore } - - shared_examples 'import project successfully' do - describe 'imported project' do - it 'has the project attributes and relations', :aggregate_failures do - expect(subject).to eq(true) - - project = Project.find_by_path('project') - - expect(project.description).to eq('Nisi et repellendus ut enim quo accusamus vel magnam.') - expect(project.labels.count).to eq(3) - expect(project.boards.count).to eq(1) - expect(project.project_feature).not_to be_nil - expect(project.custom_attributes.count).to eq(2) - expect(project.project_badges.count).to eq(2) - expect(project.snippets.count).to eq(1) - end - end - end - - shared_examples 'logging of relations creation' do - context 'when log_import_export_relation_creation feature flag is enabled' do - before do - stub_feature_flags(log_import_export_relation_creation: group) - end - - it 'logs top-level relation creation' do - expect(relation_tree_restorer.shared.logger) - .to receive(:info) - .with(hash_including(message: '[Project/Group Import] Created new object relation')) - .at_least(:once) - - subject - end - end - - context 'when log_import_export_relation_creation feature flag is disabled' do - before do - stub_feature_flags(log_import_export_relation_creation: false) - end - - it 'does not log top-level relation creation' do - expect(relation_tree_restorer.shared.logger) - .to receive(:info) - .with(hash_including(message: '[Project/Group Import] Created new object relation')) - .never - - subject - end - end - end - - context 'when restoring a project' do - let_it_be(:importable, reload: true) do - create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project') - end - - let(:importable_name) { 'project' } - let(:importable_path) { 'project' } - let(:object_builder) { Gitlab::ImportExport::Project::ObjectBuilder } - let(:relation_factory) { Gitlab::ImportExport::Project::RelationFactory } - let(:reader) { Gitlab::ImportExport::Reader.new(shared: shared) } - - context 'using legacy reader' do - let(:path) { 'spec/fixtures/lib/gitlab/import_export/complex/project.json' } - let(:relation_reader) do - Gitlab::ImportExport::Json::LegacyReader::File.new( - path, - relation_names: reader.project_relation_names, - allowed_path: 'project' - ) - end - - let(:attributes) { relation_reader.consume_attributes('project') } - - it_behaves_like 'import project successfully' - - context 'logging of relations creation' do - let_it_be(:group) { create(:group) } - let_it_be(:importable) do - create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project', group: group) - end - - include_examples 'logging of relations creation' - end - end - - context 'using ndjson reader' do - let(:path) { 'spec/fixtures/lib/gitlab/import_export/complex/tree' } - let(:relation_reader) { Gitlab::ImportExport::Json::NdjsonReader.new(path) } - - it_behaves_like 'import project successfully' - - context 'when inside a group' do - let_it_be(:group) do - create(:group, :disabled_and_unoverridable) - end - - before do - importable.update!(shared_runners_enabled: false, group: group) - end - - it_behaves_like 'import project successfully' - end - end - - context 'with invalid relations' do - let(:path) { 'spec/fixtures/lib/gitlab/import_export/project_with_invalid_relations/tree' } - let(:relation_reader) { Gitlab::ImportExport::Json::NdjsonReader.new(path) } - - it 'logs the invalid relation and its errors' do - expect(relation_tree_restorer.shared.logger) - .to receive(:warn) - .with( - error_messages: "Title can't be blank. Title is invalid", - message: '[Project/Group Import] Invalid object relation built', - relation_class: 'ProjectLabel', - relation_index: 0, - relation_key: 'labels' - ).once - - relation_tree_restorer.restore - end - end - end - - context 'when restoring a group' do - let_it_be(:group) { create(:group) } - let_it_be(:importable) { create(:group, parent: group) } - - let(:path) { 'spec/fixtures/lib/gitlab/import_export/group_exports/no_children/group.json' } - let(:importable_name) { nil } - let(:importable_path) { nil } - let(:object_builder) { Gitlab::ImportExport::Group::ObjectBuilder } - let(:relation_factory) { Gitlab::ImportExport::Group::RelationFactory } - let(:relation_reader) do - Gitlab::ImportExport::Json::LegacyReader::File.new( - path, - relation_names: reader.group_relation_names) - end - - let(:reader) do - Gitlab::ImportExport::Reader.new( - shared: shared, - config: Gitlab::ImportExport::Config.new(config: Gitlab::ImportExport.legacy_group_config_file).to_h - ) - end - - it 'restores group tree' do - expect(subject).to eq(true) - end - - include_examples 'logging of relations creation' - end -end diff --git a/spec/services/ci/create_pipeline_service/include_spec.rb b/spec/services/ci/create_pipeline_service/include_spec.rb index 5e7dace8e15..aa01977272a 100644 --- a/spec/services/ci/create_pipeline_service/include_spec.rb +++ b/spec/services/ci/create_pipeline_service/include_spec.rb @@ -7,9 +7,11 @@ RSpec.describe Ci::CreatePipelineService do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.owner } - let(:ref) { 'refs/heads/master' } - let(:source) { :push } - let(:service) { described_class.new(project, user, { ref: ref }) } + let(:ref) { 'refs/heads/master' } + let(:variables_attributes) { [{ key: 'MYVAR', secret_value: 'hello' }] } + let(:source) { :push } + + let(:service) { described_class.new(project, user, { ref: ref, variables_attributes: variables_attributes }) } let(:pipeline) { service.execute(source).payload } let(:file_location) { 'spec/fixtures/gitlab/ci/external_files/.gitlab-ci-template-1.yml' } @@ -24,6 +26,20 @@ RSpec.describe Ci::CreatePipelineService do .and_return(File.read(Rails.root.join(file_location))) end + shared_examples 'not including the file' do + it 'does not include the job in the file' do + expect(pipeline).to be_created_successfully + expect(pipeline.processables.pluck(:name)).to contain_exactly('job') + end + end + + shared_examples 'including the file' do + it 'includes the job in the file' do + expect(pipeline).to be_created_successfully + expect(pipeline.processables.pluck(:name)).to contain_exactly('job', 'rspec') + end + end + context 'with a local file' do let(:config) do <<~EOY @@ -33,13 +49,10 @@ RSpec.describe Ci::CreatePipelineService do EOY end - it 'includes the job in the file' do - expect(pipeline).to be_created_successfully - expect(pipeline.processables.pluck(:name)).to contain_exactly('job', 'rspec') - end + it_behaves_like 'including the file' end - context 'with a local file with rules' do + context 'with a local file with rules with a project variable' do let(:config) do <<~EOY include: @@ -54,19 +67,63 @@ RSpec.describe Ci::CreatePipelineService do context 'when the rules matches' do let(:project_id) { project.id } - it 'includes the job in the file' do - expect(pipeline).to be_created_successfully - expect(pipeline.processables.pluck(:name)).to contain_exactly('job', 'rspec') - end + it_behaves_like 'including the file' end context 'when the rules does not match' do let(:project_id) { non_existing_record_id } - it 'does not include the job in the file' do - expect(pipeline).to be_created_successfully - expect(pipeline.processables.pluck(:name)).to contain_exactly('job') - end + it_behaves_like 'not including the file' + end + end + + context 'with a local file with rules with a predefined pipeline variable' do + let(:config) do + <<~EOY + include: + - local: #{file_location} + rules: + - if: $CI_PIPELINE_SOURCE == "#{pipeline_source}" + job: + script: exit 0 + EOY + end + + context 'when the rules matches' do + let(:pipeline_source) { 'push' } + + it_behaves_like 'including the file' + end + + context 'when the rules does not match' do + let(:pipeline_source) { 'web' } + + it_behaves_like 'not including the file' + end + end + + context 'with a local file with rules with a run pipeline variable' do + let(:config) do + <<~EOY + include: + - local: #{file_location} + rules: + - if: $MYVAR == "#{my_var}" + job: + script: exit 0 + EOY + end + + context 'when the rules matches' do + let(:my_var) { 'hello' } + + it_behaves_like 'including the file' + end + + context 'when the rules does not match' do + let(:my_var) { 'mello' } + + it_behaves_like 'not including the file' end end end diff --git a/spec/support/shared_examples/controllers/concerns/integrations_actions_shared_examples.rb b/spec/support/shared_examples/controllers/concerns/integrations/integrations_actions_shared_examples.rb similarity index 97% rename from spec/support/shared_examples/controllers/concerns/integrations_actions_shared_examples.rb rename to spec/support/shared_examples/controllers/concerns/integrations/integrations_actions_shared_examples.rb index 748a3acf17b..a8aed0c1f0b 100644 --- a/spec/support/shared_examples/controllers/concerns/integrations_actions_shared_examples.rb +++ b/spec/support/shared_examples/controllers/concerns/integrations/integrations_actions_shared_examples.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.shared_examples IntegrationsActions do +RSpec.shared_examples Integrations::Actions do let(:integration) do create(:datadog_integration, integration_attributes.merge( diff --git a/yarn.lock b/yarn.lock index 12752d5937f..ada439ba54a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2529,12 +2529,12 @@ axios-mock-adapter@^1.15.0: dependencies: deep-equal "^1.0.1" -axios@^0.20.0: - version "0.20.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.20.0.tgz#057ba30f04884694993a8cd07fa394cff11c50bd" - integrity sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA== +axios@^0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" + integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== dependencies: - follow-redirects "^1.10.0" + follow-redirects "^1.14.4" babel-eslint@^10.0.3: version "10.0.3" @@ -4982,10 +4982,10 @@ eslint-import-resolver-node@^0.3.4: debug "^2.6.9" resolve "^1.13.1" -eslint-import-resolver-webpack@0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.1.tgz#6d2fb928091daf2da46efa1e568055555b2de902" - integrity sha512-O/8mG6AHmaKYSMb4lWxiXPpaARxOJ4rMQEHJ8vTgjS1MXooJA3KPgBPPAdOPoV17v5ML5120qod5FBLM+DtgEw== +eslint-import-resolver-webpack@0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.2.tgz#fc813df0d08b9265cc7072d22393bda5198bdc1e" + integrity sha512-XodIPyg1OgE2h5BDErz3WJoK7lawxKTJNhgPNafRST6csC/MZC+L5P6kKqsZGRInpbgc02s/WZMrb4uGJzcuRg== dependencies: array-find "^1.0.0" debug "^3.2.7" @@ -4993,8 +4993,8 @@ eslint-import-resolver-webpack@0.13.1: find-root "^1.1.0" has "^1.0.3" interpret "^1.4.0" - is-core-module "^2.4.0" - is-regex "^1.1.3" + is-core-module "^2.7.0" + is-regex "^1.1.4" lodash "^4.17.21" resolve "^1.20.0" semver "^5.7.1" @@ -5050,10 +5050,10 @@ eslint-plugin-jest@^23.8.2: dependencies: "@typescript-eslint/experimental-utils" "^2.5.0" -eslint-plugin-no-jquery@2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-no-jquery/-/eslint-plugin-no-jquery-2.6.0.tgz#7892cb7c086f7813156bca6bc48429825428e9eb" - integrity sha512-xC7pbNHJMdyxqhzcNMRrmC5/tbt1T4KCKXjOqUpKm/CaRryGKS5iWztzWPrL0KwyI3R3ub6goHFmIQS19f+mZA== +eslint-plugin-no-jquery@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-no-jquery/-/eslint-plugin-no-jquery-2.7.0.tgz#855f5631cf5b8e25b930cf6f06e02dd81f132e72" + integrity sha512-Aeg7dA6GTH1AcWLlBtWNzOU9efK5KpNi7b0EhBO0o0M+awyzguUUo8gF6hXGjQ9n5h8/uRtYv9zOqQkeC5CG0w== eslint-plugin-promise@^4.2.1: version "4.2.1" @@ -5605,10 +5605,10 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" -follow-redirects@^1.0.0, follow-redirects@^1.10.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" - integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== +follow-redirects@^1.0.0, follow-redirects@^1.14.4: + version "1.14.4" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379" + integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g== for-in@^1.0.2: version "1.0.2" @@ -5999,6 +5999,13 @@ has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -6511,10 +6518,10 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.2.0, is-core-module@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" - integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== +is-core-module@^2.2.0, is-core-module@^2.7.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548" + integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw== dependencies: has "^1.0.3" @@ -6690,13 +6697,13 @@ is-potential-custom-element-name@^1.0.0: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397" integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c= -is-regex@^1.1.1, is-regex@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" - integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== +is-regex@^1.1.1, is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== dependencies: call-bind "^1.0.2" - has-symbols "^1.0.2" + has-tostringtag "^1.0.0" is-regexp@^2.0.0: version "2.1.0"