diff options
33 files changed, 404 insertions, 208 deletions
diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index 06cf8b95db3..d631a7a5c5c 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -10,6 +10,73 @@ # - guidelines for use found in # https://docs.gitlab.com/ee/development/contributing/style_guides.html#resolving-rubocop-exceptions. +# WIP See https://gitlab.com/gitlab-org/gitlab/-/issues/322903 +Graphql/Descriptions: + Exclude: + - 'app/graphql/types/access_level_enum.rb' + - 'app/graphql/types/admin/analytics/usage_trends/measurement_identifier_enum.rb' + - 'app/graphql/types/alert_management/alert_sort_enum.rb' + - 'app/graphql/types/alert_management/domain_filter_enum.rb' + - 'app/graphql/types/alert_management/integration_type_enum.rb' + - 'app/graphql/types/base_enum.rb' + - 'app/graphql/types/ci/config/status_enum.rb' + - 'app/graphql/types/ci/job_artifact_file_type_enum.rb' + - 'app/graphql/types/ci/pipeline_config_source_enum.rb' + - 'app/graphql/types/ci/pipeline_status_enum.rb' + - 'app/graphql/types/commit_action_mode_enum.rb' + - 'app/graphql/types/commit_encoding_enum.rb' + - 'app/graphql/types/container_expiration_policy_cadence_enum.rb' + - 'app/graphql/types/container_expiration_policy_keep_enum.rb' + - 'app/graphql/types/container_expiration_policy_older_than_enum.rb' + - 'app/graphql/types/container_repository_sort_enum.rb' + - 'app/graphql/types/design_management/design_version_event_enum.rb' + - 'app/graphql/types/error_tracking/sentry_error_status_enum.rb' + - 'app/graphql/types/issuable_sort_enum.rb' + - 'app/graphql/types/issuable_state_enum.rb' + - 'app/graphql/types/issue_sort_enum.rb' + - 'app/graphql/types/issue_state_event_enum.rb' + - 'app/graphql/types/merge_request_sort_enum.rb' + - 'app/graphql/types/merge_request_state_enum.rb' + - 'app/graphql/types/milestone_state_enum.rb' + - 'app/graphql/types/mutation_operation_mode_enum.rb' + - 'app/graphql/types/notes/position_type_enum.rb' + - 'app/graphql/types/packages/package_type_enum.rb' + - 'app/graphql/types/projects/namespace_project_sort_enum.rb' + - 'app/graphql/types/release_sort_enum.rb' + - 'app/graphql/types/snippets/blob_action_enum.rb' + - 'app/graphql/types/snippets/type_enum.rb' + - 'app/graphql/types/snippets/visibility_scopes_enum.rb' + - 'app/graphql/types/sort_enum.rb' + - 'app/graphql/types/todo_action_enum.rb' + - 'app/graphql/types/todo_target_enum.rb' + - 'app/graphql/types/tree/type_enum.rb' + - 'app/graphql/types/user_state_enum.rb' + - 'ee/app/graphql/ee/types/issue_sort_enum.rb' + - 'ee/app/graphql/ee/types/list_limit_metric_enum.rb' + - 'ee/app/graphql/ee/types/todo_target_enum.rb' + - 'ee/app/graphql/types/alert_management/payload_alert_field_name_enum.rb' + - 'ee/app/graphql/types/alert_management/payload_alert_field_type_enum.rb' + - 'ee/app/graphql/types/boards/epic_wildcard_id_enum.rb' + - 'ee/app/graphql/types/boards/iteration_wildcard_id_enum.rb' + - 'ee/app/graphql/types/dast_site_profile_validation_status_enum.rb' + - 'ee/app/graphql/types/dast_site_validation_strategy_enum.rb' + - 'ee/app/graphql/types/epic_sort_enum.rb' + - 'ee/app/graphql/types/epic_state_enum.rb' + - 'ee/app/graphql/types/epic_state_event_enum.rb' + - 'ee/app/graphql/types/geo/registry_state_enum.rb' + - 'ee/app/graphql/types/health_status_enum.rb' + - 'ee/app/graphql/types/iteration_state_enum.rb' + - 'ee/app/graphql/types/move_type_enum.rb' + - 'ee/app/graphql/types/requirements_management/requirement_state_enum.rb' + - 'ee/app/graphql/types/requirements_management/test_report_state_enum.rb' + - 'ee/app/graphql/types/security_scanner_type_enum.rb' + - 'ee/app/graphql/types/vulnerability/issue_link_type_enum.rb' + - 'ee/app/graphql/types/vulnerability_grade_enum.rb' + - 'ee/app/graphql/types/vulnerability_report_type_enum.rb' + - 'ee/app/graphql/types/vulnerability_severity_enum.rb' + - 'ee/app/graphql/types/vulnerability_sort_enum.rb' + - 'ee/app/graphql/types/vulnerability_state_enum.rb' + # WIP See https://gitlab.com/gitlab-org/gitlab/-/issues/267606 FactoryBot/InlineAssociation: Exclude: diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 66068a748a9..0ba19ac773f 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -5d9c71d8d7188bd58f272dc62a7939ece747909d +7991d3def6026a252828ea6bca74df20d4532b9b diff --git a/app/assets/javascripts/monitoring/stores/embed_group/index.js b/app/assets/javascripts/monitoring/stores/embed_group/index.js index 773bca9f87e..66c65adc413 100644 --- a/app/assets/javascripts/monitoring/stores/embed_group/index.js +++ b/app/assets/javascripts/monitoring/stores/embed_group/index.js @@ -20,5 +20,3 @@ export const createStore = () => }, }, }); - -export default createStore(); diff --git a/app/assets/javascripts/pages/projects/labels/edit/index.js b/app/assets/javascripts/pages/projects/labels/edit/index.js index 83d6ac9fd14..3b7562deed9 100644 --- a/app/assets/javascripts/pages/projects/labels/edit/index.js +++ b/app/assets/javascripts/pages/projects/labels/edit/index.js @@ -1,3 +1,3 @@ import Labels from 'ee_else_ce/labels'; -document.addEventListener('DOMContentLoaded', () => new Labels()); +new Labels(); // eslint-disable-line no-new diff --git a/app/serializers/test_suite_comparer_entity.rb b/app/serializers/test_suite_comparer_entity.rb index aab805f9598..cfa728c01be 100644 --- a/app/serializers/test_suite_comparer_entity.rb +++ b/app/serializers/test_suite_comparer_entity.rb @@ -34,4 +34,16 @@ class TestSuiteComparerEntity < Grape::Entity expose :resolved_errors, using: TestCaseEntity do |suite| suite.limited_tests.resolved_errors end + + expose :suite_errors do |suite| + head_suite_error = suite.head_suite.suite_error + base_suite_error = suite.base_suite.suite_error + + next unless head_suite_error.present? || base_suite_error.present? + + { + head: head_suite_error, + base: base_suite_error + } + end end diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml index c24fe5c6307..b92f35c108c 100644 --- a/app/views/dashboard/_groups_head.html.haml +++ b/app/views/dashboard/_groups_head.html.haml @@ -3,7 +3,7 @@ - if current_user.can_create_group? .page-title-controls - = link_to _("New group"), new_group_path, class: "gl-button btn btn-success" + = link_to _("New group"), new_group_path, class: "gl-button btn btn-confirm", data: { testid: "new-group-button" } .top-area %ul.nav-links.mobile-separator.nav.nav-tabs diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml index 6c994f3b230..57c0801074b 100644 --- a/app/views/dashboard/_projects_head.html.haml +++ b/app/views/dashboard/_projects_head.html.haml @@ -9,7 +9,7 @@ - if current_user.can_create_project? .page-title-controls - = link_to _("New project"), new_project_path, class: "gl-button btn btn-success" + = link_to _("New project"), new_project_path, class: "gl-button btn btn-confirm" .top-area.scrolling-tabs-container.inner-page-scroll-tabs .fade-left= sprite_icon('chevron-lg-left', size: 12) diff --git a/app/views/dashboard/_snippets_head.html.haml b/app/views/dashboard/_snippets_head.html.haml index 2640d483615..e96b5695ddc 100644 --- a/app/views/dashboard/_snippets_head.html.haml +++ b/app/views/dashboard/_snippets_head.html.haml @@ -4,7 +4,7 @@ - if current_user && current_user.snippets.any? || @snippets.any? .page-title-controls - if can?(current_user, :create_snippet) - = link_to _("New snippet"), new_snippet_path, class: "gl-button btn btn-success", title: _("New snippet") + = link_to _("New snippet"), new_snippet_path, class: "gl-button btn btn-confirm", title: _("New snippet") .top-area %ul.nav-links.nav.nav-tabs diff --git a/changelogs/unreleased/ab-remove-minergate-trigger.yml b/changelogs/unreleased/ab-remove-minergate-trigger.yml new file mode 100644 index 00000000000..fd75be2845b --- /dev/null +++ b/changelogs/unreleased/ab-remove-minergate-trigger.yml @@ -0,0 +1,5 @@ +--- +title: Remove historic minergate trigger (GitLab.com only) +merge_request: 54910 +author: +type: other diff --git a/changelogs/unreleased/btn-confirm-home.yml b/changelogs/unreleased/btn-confirm-home.yml new file mode 100644 index 00000000000..a8b063fc5f3 --- /dev/null +++ b/changelogs/unreleased/btn-confirm-home.yml @@ -0,0 +1,5 @@ +--- +title: Remove deprecated button variant in groups, projects and snippets +merge_request: 54747 +author: Yogi (@yo) +type: changed diff --git a/changelogs/unreleased/dry-up-notes-build-service-spec.yml b/changelogs/unreleased/dry-up-notes-build-service-spec.yml new file mode 100644 index 00000000000..4aadaaa6030 --- /dev/null +++ b/changelogs/unreleased/dry-up-notes-build-service-spec.yml @@ -0,0 +1,5 @@ +--- +title: Dry up notes build service spec +merge_request: 54632 +author: Lee Tickett @leetickett +type: other diff --git a/config/feature_flags/development/saml_group_links.yml b/config/feature_flags/development/saml_group_links.yml deleted file mode 100644 index 3b427bd83fa..00000000000 --- a/config/feature_flags/development/saml_group_links.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: saml_group_links -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45080 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/267020 -milestone: '13.6' -type: development -group: group::access -default_enabled: true diff --git a/danger/product_intelligence/Dangerfile b/danger/product_intelligence/Dangerfile index e9f6a867fec..5709e5eb83f 100644 --- a/danger/product_intelligence/Dangerfile +++ b/danger/product_intelligence/Dangerfile @@ -37,8 +37,8 @@ tracking_files = [ tracking_changed_files = all_changed_files & tracking_files usage_data_changed_files = all_changed_files.grep(%r{(usage_data)}) -metrics_changed_files = all_changed_files.grep(%r{((ee/)?config/metrics/.*\.yml)}) dictionary_changed_file = all_changed_files.grep(%r{(doc/development/usage_ping/dictionary.md)}) +metrics_changed_files = all_changed_files.grep(%r{((ee/)?config/metrics/.*\.yml)}) def matching_files?(file, extension:, pattern:) return unless file.end_with?(extension) @@ -52,6 +52,16 @@ js_patterns = Regexp.union( 'data-track-event' ) +dictionary_pattern = Regexp.union( + 'key_path:', + 'description:', + 'product_section:', + 'product_stage:', + 'product_group:', + 'status:', + 'tier:' +) + snowplow_changed_files = all_changed_files.select do |file| matching_files?(file, extension: '.rb', pattern: %r{Gitlab::Tracking\.event}) || matching_files?(file, extension: '.js', pattern: js_patterns) || @@ -59,7 +69,15 @@ snowplow_changed_files = all_changed_files.select do |file| matching_files?(file, extension: '.haml', pattern: %r{data: \{ track}) end -matching_changed_files = usage_data_changed_files + tracking_changed_files + metrics_changed_files + dictionary_changed_file + snowplow_changed_files +required_dictionary_update_changed_files = dictionary_changed_file.select do |file| + matching_files?(file, extension: '.yml', pattern: dictionary_pattern) +end + +matching_changed_files = usage_data_changed_files + + tracking_changed_files + + metrics_changed_files + + dictionary_changed_file + + snowplow_changed_files if matching_changed_files.any? @@ -71,7 +89,7 @@ if matching_changed_files.any? warn format(CHANGED_FILES_MESSAGE, changed_files: helper.markdown_list(matching_changed_files), engineers_group: mention) - fail format(UPDATE_DICTIONARY_MESSAGE) if metrics_changed_files.any? && dictionary_changed_file.empty? + fail format(UPDATE_DICTIONARY_MESSAGE) if metrics_changed_files.any? && required_dictionary_update_changed_files.empty? labels = ['product intelligence'] labels << 'product intelligence::review pending' unless helper.mr_has_labels?('product intelligence::approved') diff --git a/db/migrate/20210223105256_remove_minergate_trigger_on_git_lab_com.rb b/db/migrate/20210223105256_remove_minergate_trigger_on_git_lab_com.rb new file mode 100644 index 00000000000..b5f018efa59 --- /dev/null +++ b/db/migrate/20210223105256_remove_minergate_trigger_on_git_lab_com.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +class RemoveMinergateTriggerOnGitLabCom < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + return unless Gitlab.com? + + with_lock_retries do + execute <<~SQL + DROP TRIGGER IF EXISTS ci_builds_block_minergate ON ci_builds; + SQL + end + + execute <<~SQL + DROP FUNCTION IF EXISTS make_build_to_be_stuck_with_lock_version(); + DROP FUNCTION IF EXISTS make_build_to_be_stuck(); + SQL + end + + def down + # no-op + end +end diff --git a/db/schema_migrations/20210223105256 b/db/schema_migrations/20210223105256 new file mode 100644 index 00000000000..cfee761bbc1 --- /dev/null +++ b/db/schema_migrations/20210223105256 @@ -0,0 +1 @@ +5fa0e89f9d7715309f6fd9808a07c34bcca0d556a2a4a2841f419b3279db8759
\ No newline at end of file diff --git a/doc/ci/git_submodules.md b/doc/ci/git_submodules.md index d9a40c1feb6..01df4f63c92 100644 --- a/doc/ci/git_submodules.md +++ b/doc/ci/git_submodules.md @@ -5,38 +5,23 @@ info: To determine the technical writer assigned to the Stage/Group associated w type: reference --- -# Using Git submodules with GitLab CI +# Using Git submodules with GitLab CI/CD -> **Notes:** -> -> - GitLab 8.12 introduced a new [CI job permissions model](../user/project/new_ci_build_permissions_model.md) and you -> are encouraged to upgrade your GitLab instance if you haven't done already. -> If you are **not** using GitLab 8.12 or higher, you would need to work your way -> around submodules in order to access the sources of e.g., `gitlab.com/group/project` -> with the use of [SSH keys](ssh_keys/index.md). -> - With GitLab 8.12 onward, your permissions are used to evaluate what a CI job -> can access. More information about how this system works can be found in the -> [Jobs permissions model](../user/permissions.md#job-permissions). -> - The HTTP(S) Git protocol [must be enabled](../user/admin_area/settings/visibility_and_access_controls.md#enabled-git-access-protocols) in your GitLab instance. +Use [Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) to keep +a Git repository as a subdirectory of another Git repository. You can clone another +repository into your project and keep your commits separate. -## Configuring the `.gitmodules` file +## Configure the `.gitmodules` file -If dealing with [Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules), your project probably has a file -named `.gitmodules`. +When you use Git submodules, your project should have a file named `.gitmodules`. +You might need to modify it to work in a GitLab CI/CD job. -Let's consider the following example: +For example, your `.gitmodules` configuration might look like the following if: -1. Your project is located at `https://gitlab.com/secret-group/my-project`. -1. To checkout your sources you usually use an SSH address like - `git@gitlab.com:secret-group/my-project.git`. -1. Your project depends on `https://gitlab.com/group/project`, which you want - to include as a submodule. - -If you are using GitLab 8.12+ and your submodule is on the same GitLab server, -you must update your `.gitmodules` file to use **relative URLs**. -Since Git allows the usage of relative URLs for your `.gitmodules` configuration, -this easily allows you to use HTTP(S) for cloning all your CI jobs and SSH -for all your local checkouts. The `.gitmodules` would look like: +- Your project is located at `https://gitlab.com/secret-group/my-project`. +- Your project depends on `https://gitlab.com/group/project`, which you want + to include as a submodule. +- You check out your sources with an SSH address like `git@gitlab.com:secret-group/my-project.git`. ```ini [submodule "project"] @@ -44,14 +29,16 @@ for all your local checkouts. The `.gitmodules` would look like: url = ../../group/project.git ``` -The above configuration instructs Git to automatically deduce the URL that -should be used when cloning sources. Whether you use HTTP(S) or SSH, Git uses -that same channel and it makes all your CI jobs use HTTP(S). -GitLab CI/CD only uses HTTP(S) for cloning your sources, and all your local -clones continue using SSH. +When your submodule is on the same GitLab server, you should use relative URLs in +your `.gitmodules` file. Then you can clone with HTTPS in all your CI/CD jobs. You +can also use SSH for all your local checkouts. + +The above configuration instructs Git to automatically deduce the URL to +use when cloning sources. Git uses the same configuration for both HTTPS and SSH. +GitLab CI/CD uses HTTPS for cloning your sources, and you can continue to use SSH +to clone locally. -For all other submodules not located on the same GitLab server, use the full -HTTP(S) protocol URL: +For submodules not located on the same GitLab server, use the full URL: ```ini [submodule "project-x"] @@ -59,45 +46,16 @@ HTTP(S) protocol URL: url = https://gitserver.com/group/project-x.git ``` -Once `.gitmodules` is correctly configured, you can move on to -[configuring your `.gitlab-ci.yml`](#using-git-submodules-in-your-ci-jobs). - -## Using Git submodules in your CI jobs +## Use Git submodules in CI/CD jobs -There are a few steps you need to take in order to make submodules work -correctly with your CI jobs: +To make submodules work correctly in CI/CD jobs: -1. First, make sure you have used [relative URLs](#configuring-the-gitmodules-file) - for the submodules located in the same GitLab server. -1. Next, if you are using `gitlab-runner` v1.10+, you can set the - `GIT_SUBMODULE_STRATEGY` variable to either `normal` or `recursive` to tell - the runner to fetch your submodules before the job: +1. Make sure you use [relative URLs](#configure-the-gitmodules-file) + for submodules located in the same GitLab server. +1. You can set the `GIT_SUBMODULE_STRATEGY` variable to either `normal` or `recursive` + to tell the runner to [fetch your submodules before the job](runners/README.md#git-submodule-strategy): ```yaml variables: GIT_SUBMODULE_STRATEGY: recursive ``` - - See the [GitLab Runner documentation](runners/README.md#git-submodule-strategy) - for more details about `GIT_SUBMODULE_STRATEGY`. - -1. If you are using an older version of `gitlab-runner`, then use - `git submodule sync/update` in `before_script`: - - ```yaml - before_script: - - git submodule sync --recursive - - git submodule update --init --recursive - ``` - - `--recursive` should be used in either both or none (`sync/update`) depending on - whether you have recursive submodules. - -The rationale to set the `sync` and `update` in `before_script` is because of -the way Git submodules work. On a fresh runner workspace, Git sets the -submodule URL including the token in `.git/config` -(or `.git/modules/<submodule>/config`) based on `.gitmodules` and the current -remote URL. On subsequent jobs on the same runner, `.git/config` is cached -and already contains a full URL for the submodule, corresponding to the previous -job, and to **a token from a previous job**. `sync` allows to force updating -the full URL. diff --git a/doc/topics/git/index.md b/doc/topics/git/index.md index d6e1dcf0998..36de660bc18 100644 --- a/doc/topics/git/index.md +++ b/doc/topics/git/index.md @@ -84,7 +84,7 @@ The following are advanced topics for those who want to get the most out of Git: - [Introduction to Git rebase, force-push, and merge conflicts](git_rebase.md) - [Server Hooks](../../administration/server_hooks.md) - [Git Attributes](../../user/project/git_attributes.md) -- Git Submodules: [Using Git submodules with GitLab CI](../../ci/git_submodules.md#using-git-submodules-with-gitlab-ci) +- Git Submodules: [Using Git submodules with GitLab CI](../../ci/git_submodules.md) - [Partial Clone](partial_clone.md) ## API diff --git a/doc/user/application_security/security_dashboard/index.md b/doc/user/application_security/security_dashboard/index.md index 7f9d60d853b..007581c5d26 100644 --- a/doc/user/application_security/security_dashboard/index.md +++ b/doc/user/application_security/security_dashboard/index.md @@ -75,7 +75,7 @@ CSV file containing details of the resources scanned. At the project level, the Security Dashboard displays a chart with the number of vulnerabilities over time. Access it by navigating to **Security & Compliance > Security Dashboard**. We display historical -data up to 365 days. +data up to 365 days. The chart's data is updated daily. ![Project Security Dashboard](img/project_security_dashboard_chart_v13_6.png) diff --git a/lib/release_highlights/validator/entry.rb b/lib/release_highlights/validator/entry.rb index 3c83ca21123..133afcb52ae 100644 --- a/lib/release_highlights/validator/entry.rb +++ b/lib/release_highlights/validator/entry.rb @@ -11,7 +11,7 @@ module ReleaseHighlights validates :title, :body, :stage, presence: true validates :'self-managed', :'gitlab-com', inclusion: { in: [true, false], message: "must be a boolean" } - validates :url, :image_url, format: { with: URI::DEFAULT_PARSER.make_regexp, message: 'must be a URL' } + validates :url, :image_url, public_url: { dns_rebind_protection: true } validates :release, numericality: true validate :validate_published_at validate :validate_packages diff --git a/package.json b/package.json index 05280523aee..7814aa04964 100644 --- a/package.json +++ b/package.json @@ -180,7 +180,7 @@ "commander": "^2.18.0", "custom-jquery-matchers": "^2.1.0", "docdash": "^1.0.2", - "eslint": "7.20.0", + "eslint": "7.21.0", "eslint-import-resolver-jest": "3.0.0", "eslint-import-resolver-webpack": "0.13.0", "eslint-plugin-jasmine": "4.1.2", diff --git a/rubocop/cop/graphql/descriptions.rb b/rubocop/cop/graphql/descriptions.rb index 1585e5c9788..ec233c65874 100644 --- a/rubocop/cop/graphql/descriptions.rb +++ b/rubocop/cop/graphql/descriptions.rb @@ -1,26 +1,31 @@ # frozen_string_literal: true -# This cop checks for missing GraphQL field descriptions. +# This cop checks for missing GraphQL descriptions and enforces the description style guide: +# https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#description-style-guide # -# @example +# @examples # # # bad -# class AwfulClass +# class AwfulType # field :some_field, GraphQL::STRING_TYPE # end # -# class TerribleClass +# class TerribleType # argument :some_argument, GraphQL::STRING_TYPE # end # -# class UngoodClass +# class UngoodType # field :some_argument, # GraphQL::STRING_TYPE, # description: "A description that does not end in a period" # end # +# class BadEnum +# value "some_value" +# end +# # # good -# class GreatClass +# class GreatType # argument :some_field, # GraphQL::STRING_TYPE, # description: "Well described - a superb description." @@ -29,6 +34,10 @@ # GraphQL::STRING_TYPE, # description: "A thorough and compelling description." # end +# +# class GoodEnum +# value "some_value", "Good description." +# end module RuboCop module Cop @@ -37,19 +46,26 @@ module RuboCop MSG_NO_DESCRIPTION = 'Please add a `description` property.' MSG_NO_PERIOD = '`description` strings must end with a `.`.' - # ability_field and permission_field set a default description. - def_node_matcher :field_or_argument?, <<~PATTERN - (send nil? {:field :argument} ...) + def_node_matcher :graphql_describable?, <<~PATTERN + (send nil? {:field :argument :value} ...) + PATTERN + + def_node_matcher :enum?, <<~PATTERN + (send nil? :value ...) PATTERN - def_node_matcher :description, <<~PATTERN + def_node_matcher :description_kwarg, <<~PATTERN (... (hash <(pair (sym :description) $_) ...>)) PATTERN + def_node_matcher :enum_style_description, <<~PATTERN + (send nil? :value _ $str ...) + PATTERN + def on_send(node) - return unless field_or_argument?(node) + return unless graphql_describable?(node) - description = description(node) + description = locate_description(node) return add_offense(node, location: :expression, message: MSG_NO_DESCRIPTION) unless description @@ -59,7 +75,7 @@ module RuboCop # Autocorrect missing periods at end of description. def autocorrect(node) lambda do |corrector| - description = description(node) + description = locate_description(node) next unless description corrector.insert_after(before_end_quote(description), '.') @@ -68,6 +84,16 @@ module RuboCop private + # Fields and arguments define descriptions using a `description` keyword argument. + # Enums may define descriptions this way, or as a second `String` param. + def locate_description(node) + description = description_kwarg(node) + + return description unless description.nil? && enum?(node) + + enum_style_description(node) + end + def no_period?(description) # Test that the description node is a `:str` (as opposed to # a `#copy_field_description` call) before checking. diff --git a/scripts/verify-tff-mapping b/scripts/verify-tff-mapping index 9931e14008a..5bc4b23f99f 100755 --- a/scripts/verify-tff-mapping +++ b/scripts/verify-tff-mapping @@ -110,6 +110,12 @@ tests = [ explanation: 'Migration should map to its timestamped spec', source: 'db/post_migrate/20190924152703_migrate_issue_trackers_data.rb', expected: ['spec/migrations/20190924152703_migrate_issue_trackers_data_spec.rb'] + }, + + { + explanation: 'Whats New should map to its respective spec', + source: 'data/whats_new/202101140001_13_08.yml', + expected: ['spec/lib/release_highlights/validator_spec.rb'] } ] diff --git a/spec/features/dashboard/group_spec.rb b/spec/features/dashboard/group_spec.rb index 0b99fed2a2d..bc6f449edc5 100644 --- a/spec/features/dashboard/group_spec.rb +++ b/spec/features/dashboard/group_spec.rb @@ -15,7 +15,7 @@ RSpec.describe 'Dashboard Group' do it 'creates new group', :js do visit dashboard_groups_path - find('.btn-success').click + find('[data-testid="new-group-button"]').click new_name = 'Samurai' fill_in 'group_name', with: new_name diff --git a/spec/fixtures/api/schemas/entities/test_suite_comparer.json b/spec/fixtures/api/schemas/entities/test_suite_comparer.json index ecb331ae013..ac001ef8843 100644 --- a/spec/fixtures/api/schemas/entities/test_suite_comparer.json +++ b/spec/fixtures/api/schemas/entities/test_suite_comparer.json @@ -26,7 +26,14 @@ "existing_failures": { "type": "array", "items": { "$ref": "test_case.json" } }, "new_errors": { "type": "array", "items": { "$ref": "test_case.json" } }, "resolved_errors": { "type": "array", "items": { "$ref": "test_case.json" } }, - "existing_errors": { "type": "array", "items": { "$ref": "test_case.json" } } + "existing_errors": { "type": "array", "items": { "$ref": "test_case.json" } }, + "suite_errors": { + "type": ["object", "null"], + "properties": { + "head": { "type": ["string", "null"] }, + "base": { "type": ["string", "null"] } + } + } }, "additionalProperties": false } diff --git a/spec/lib/gitlab/ci/variables/collection/sort_spec.rb b/spec/lib/gitlab/ci/variables/collection/sort_spec.rb index 6420798d6f5..c36dbe5bc07 100644 --- a/spec/lib/gitlab/ci/variables/collection/sort_spec.rb +++ b/spec/lib/gitlab/ci/variables/collection/sort_spec.rb @@ -214,7 +214,6 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Sort do context 'when FF :variable_inside_variable is enabled' do before do stub_licensed_features(group_saml_group_sync: true) - stub_feature_flags(saml_group_links: true) stub_feature_flags(variable_inside_variable: true) end diff --git a/spec/lib/release_highlights/validator/entry_spec.rb b/spec/lib/release_highlights/validator/entry_spec.rb index da44938f165..5f7ccbf4310 100644 --- a/spec/lib/release_highlights/validator/entry_spec.rb +++ b/spec/lib/release_highlights/validator/entry_spec.rb @@ -40,8 +40,8 @@ RSpec.describe ReleaseHighlights::Validator::Entry do end it 'validates boolean value of "self-managed" and "gitlab-com"' do - allow(entry).to receive(:value_for).with('self-managed').and_return('nope') - allow(entry).to receive(:value_for).with('gitlab-com').and_return('yerp') + allow(entry).to receive(:value_for).with(:'self-managed').and_return('nope') + allow(entry).to receive(:value_for).with(:'gitlab-com').and_return('yerp') subject.valid? @@ -50,17 +50,18 @@ RSpec.describe ReleaseHighlights::Validator::Entry do end it 'validates URI of "url" and "image_url"' do - allow(entry).to receive(:value_for).with('image_url').and_return('imgur/gitlab_feature.gif') - allow(entry).to receive(:value_for).with('url').and_return('gitlab/newest_release.html') + stub_env('RSPEC_ALLOW_INVALID_URLS', 'false') + allow(entry).to receive(:value_for).with(:image_url).and_return('https://foobar.x/images/ci/gitlab-ci-cd-logo_2x.png') + allow(entry).to receive(:value_for).with(:url).and_return('') subject.valid? - expect(subject.errors[:url]).to include(/must be a URL/) - expect(subject.errors[:image_url]).to include(/must be a URL/) + expect(subject.errors[:url]).to include(/must be a valid URL/) + expect(subject.errors[:image_url]).to include(/is blocked: Host cannot be resolved or invalid/) end it 'validates release is numerical' do - allow(entry).to receive(:value_for).with('release').and_return('one') + allow(entry).to receive(:value_for).with(:release).and_return('one') subject.valid? @@ -68,7 +69,7 @@ RSpec.describe ReleaseHighlights::Validator::Entry do end it 'validates published_at is a date' do - allow(entry).to receive(:value_for).with('published_at').and_return('christmas day') + allow(entry).to receive(:value_for).with(:published_at).and_return('christmas day') subject.valid? @@ -76,7 +77,7 @@ RSpec.describe ReleaseHighlights::Validator::Entry do end it 'validates packages are included in list' do - allow(entry).to receive(:value_for).with('packages').and_return(['ALL']) + allow(entry).to receive(:value_for).with(:packages).and_return(['ALL']) subject.valid? diff --git a/spec/lib/release_highlights/validator_spec.rb b/spec/lib/release_highlights/validator_spec.rb index a423e8cc5f6..f30754b4167 100644 --- a/spec/lib/release_highlights/validator_spec.rb +++ b/spec/lib/release_highlights/validator_spec.rb @@ -78,7 +78,10 @@ RSpec.describe ReleaseHighlights::Validator do end describe 'when validating all files' do - it 'they should have no errors' do + # Permit DNS requests to validate all URLs in the YAML files + it 'they should have no errors', :permit_dns do + stub_env('RSPEC_ALLOW_INVALID_URLS', 'false') + expect(described_class.validate_all!).to be_truthy, described_class.error_message end end diff --git a/spec/rubocop/cop/graphql/descriptions_spec.rb b/spec/rubocop/cop/graphql/descriptions_spec.rb index 9ad40fad83d..276e4e03c15 100644 --- a/spec/rubocop/cop/graphql/descriptions_spec.rb +++ b/spec/rubocop/cop/graphql/descriptions_spec.rb @@ -91,6 +91,50 @@ RSpec.describe RuboCop::Cop::Graphql::Descriptions do end end + context 'enum values' do + it 'adds an offense when there is no description' do + expect_offense(<<~TYPE) + module Types + class FakeEnum < BaseEnum + value 'FOO', value: 'foo' + ^^^^^^^^^^^^^^^^^^^^^^^^^ Please add a `description` property. + end + end + TYPE + end + + it 'adds an offense when description does not end in a period' do + expect_offense(<<~TYPE) + module Types + class FakeEnum < BaseEnum + value 'FOO', value: 'foo', description: 'bar' + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `description` strings must end with a `.`. + end + end + TYPE + end + + it 'does not add an offense when description is correct (defined using `description:`)' do + expect_no_offenses(<<~TYPE.strip) + module Types + class FakeEnum < BaseEnum + value 'FOO', value: 'foo', description: 'bar.' + end + end + TYPE + end + + it 'does not add an offense when description is correct (defined as a second argument)' do + expect_no_offenses(<<~TYPE.strip) + module Types + class FakeEnum < BaseEnum + value 'FOO', 'bar.', value: 'foo' + end + end + TYPE + end + end + describe 'autocorrecting descriptions without periods' do it 'can autocorrect' do expect_offense(<<~TYPE) diff --git a/spec/serializers/test_suite_comparer_entity_spec.rb b/spec/serializers/test_suite_comparer_entity_spec.rb index a63f5683779..318d1d3c1e3 100644 --- a/spec/serializers/test_suite_comparer_entity_spec.rb +++ b/spec/serializers/test_suite_comparer_entity_spec.rb @@ -35,6 +35,7 @@ RSpec.describe TestSuiteComparerEntity do end expect(subject[:resolved_failures]).to be_empty expect(subject[:existing_failures]).to be_empty + expect(subject[:suite_errors]).to be_nil end end @@ -56,6 +57,7 @@ RSpec.describe TestSuiteComparerEntity do end expect(subject[:resolved_failures]).to be_empty expect(subject[:existing_failures]).to be_empty + expect(subject[:suite_errors]).to be_nil end end @@ -77,6 +79,7 @@ RSpec.describe TestSuiteComparerEntity do expect(existing_failure[:execution_time]).to eq(test_case_failed.execution_time) expect(existing_failure[:system_output]).to eq(test_case_failed.system_output) end + expect(subject[:suite_errors]).to be_nil end end @@ -98,6 +101,47 @@ RSpec.describe TestSuiteComparerEntity do expect(resolved_failure[:system_output]).to eq(test_case_success.system_output) end expect(subject[:existing_failures]).to be_empty + expect(subject[:suite_errors]).to be_nil + end + end + + context 'when head suite has suite error' do + before do + allow(head_suite).to receive(:suite_error).and_return('some error') + end + + it 'contains suite error for head suite' do + expect(subject[:suite_errors]).to eq( + head: 'some error', + base: nil + ) + end + end + + context 'when base suite has suite error' do + before do + allow(base_suite).to receive(:suite_error).and_return('some error') + end + + it 'contains suite error for head suite' do + expect(subject[:suite_errors]).to eq( + head: nil, + base: 'some error' + ) + end + end + + context 'when base and head suite both have suite errors' do + before do + allow(head_suite).to receive(:suite_error).and_return('head error') + allow(base_suite).to receive(:suite_error).and_return('base error') + end + + it 'contains suite error for head suite' do + expect(subject[:suite_errors]).to eq( + head: 'head error', + base: 'base error' + ) end end end diff --git a/spec/services/notes/build_service_spec.rb b/spec/services/notes/build_service_spec.rb index 2a8c3bd75fa..deeab66c4e9 100644 --- a/spec/services/notes/build_service_spec.rb +++ b/spec/services/notes/build_service_spec.rb @@ -8,26 +8,33 @@ RSpec.describe Notes::BuildService do let(:note) { create(:discussion_note_on_issue) } let(:project) { note.project } let(:author) { note.author } + let(:user) { author } let(:merge_request) { create(:merge_request, source_project: project) } - let(:mr_note) { create(:discussion_note_on_merge_request, noteable: merge_request, project: project, author: author) } + let(:mr_note) { create(:discussion_note_on_merge_request, noteable: merge_request, project: project, author: note.author) } + let(:base_params) { { note: 'Test' } } + let(:params) { {} } + + subject(:new_note) { described_class.new(project, user, base_params.merge(params)).execute } describe '#execute' do context 'when in_reply_to_discussion_id is specified' do + let(:params) { { in_reply_to_discussion_id: note.discussion_id } } + context 'when a note with that original discussion ID exists' do it 'sets the note up to be in reply to that note' do - new_note = described_class.new(project, author, note: 'Test', in_reply_to_discussion_id: note.discussion_id).execute expect(new_note).to be_valid expect(new_note.in_reply_to?(note)).to be_truthy expect(new_note.resolved?).to be_falsey end context 'when discussion is resolved' do + let(:params) { { in_reply_to_discussion_id: mr_note.discussion_id } } + before do mr_note.resolve!(author) end it 'resolves the note' do - new_note = described_class.new(project, author, note: 'Test', in_reply_to_discussion_id: mr_note.discussion_id).execute expect(new_note).to be_valid expect(new_note.resolved?).to be_truthy end @@ -36,24 +43,23 @@ RSpec.describe Notes::BuildService do context 'when a note with that discussion ID exists' do it 'sets the note up to be in reply to that note' do - new_note = described_class.new(project, author, note: 'Test', in_reply_to_discussion_id: note.discussion_id).execute expect(new_note).to be_valid expect(new_note.in_reply_to?(note)).to be_truthy end end context 'when no note with that discussion ID exists' do + let(:params) { { in_reply_to_discussion_id: 'foo' } } + it 'sets an error' do - new_note = described_class.new(project, author, note: 'Test', in_reply_to_discussion_id: 'foo').execute expect(new_note.errors[:base]).to include('Discussion to reply to cannot be found') end end context 'when user has no access to discussion' do - it 'sets an error' do - another_user = create(:user) - new_note = described_class.new(project, another_user, note: 'Test', in_reply_to_discussion_id: note.discussion_id).execute + let(:user) { create(:user) } + it 'sets an error' do expect(new_note.errors[:base]).to include('Discussion to reply to cannot be found') end end @@ -129,22 +135,21 @@ RSpec.describe Notes::BuildService do context 'when replying to individual note' do let(:note) { create(:note_on_issue) } - - subject { described_class.new(project, author, note: 'Test', in_reply_to_discussion_id: note.discussion_id).execute } + let(:params) { { in_reply_to_discussion_id: note.discussion_id } } it 'sets the note up to be in reply to that note' do - expect(subject).to be_valid - expect(subject).to be_a(DiscussionNote) - expect(subject.discussion_id).to eq(note.discussion_id) + expect(new_note).to be_valid + expect(new_note).to be_a(DiscussionNote) + expect(new_note.discussion_id).to eq(note.discussion_id) end context 'when noteable does not support replies' do let(:note) { create(:note_on_commit) } it 'builds another individual note' do - expect(subject).to be_valid - expect(subject).to be_a(Note) - expect(subject.discussion_id).not_to eq(note.discussion_id) + expect(new_note).to be_valid + expect(new_note).to be_a(Note) + expect(new_note.discussion_id).not_to eq(note.discussion_id) end end end @@ -156,124 +161,92 @@ RSpec.describe Notes::BuildService do context 'when replying to a confidential comment' do let(:note) { create(:note_on_issue, confidential: true) } + let(:params) { { in_reply_to_discussion_id: note.discussion_id, confidential: false } } context 'when the user can read confidential comments' do - subject do - described_class.new( - project, - author, - note: 'Test', - in_reply_to_discussion_id: note.discussion_id, - confidential: false - ).execute - end - it '`confidential` param is ignored and set to `true`' do - expect(subject.confidential).to be_truthy + expect(new_note.confidential).to be_truthy end end context 'when the user cannot read confidential comments' do - let(:another_user) { create(:user) } - - subject do - described_class.new( - project, - another_user, - note: 'Test', - in_reply_to_discussion_id: note.discussion_id, - confidential: false - ).execute - end + let(:user) { create(:user) } it 'returns `Discussion to reply to cannot be found` error' do - expect(subject.errors.first).to include("Discussion to reply to cannot be found") + expect(new_note.errors.first).to include("Discussion to reply to cannot be found") end end end context 'when replying to a public comment' do let(:note) { create(:note_on_issue, confidential: false) } - - subject do - described_class.new( - project, - author, - note: 'Test', - in_reply_to_discussion_id: note.discussion_id, - confidential: true - ).execute - end + let(:params) { { in_reply_to_discussion_id: note.discussion_id, confidential: true } } it '`confidential` param is ignored and set to `false`' do - expect(subject.confidential).to be_falsey + expect(new_note.confidential).to be_falsey end end context 'when creating a new comment' do context 'when the `confidential` note flag is set to `true`' do context 'when the user is allowed (reporter)' do - subject { described_class.new(project, author, note: 'Test', noteable: merge_request, confidential: true).execute } + let(:params) { { confidential: true, noteable: merge_request } } it 'note `confidential` flag is set to `true`' do - expect(subject.confidential).to be_truthy + expect(new_note.confidential).to be_truthy end end context 'when the user is allowed (issuable author)' do - let(:another_user) { create(:user) } - let(:issue) { create(:issue, author: another_user) } - - subject { described_class.new(project, another_user, note: 'Test', noteable: issue, confidential: true).execute } + let(:user) { create(:user) } + let(:issue) { create(:issue, author: user) } + let(:params) { { confidential: true, noteable: issue } } it 'note `confidential` flag is set to `true`' do - expect(subject.confidential).to be_truthy + expect(new_note.confidential).to be_truthy end end context 'when the user is allowed (admin)' do before do - enable_admin_mode!(another_user) + enable_admin_mode!(admin) end - let(:another_user) { create(:admin) } - - subject { described_class.new(project, another_user, note: 'Test', noteable: merge_request, confidential: true).execute } + let(:admin) { create(:admin) } + let(:params) { { confidential: true, noteable: merge_request } } it 'note `confidential` flag is set to `true`' do - expect(subject.confidential).to be_truthy + expect(new_note.confidential).to be_truthy end end context 'when the user is not allowed' do - let(:another_user) { create(:user) } - - subject { described_class.new(project, another_user, note: 'Test', noteable: merge_request, confidential: true).execute } + let(:user) { create(:user) } + let(:params) { { confidential: true, noteable: merge_request } } it 'note `confidential` flag is set to `false`' do - expect(subject.confidential).to be_falsey + expect(new_note.confidential).to be_falsey end end end context 'when the `confidential` note flag is set to `false`' do - subject { described_class.new(project, author, note: 'Test', noteable: merge_request, confidential: false).execute } + let(:params) { { confidential: false, noteable: merge_request } } it 'note `confidential` flag is set to `false`' do - expect(subject.confidential).to be_falsey + expect(new_note.confidential).to be_falsey end end end end - it 'builds a note without saving it' do - new_note = described_class.new(project, - author, - noteable_type: note.noteable_type, - noteable_id: note.noteable_id, - note: 'Test').execute - expect(new_note).to be_valid - expect(new_note).not_to be_persisted + context 'when noteable is not set' do + let(:params) { { noteable_type: note.noteable_type, noteable_id: note.noteable_id } } + + it 'builds a note without saving it' do + expect(new_note).to be_valid + expect(new_note).not_to be_persisted + end end end end diff --git a/tests.yml b/tests.yml index d24cc44a403..15b1814df3a 100644 --- a/tests.yml +++ b/tests.yml @@ -52,3 +52,7 @@ mapping: # EE/FOSS factory should map to factories spec - source: (ee/)?spec/factories/.+\.rb test: spec/factories_spec.rb + + # Whats New should map to its respective spec + - source: data/whats_new/\w*.yml + test: spec/lib/release_highlights/validator_spec.rb diff --git a/tooling/danger/helper.rb b/tooling/danger/helper.rb index 96a51806d31..ef5b2e16bb0 100644 --- a/tooling/danger/helper.rb +++ b/tooling/danger/helper.rb @@ -209,6 +209,7 @@ module Tooling %r{\Adoc/.*(\.(md|png|gif|jpg))\z} => :docs, %r{\A(CONTRIBUTING|LICENSE|MAINTENANCE|PHILOSOPHY|PROCESS|README)(\.md)?\z} => :docs, + %r{\Adata/whats_new/} => :docs, %r{\A(ee/)?app/(assets|views)/} => :frontend, %r{\A(ee/)?public/} => :frontend, diff --git a/yarn.lock b/yarn.lock index f85f6161e4b..a38dd3822a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -847,10 +847,10 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@eslint/eslintrc@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.3.0.tgz#d736d6963d7003b6514e6324bec9c602ac340318" - integrity sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg== +"@eslint/eslintrc@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.0.tgz#99cc0a0584d72f1df38b900fb062ba995f395547" + integrity sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog== dependencies: ajv "^6.12.4" debug "^4.1.1" @@ -859,7 +859,6 @@ ignore "^4.0.6" import-fresh "^3.2.1" js-yaml "^3.13.1" - lodash "^4.17.20" minimatch "^3.0.4" strip-json-comments "^3.1.1" @@ -4791,13 +4790,13 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint@7.20.0: - version "7.20.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.20.0.tgz#db07c4ca4eda2e2316e7aa57ac7fc91ec550bdc7" - integrity sha512-qGi0CTcOGP2OtCQBgWZlQjcTuP0XkIpYFj25XtRTQSHC+umNnp7UMshr2G8SLsRFYDdAPFeHOsiteadmMH02Yw== +eslint@7.21.0: + version "7.21.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.21.0.tgz#4ecd5b8c5b44f5dedc9b8a110b01bbfeb15d1c83" + integrity sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg== dependencies: "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.3.0" + "@eslint/eslintrc" "^0.4.0" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -4810,7 +4809,7 @@ eslint@7.20.0: espree "^7.3.1" esquery "^1.4.0" esutils "^2.0.2" - file-entry-cache "^6.0.0" + file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" globals "^12.1.0" @@ -5173,10 +5172,10 @@ figgy-pudding@^3.5.1: resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== -file-entry-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.0.tgz#7921a89c391c6d93efec2169ac6bf300c527ea0a" - integrity sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA== +file-entry-cache@^6.0.0, file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: flat-cache "^3.0.4" |