diff options
39 files changed, 543 insertions, 233 deletions
diff --git a/app/assets/javascripts/pipelines/components/graph_shared/api.js b/app/assets/javascripts/pipelines/components/graph_shared/api.js index 49cd04d11e9..0fe7d9ffda3 100644 --- a/app/assets/javascripts/pipelines/components/graph_shared/api.js +++ b/app/assets/javascripts/pipelines/components/graph_shared/api.js @@ -2,6 +2,11 @@ import axios from '~/lib/utils/axios_utils'; import { reportToSentry } from '../../utils'; export const reportPerformance = (path, stats) => { + // FIXME: https://gitlab.com/gitlab-org/gitlab/-/issues/330245 + if (!path) { + return; + } + axios.post(path, stats).catch((err) => { reportToSentry('links_inner_perf', `error: ${err}`); }); diff --git a/app/graphql/mutations/namespace/package_settings/update.rb b/app/graphql/mutations/namespace/package_settings/update.rb index ca21c3418fc..75c80cfbd3e 100644 --- a/app/graphql/mutations/namespace/package_settings/update.rb +++ b/app/graphql/mutations/namespace/package_settings/update.rb @@ -25,6 +25,16 @@ module Mutations required: false, description: copy_field_description(Types::Namespace::PackageSettingsType, :maven_duplicate_exception_regex) + argument :generic_duplicates_allowed, + GraphQL::BOOLEAN_TYPE, + required: false, + description: copy_field_description(Types::Namespace::PackageSettingsType, :generic_duplicates_allowed) + + argument :generic_duplicate_exception_regex, + Types::UntrustedRegexp, + required: false, + description: copy_field_description(Types::Namespace::PackageSettingsType, :generic_duplicate_exception_regex) + field :package_settings, Types::Namespace::PackageSettingsType, null: true, diff --git a/app/graphql/types/namespace/package_settings_type.rb b/app/graphql/types/namespace/package_settings_type.rb index 0720a1cfb4b..af091515979 100644 --- a/app/graphql/types/namespace/package_settings_type.rb +++ b/app/graphql/types/namespace/package_settings_type.rb @@ -10,5 +10,7 @@ module Types field :maven_duplicates_allowed, GraphQL::BOOLEAN_TYPE, null: false, description: 'Indicates whether duplicate Maven packages are allowed for this namespace.' field :maven_duplicate_exception_regex, Types::UntrustedRegexp, null: true, description: 'When maven_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect.' + field :generic_duplicates_allowed, GraphQL::BOOLEAN_TYPE, null: false, description: 'Indicates whether duplicate generic packages are allowed for this namespace.' + field :generic_duplicate_exception_regex, Types::UntrustedRegexp, null: true, description: 'When generic_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect.' end end diff --git a/app/models/namespace/package_setting.rb b/app/models/namespace/package_setting.rb index ce0b736e9a5..881b2f3acb3 100644 --- a/app/models/namespace/package_setting.rb +++ b/app/models/namespace/package_setting.rb @@ -6,13 +6,15 @@ class Namespace::PackageSetting < ApplicationRecord PackageSettingNotImplemented = Class.new(StandardError) - PACKAGES_WITH_SETTINGS = %w[maven].freeze + PACKAGES_WITH_SETTINGS = %w[maven generic].freeze belongs_to :namespace, inverse_of: :package_setting_relation validates :namespace, presence: true validates :maven_duplicates_allowed, inclusion: { in: [true, false] } validates :maven_duplicate_exception_regex, untrusted_regexp: true, length: { maximum: 255 } + validates :generic_duplicates_allowed, inclusion: { in: [true, false] } + validates :generic_duplicate_exception_regex, untrusted_regexp: true, length: { maximum: 255 } class << self def duplicates_allowed?(package) diff --git a/app/models/packages.rb b/app/models/packages.rb index e14c9290093..19490d23ce4 100644 --- a/app/models/packages.rb +++ b/app/models/packages.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true module Packages + DuplicatePackageError = Class.new(StandardError) + def self.table_name_prefix 'packages_' end diff --git a/app/models/repository.rb b/app/models/repository.rb index 9d2e514f377..9411cbc35fc 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -995,7 +995,13 @@ class Repository def search_files_by_wildcard_path(path, ref = 'HEAD') # We need to use RE2 to match Gitaly's regexp engine - regexp_string = RE2::Regexp.escape(path).gsub('\*', '.*?') + regexp_string = RE2::Regexp.escape(path) + + anything = '.*?' + anything_but_not_slash = '([^\/])*?' + regexp_string.gsub!('\*\*', anything) + regexp_string.gsub!('\*', anything_but_not_slash) + raw_repository.search_files_by_regexp("^#{regexp_string}$", ref) end diff --git a/app/services/namespaces/package_settings/update_service.rb b/app/services/namespaces/package_settings/update_service.rb index 0964963647a..cbadbe5c907 100644 --- a/app/services/namespaces/package_settings/update_service.rb +++ b/app/services/namespaces/package_settings/update_service.rb @@ -5,7 +5,10 @@ module Namespaces class UpdateService < BaseContainerService include Gitlab::Utils::StrongMemoize - ALLOWED_ATTRIBUTES = %i[maven_duplicates_allowed maven_duplicate_exception_regex].freeze + ALLOWED_ATTRIBUTES = %i[maven_duplicates_allowed + maven_duplicate_exception_regex + generic_duplicates_allowed + generic_duplicate_exception_regex].freeze def execute return ServiceResponse.error(message: 'Access Denied', http_status: 403) unless allowed? diff --git a/app/services/packages/generic/create_package_file_service.rb b/app/services/packages/generic/create_package_file_service.rb index 1451a022a39..42a191fb415 100644 --- a/app/services/packages/generic/create_package_file_service.rb +++ b/app/services/packages/generic/create_package_file_service.rb @@ -23,6 +23,10 @@ module Packages .new(project, current_user, package_params) .execute + unless Namespace::PackageSetting.duplicates_allowed?(package) + raise ::Packages::DuplicatePackageError if target_file_is_duplicate?(package) + end + package.update_column(:status, params[:status]) if params[:status] && params[:status] != package.status package.build_infos.safe_find_or_create_by!(pipeline: params[:build].pipeline) if params[:build].present? @@ -40,6 +44,10 @@ module Packages ::Packages::CreatePackageFileService.new(package, file_params).execute end + + def target_file_is_duplicate?(package) + package.package_files.with_file_name(params[:file_name]).exists? + end end end end diff --git a/app/views/shared/snippets/_snippet.html.haml b/app/views/shared/snippets/_snippet.html.haml index 52cf0248f21..4e373dda013 100644 --- a/app/views/shared/snippets/_snippet.html.haml +++ b/app/views/shared/snippets/_snippet.html.haml @@ -8,7 +8,7 @@ = link_to gitlab_snippet_path(snippet) do = snippet.title - %ul.controls + %ul.controls{ data: { qa_selector: 'snippet_file_count_content', qa_snippet_files: snippet.statistics&.file_count } } %li = snippet_file_count(snippet) %li @@ -16,7 +16,7 @@ = sprite_icon('comments', css_class: 'gl-vertical-align-text-bottom') = notes_count %li - %span.sr-only + %span.sr-only{ data: { qa_selector: 'snippet_visibility_content', qa_snippet_visibility: visibility_level_label(snippet.visibility_level) } } = visibility_level_label(snippet.visibility_level) = visibility_level_icon(snippet.visibility_level) diff --git a/changelogs/unreleased/293755-generic-dupe-settings.yml b/changelogs/unreleased/293755-generic-dupe-settings.yml new file mode 100644 index 00000000000..fd2e5a8d1d8 --- /dev/null +++ b/changelogs/unreleased/293755-generic-dupe-settings.yml @@ -0,0 +1,5 @@ +--- +title: Add setting to allow or disallow duplicates for generic packages +merge_request: 60664 +author: +type: added diff --git a/changelogs/unreleased/327414-agent-proxy-request-count.yml b/changelogs/unreleased/327414-agent-proxy-request-count.yml new file mode 100644 index 00000000000..1f3bf7027b6 --- /dev/null +++ b/changelogs/unreleased/327414-agent-proxy-request-count.yml @@ -0,0 +1,5 @@ +--- +title: Add kubernetes_agent_proxy_request to usage ping +merge_request: 60978 +author: +type: changed diff --git a/db/migrate/20210429192653_add_generic_package_duplicate_settings_to_namespace_package_settings.rb b/db/migrate/20210429192653_add_generic_package_duplicate_settings_to_namespace_package_settings.rb new file mode 100644 index 00000000000..8c328eb52fc --- /dev/null +++ b/db/migrate/20210429192653_add_generic_package_duplicate_settings_to_namespace_package_settings.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class AddGenericPackageDuplicateSettingsToNamespacePackageSettings < ActiveRecord::Migration[6.0] + # rubocop:disable Migration/AddLimitToTextColumns + # limit is added in 20210429193106_add_text_limit_to_namespace_package_settings_generic_duplicate_exception_regex + def change + add_column :namespace_package_settings, :generic_duplicates_allowed, :boolean, null: false, default: true + add_column :namespace_package_settings, :generic_duplicate_exception_regex, :text, null: false, default: '' + end + # rubocop:enable Migration/AddLimitToTextColumns +end diff --git a/db/migrate/20210429193106_add_text_limit_to_namespace_package_settings_generic_duplicate_exception_regex.rb b/db/migrate/20210429193106_add_text_limit_to_namespace_package_settings_generic_duplicate_exception_regex.rb new file mode 100644 index 00000000000..5d02ad3e2f5 --- /dev/null +++ b/db/migrate/20210429193106_add_text_limit_to_namespace_package_settings_generic_duplicate_exception_regex.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class AddTextLimitToNamespacePackageSettingsGenericDuplicateExceptionRegex < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + disable_ddl_transaction! + + def up + add_text_limit :namespace_package_settings, :generic_duplicate_exception_regex, 255 + end + + def down + remove_text_limit :namespace_package_settings, :generic_duplicate_exception_regex + end +end diff --git a/db/schema_migrations/20210429192653 b/db/schema_migrations/20210429192653 new file mode 100644 index 00000000000..5e380380d72 --- /dev/null +++ b/db/schema_migrations/20210429192653 @@ -0,0 +1 @@ +c2b5ad6786e1c71ccff391b03fcd0635dfd42d69484443291a692cef9f3ffda5
\ No newline at end of file diff --git a/db/schema_migrations/20210429193106 b/db/schema_migrations/20210429193106 new file mode 100644 index 00000000000..49f1838a585 --- /dev/null +++ b/db/schema_migrations/20210429193106 @@ -0,0 +1 @@ +e0898e4e439cde4e3b84808e7505490fe956cf17922f5c779b3384997d36cafd
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 5bf73b4955a..ce42f7362ca 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -14798,6 +14798,9 @@ CREATE TABLE namespace_package_settings ( namespace_id bigint NOT NULL, maven_duplicates_allowed boolean DEFAULT true NOT NULL, maven_duplicate_exception_regex text DEFAULT ''::text NOT NULL, + generic_duplicates_allowed boolean DEFAULT true NOT NULL, + generic_duplicate_exception_regex text DEFAULT ''::text NOT NULL, + CONSTRAINT check_31340211b1 CHECK ((char_length(generic_duplicate_exception_regex) <= 255)), CONSTRAINT check_d63274b2b6 CHECK ((char_length(maven_duplicate_exception_regex) <= 255)) ); diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index be5de5c86ac..28f91c7139e 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -3911,6 +3911,8 @@ Input type: `UpdateNamespacePackageSettingsInput` | Name | Type | Description | | ---- | ---- | ----------- | | <a id="mutationupdatenamespacepackagesettingsclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| <a id="mutationupdatenamespacepackagesettingsgenericduplicateexceptionregex"></a>`genericDuplicateExceptionRegex` | [`UntrustedRegexp`](#untrustedregexp) | When generic_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect. | +| <a id="mutationupdatenamespacepackagesettingsgenericduplicatesallowed"></a>`genericDuplicatesAllowed` | [`Boolean`](#boolean) | Indicates whether duplicate generic packages are allowed for this namespace. | | <a id="mutationupdatenamespacepackagesettingsmavenduplicateexceptionregex"></a>`mavenDuplicateExceptionRegex` | [`UntrustedRegexp`](#untrustedregexp) | When maven_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect. | | <a id="mutationupdatenamespacepackagesettingsmavenduplicatesallowed"></a>`mavenDuplicatesAllowed` | [`Boolean`](#boolean) | Indicates whether duplicate Maven packages are allowed for this namespace. | | <a id="mutationupdatenamespacepackagesettingsnamespacepath"></a>`namespacePath` | [`ID!`](#id) | The namespace path where the namespace package setting is located. | @@ -10488,6 +10490,8 @@ Namespace-level Package Registry settings. | Name | Type | Description | | ---- | ---- | ----------- | +| <a id="packagesettingsgenericduplicateexceptionregex"></a>`genericDuplicateExceptionRegex` | [`UntrustedRegexp`](#untrustedregexp) | When generic_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect. | +| <a id="packagesettingsgenericduplicatesallowed"></a>`genericDuplicatesAllowed` | [`Boolean!`](#boolean) | Indicates whether duplicate generic packages are allowed for this namespace. | | <a id="packagesettingsmavenduplicateexceptionregex"></a>`mavenDuplicateExceptionRegex` | [`UntrustedRegexp`](#untrustedregexp) | When maven_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect. | | <a id="packagesettingsmavenduplicatesallowed"></a>`mavenDuplicatesAllowed` | [`Boolean!`](#boolean) | Indicates whether duplicate Maven packages are allowed for this namespace. | diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index f815bfbc5a9..23cdce8187b 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -487,7 +487,7 @@ Use local includes instead of symbolic links. > - It's not recommended for production use. > - To use it in GitLab self-managed instances, ask a GitLab administrator to enable it. **(CORE ONLY)** -You can use wildcard paths (`*`) with `include:local`. +You can use wildcard paths (`*` and `**`) with `include:local`. Example: @@ -495,7 +495,19 @@ Example: include: 'configs/*.yml' ``` -When the pipeline runs, it adds all `.yml` files in the `configs` folder into the pipeline configuration. +When the pipeline runs, GitLab: + +- Adds all `.yml` files in the `configs` directory into the pipeline configuration. +- Does not add `.yml` files in subfolders of the `configs` directory. To allow this, + add the following configuration: + + ```yaml + # This matches all `.yml` files in `configs` and any subfolder in it. + include: 'configs/**.yml' + + # This matches all `.yml` files only in subfolders of `configs`. + include: 'configs/**/*.yml' + ``` The wildcard file paths feature is under development and not ready for production use. It is deployed behind a feature flag that is **disabled by default**. diff --git a/doc/development/internal_api.md b/doc/development/internal_api.md index 7cf740d16a0..57dd1c0a65b 100644 --- a/doc/development/internal_api.md +++ b/doc/development/internal_api.md @@ -453,7 +453,8 @@ metric counters. | Attribute | Type | Required | Description | |:----------|:-------|:---------|:------------| -| `gitops_sync_count` | integer| yes | The number to increase the `gitops_sync_count` counter by | +| `gitops_sync_count` | integer| no | The number to increase the `gitops_sync_count` counter by | +| `k8s_api_proxy_request_count` | integer| no | The number to increase the `k8s_api_proxy_request_count` counter by | ```plaintext POST /internal/kubernetes/usage_metrics diff --git a/doc/development/usage_ping/dictionary.md b/doc/development/usage_ping/dictionary.md index 8c60a768b46..80816bf583a 100644 --- a/doc/development/usage_ping/dictionary.md +++ b/doc/development/usage_ping/dictionary.md @@ -2902,6 +2902,18 @@ Status: `data_available` Tiers: `premium`, `ultimate` +### `counts.kubernetes_agent_k8s_api_proxy_request` + +Count of Kubernetes API proxy requests + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_all/20210505015532_kubernetes_agent_k8s_api_proxy_request.yml) + +Group: `group::configure` + +Status: `implemented` + +Tiers: `premium`, `ultimate` + ### `counts.kubernetes_agents` Count of Kubernetes agents diff --git a/doc/topics/git/numerous_undo_possibilities_in_git/index.md b/doc/topics/git/numerous_undo_possibilities_in_git/index.md index eb1c0a597f3..2426fa2f7cb 100644 --- a/doc/topics/git/numerous_undo_possibilities_in_git/index.md +++ b/doc/topics/git/numerous_undo_possibilities_in_git/index.md @@ -5,139 +5,134 @@ info: To determine the technical writer assigned to the Stage/Group associated w type: howto --- -# Numerous undo possibilities in Git **(FREE)** +# Undo possibilities in Git **(FREE)** -This tutorial shows you different ways of undoing your work in Git. -We assume you have a basic working knowledge of Git. Check the GitLab -[Git documentation](../index.md) for reference. +[Nothing in Git is deleted](https://git-scm.com/book/en/v2/Git-Internals-Maintenance-and-Data-Recovery), +so when you work in Git, you can undo your work. -We only provide some general information about the commands to get you started. -For more advanced examples, refer to the [Git book](https://git-scm.com/book/en/v2). +All version control systems have options for undoing work. However, +because of the de-centralized nature of Git, these options are multiplied. +The actions you take are based on the +[stage of development](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) +you are in. -A few different techniques exist to undo your changes, based on the stage -of the change in your current development. Remember that -[nothing in Git is really deleted](https://git-scm.com/book/en/v2/Git-Internals-Maintenance-and-Data-Recovery). -Until Git cleans detached commits - commits that cannot be accessed by branch or tag - -you can view them with `git reflog` command, and access them with direct commit ID. -Read more about [redoing the undo](#redoing-the-undo) in the section below. +For more information about working with Git and GitLab: -> For more information about working with Git and GitLab: -> -> - <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> Learn why [North Western Mutual chose GitLab](https://youtu.be/kPNMyxKRRoM) for their Enterprise source code management. -> - Learn how to [get started with Git](https://about.gitlab.com/resources/whitepaper-moving-to-git/). +- <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> Learn why [North Western Mutual chose GitLab](https://youtu.be/kPNMyxKRRoM) for their enterprise source code management. +- Learn how to [get started with Git](https://about.gitlab.com/resources/whitepaper-moving-to-git/). +- For more advanced examples, refer to the [Git book](https://git-scm.com/book/en/v2). -## Introduction +## When you can undo changes -This guide is organized depending on the [stage of development](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository): +In the standard Git workflow: -- Where do you want to undo your changes from? -- Were they shared with other developers? +1. You create or edit a file. It starts in the unstaged state. If it's new, it is not yet tracked by Git. +1. You add the file to your local repository (`git add`), which puts the file into the staged state. +1. You commit the file to your local repository (`git commit`). +1. You can then share the file with other developers, by committing to a remote repository (`git push`). -Because Git tracks changes, a created or edited file is in the unstaged state -(if created it is untracked by Git). After you add it to a repository (`git add`) you put -a file into the **staged** state, which is then committed (`git commit`) to your -local repository. After that, file can be shared with other developers (`git push`). -This tutorial covers: +You can undo changes at any point in this workflow: -- [Undo local changes](#undo-local-changes) which were not pushed to a remote repository: +- [When you're working locally](#undo-local-changes) and haven't yet pushed to a remote repository. +- When you have already pushed to a remote repository and you want to: + - [Keep the history intact](#undo-remote-changes-without-changing-history) (preferred). + - [Change the history](#undo-remote-changes-with-modifying-history) (requires + coordination with team and force pushes). - - Before you commit, in both unstaged and staged state. - - After you committed. +## Undo local changes -- Undo changes after they are pushed to a remote repository: +Until you push your changes to a remote repository, changes +you make in Git are only in your local development environment. - - [Without history modification](#undo-remote-changes-without-changing-history) (preferred way). - - [With history modification](#undo-remote-changes-with-modifying-history) (requires - coordination with team and force pushes). - - [Use cases when modifying history is generally acceptable](#where-modifying-history-is-generally-acceptable). - - [How to modify history](#how-modifying-history-is-done). - - [How to remove sensitive information from repository](#deleting-sensitive-information-from-commits). +### Undo unstaged local changes before you commit -### Branching strategy +When you make a change, but have not yet staged it, you can undo your work. -[Git](https://git-scm.com/) is a de-centralized version control system. Beside regular -versioning of the whole repository, it has possibilities to exchange changes -with other repositories. +1. Confirm that the file is unstaged (that you did not use `git add <file>`) by running `git status`: -To avoid chaos with -[multiple sources of truth](https://git-scm.com/about/distributed), various -development workflows have to be followed. It depends on your internal -workflow how certain changes or commits can be undone or changed. + ```shell + $ git status + On branch main + Your branch is up-to-date with 'origin/main'. + Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) -[GitLab Flow](https://about.gitlab.com/topics/version-control/what-is-gitlab-flow/) provides a good -balance between developers clashing with each other while -developing the same feature and cooperating seamlessly. However, it does not enable -joined development of the same feature by multiple developers by default. + modified: <file> + no changes added to commit (use "git add" and/or "git commit -a") + ``` -When multiple developers develop the same feature on the same branch, clashing -with every synchronization is unavoidable. However, a proper or chosen Git Workflow -prevents lost or out-of-sync data when the feature is complete. +1. Choose an option and undo your changes: -You can also -read through this blog post on [Git Tips & Tricks](https://about.gitlab.com/blog/2016/12/08/git-tips-and-tricks/) -to learn how to do things in Git. + - To overwrite local changes: -## Undo local changes + ```shell + git checkout -- <file> + ``` -Until you push your changes to any remote repository, they only affect you. -That broadens your options on how to handle undoing them. Still, local changes -can be on various stages and each stage has a different approach on how to tackle them. + - To save local changes so you can [re-use them later](#quickly-save-local-changes): -### Unstaged local changes (before you commit) + ```shell + git stash + ``` -When a change is made, but not added to the staged tree, Git -proposes a solution to discard changes to the file. + - To discard local changes to all files, permanently: -Suppose you edited a file to change the content using your favorite editor: + ```shell + git reset --hard + ``` -```shell -vim <file> -``` +### Undo staged local changes before you commit -Because you did not `git add <file>` to staging, it should be under unstaged files (or -untracked if file was created). You can confirm that with: +If you added a file to staging, you can undo it. -```shell -$ git status -On branch master -Your branch is up-to-date with 'origin/master'. -Changes not staged for commit: - (use "git add <file>..." to update what will be committed) - (use "git checkout -- <file>..." to discard changes in working directory) - - modified: <file> -no changes added to commit (use "git add" and/or "git commit -a") -``` +1. Confirm that the file is staged (that you used `git add <file>`) by running `git status`: -At this point there are 3 options to undo the local changes you have: + ```shell + $ git status + On branch main + Your branch is up-to-date with 'origin/main'. + Changes to be committed: + (use "git restore --staged <file>..." to unstage) -- Discard all local changes, but save them for possible re-use [later](#quickly-save-local-changes): + new file: <file> + ``` - ```shell - git stash - ``` +1. Choose an option and undo your changes: -- Discarding local changes (permanently) to a file: + - To unstage the file but keep your changes: - ```shell - git checkout -- <file> - ``` + ```shell + git restore --staged <file> + ``` -- Discard all local changes to all files permanently: + - To unstage everything but keep your changes: - ```shell - git reset --hard - ``` + ```shell + git reset + ``` + + - To unstage the file to current commit (HEAD): + + ```shell + git reset HEAD <file> + ``` + + - To discard all local changes, but save them for [later](#quickly-save-local-changes): + + ```shell + git stash + ``` + + - To discard everything permanently: -Before executing `git reset --hard`, keep in mind that there is also a way to -just temporary store the changes without committing them using `git stash`. -This command resets the changes to all files, but it also saves them in case -you would like to apply them at some later time. You can read more about it in -[section below](#quickly-save-local-changes). + ```shell + git reset --hard + ``` ### Quickly save local changes -You are working on a feature when a boss drops by with an urgent task. Because your +You are working on a feature when someone drops by with an urgent task. Because your feature is not complete, but you need to swap to another branch, you can use `git stash` to: @@ -159,60 +154,7 @@ additional options like: - `git stash pop`, which redoes previously stashed changes and removes them from stashed list - `git stash apply`, which redoes previously stashed changes, but keeps them on stashed list -### Staged local changes (before you commit) - -If you add some files to staging, but you want to remove them from the -current commit while retaining those changes, move them outside -of the staging tree. You can also discard all changes with -`git reset --hard` or think about `git stash` [as described earlier.](#quickly-save-local-changes) - -Lets start the example by editing a file with your favorite editor to change the -content and add it to staging: - -```shell -vim <file> -git add <file> -``` - -The file is now added to staging as confirmed by `git status` command: - -```shell -$ git status -On branch master -Your branch is up-to-date with 'origin/master'. -Changes to be committed: - (use "git reset HEAD <file>..." to unstage) - - new file: <file> -``` - -Now you have 4 options to undo your changes: - -- Unstage the file to current commit (HEAD): - - ```shell - git reset HEAD <file> - ``` - -- Unstage everything - retain changes: - - ```shell - git reset - ``` - -- Discard all local changes, but save them for [later](#quickly-save-local-changes): - - ```shell - git stash - ``` - -- Discard everything permanently: - - ```shell - git reset --hard - ``` - -## Committed local changes +## Undo committed local changes After you commit, your changes are recorded by the version control system. Because you haven't pushed to your remote repository yet, your changes are @@ -289,6 +231,9 @@ these options to remove all or part of it from our repository: ### With history modification +You can rewrite history in Git, but you should avoid it, because it can cause problems +when multiple developers are contributing to the same codebase. + There is one command for history modification and that is `git rebase`. Command provides interactive mode (`-i` flag) which enables you to: @@ -335,7 +280,7 @@ In case you want to modify something introduced in commit `B`. You can find some more examples in the section explaining [how to modify history](#how-modifying-history-is-done). -### Redoing the Undo +### Redoing the undo Sometimes you realize that the changes you undid were useful and you want them back. Well because of first paragraph you are in luck. Command `git reflog` @@ -501,14 +446,6 @@ feature set as `git filter-branch` does, but focus on specific use cases. Refer [Reduce repository size](../../../user/project/repository/reducing_the_repo_size_using_git.md) page to know more about purging files from repository history & GitLab storage. -## Conclusion - -Various options exist for undoing your work with any version control system, but -because of the de-centralized nature of Git, these options are multiplied (or limited) -depending on the stage of your process. Git also enables rewriting history, but that -should be avoided as it might cause problems when multiple developers are -contributing to the same codebase. - <!-- ## Troubleshooting Include any troubleshooting steps that you can foresee. If you know beforehand what issues diff --git a/lib/api/generic_packages.rb b/lib/api/generic_packages.rb index cce55fa92d9..d0680ad7bc5 100644 --- a/lib/api/generic_packages.rb +++ b/lib/api/generic_packages.rb @@ -74,6 +74,8 @@ module API Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: project.id }) forbidden! + rescue ::Packages::DuplicatePackageError + bad_request!('Duplicate package is not allowed') end desc 'Download package file' do diff --git a/lib/api/internal/kubernetes.rb b/lib/api/internal/kubernetes.rb index af2c53dd778..ca8e041b319 100644 --- a/lib/api/internal/kubernetes.rb +++ b/lib/api/internal/kubernetes.rb @@ -107,18 +107,18 @@ module API detail 'Updates usage metrics for agent' end params do - requires :gitops_sync_count, type: Integer, desc: 'The count to increment the gitops_sync metric by' + optional :gitops_sync_count, type: Integer, desc: 'The count to increment the gitops_sync metric by' + optional :k8s_api_proxy_request_count, type: Integer, desc: 'The count to increment the k8s_api_proxy_request_count metric by' end post '/' do - gitops_sync_count = params[:gitops_sync_count] + events = params.slice(:gitops_sync_count, :k8s_api_proxy_request_count) + events.transform_keys! { |event| event.to_s.chomp('_count') } - if gitops_sync_count < 0 - bad_request!('gitops_sync_count must be greater than or equal to zero') - else - Gitlab::UsageDataCounters::KubernetesAgentCounter.increment_gitops_sync(gitops_sync_count) + Gitlab::UsageDataCounters::KubernetesAgentCounter.increment_event_counts(events) - no_content! - end + no_content! + rescue ArgumentError => e + bad_request!(e.message) end end end diff --git a/lib/gitlab/usage_data_counters/kubernetes_agent_counter.rb b/lib/gitlab/usage_data_counters/kubernetes_agent_counter.rb index eae42bdc4a1..8b9ca0fc220 100644 --- a/lib/gitlab/usage_data_counters/kubernetes_agent_counter.rb +++ b/lib/gitlab/usage_data_counters/kubernetes_agent_counter.rb @@ -4,17 +4,27 @@ module Gitlab module UsageDataCounters class KubernetesAgentCounter < BaseCounter PREFIX = 'kubernetes_agent' - KNOWN_EVENTS = %w[gitops_sync].freeze + KNOWN_EVENTS = %w[gitops_sync k8s_api_proxy_request].freeze class << self - def increment_gitops_sync(incr) - raise ArgumentError, 'must be greater than or equal to zero' if incr < 0 + def increment_event_counts(events) + validate!(events) - # rather then hitting redis for this no-op, we return early - # note: redis returns the increment, so we mimic this here - return 0 if incr == 0 + events.each do |event, incr| + # rather then hitting redis for this no-op, we return early + next if incr == 0 - increment_by(redis_key(:gitops_sync), incr) + increment_by(redis_key(event), incr) + end + end + + private + + def validate!(events) + events.each do |event, incr| + raise ArgumentError, "unknown event #{event}" unless event.in?(KNOWN_EVENTS) + raise ArgumentError, "#{event} count must be greater than or equal to zero" if incr < 0 + end end end end diff --git a/qa/qa/page/dashboard/snippet/index.rb b/qa/qa/page/dashboard/snippet/index.rb index 8c4abfdf606..63589c376f6 100644 --- a/qa/qa/page/dashboard/snippet/index.rb +++ b/qa/qa/page/dashboard/snippet/index.rb @@ -10,10 +10,32 @@ module QA element :global_new_snippet_link end + view 'app/views/shared/snippets/_snippet.html.haml' do + element :snippet_link + element :snippet_visibility_content + element :snippet_file_count_content + end + def go_to_new_snippet_page click_element :new_menu_toggle click_element :global_new_snippet_link end + + def has_snippet_title?(snippet_title) + has_element?(:snippet_link, snippet_title: snippet_title) + end + + def has_visibility_level?(snippet_title, visibility) + within_element(:snippet_link, snippet_title: snippet_title) do + has_element?(:snippet_visibility_content, snippet_visibility: visibility) + end + end + + def has_number_of_files?(snippet_title, number) + within_element(:snippet_link, snippet_title: snippet_title) do + has_element?(:snippet_file_count_content, snippet_files: number) + end + end end end end diff --git a/qa/qa/resource/snippet.rb b/qa/qa/resource/snippet.rb index 6423dc7a41c..a94ae02b8fd 100644 --- a/qa/qa/resource/snippet.rb +++ b/qa/qa/resource/snippet.rb @@ -3,7 +3,7 @@ module QA module Resource class Snippet < Base - attr_accessor :title, :description, :file_content, :visibility, :file_name + attr_accessor :title, :description, :file_content, :visibility, :file_name, :files attribute :id attribute :http_url_to_repo diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb new file mode 100644 index 00000000000..9c7dc868a2e --- /dev/null +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +module QA + RSpec.describe 'Create' do + describe 'Snippet index page' do + let(:personal_snippet_with_single_file) do + Resource::Snippet.fabricate_via_api! do |snippet| + snippet.title = "Personal snippet with one file-#{SecureRandom.hex(8)}" + snippet.visibility = 'Public' + end + end + + let(:personal_snippet_with_multiple_files) do + Resource::Snippet.fabricate_via_api! do |snippet| + snippet.title = "Personal snippet with multiple files-#{SecureRandom.hex(8)}" + snippet.visibility = 'Private' + snippet.file_name = 'First file name' + snippet.file_content = 'first file content' + + snippet.add_files do |files| + files.append(name: 'Second file name', content: 'second file content') + files.append(name: 'Third file name', content: 'third file content') + end + end + end + + let(:project_snippet_with_single_file) do + Resource::ProjectSnippet.fabricate_via_api! do |snippet| + snippet.title = "Project snippet with one file-#{SecureRandom.hex(8)}" + snippet.visibility = 'Private' + end + end + + let(:project_snippet_with_multiple_files) do + Resource::ProjectSnippet.fabricate_via_api! do |snippet| + snippet.title = "Project snippet with multiple files-#{SecureRandom.hex(8)}" + snippet.visibility = 'Public' + snippet.file_name = 'First file name' + snippet.file_content = 'first file content' + + snippet.add_files do |files| + files.append(name: 'Second file name', content: 'second file content') + files.append(name: 'Third file name', content: 'third file content') + end + end + end + + before do + Flow::Login.sign_in + end + + after do + personal_snippet_with_single_file.remove_via_api! + personal_snippet_with_multiple_files.remove_via_api! + project_snippet_with_single_file.remove_via_api! + project_snippet_with_multiple_files.remove_via_api! + end + + shared_examples 'displaying details on index page' do |snippet_type| + it "shows correct details of #{snippet_type} including file number" do + send(snippet_type) + Page::Main::Menu.perform do |menu| + menu.go_to_more_dropdown_option(:snippets_link) + end + + Page::Dashboard::Snippet::Index.perform do |snippet| + aggregate_failures 'file content verification' do + expect(snippet).to have_snippet_title(send(snippet_type).title) + expect(snippet).to have_visibility_level(send(snippet_type).title, send(snippet_type).visibility) + expect(snippet).to have_number_of_files(send(snippet_type).title, send(snippet_type).files.count) + end + end + end + end + + it_behaves_like 'displaying details on index page', :personal_snippet_with_single_file + it_behaves_like 'displaying details on index page', :personal_snippet_with_multiple_files + it_behaves_like 'displaying details on index page', :project_snippet_with_single_file + it_behaves_like 'displaying details on index page', :project_snippet_with_multiple_files + end + end +end diff --git a/spec/factories/namespace_package_settings.rb b/spec/factories/namespace_package_settings.rb index 875933ce84f..042808f042f 100644 --- a/spec/factories/namespace_package_settings.rb +++ b/spec/factories/namespace_package_settings.rb @@ -7,6 +7,9 @@ FactoryBot.define do maven_duplicates_allowed { true } maven_duplicate_exception_regex { 'SNAPSHOT' } + generic_duplicates_allowed { true } + generic_duplicate_exception_regex { 'foo' } + trait :group do namespace { association(:group) } end diff --git a/spec/frontend/pipelines/graph_shared/links_layer_spec.js b/spec/frontend/pipelines/graph_shared/links_layer_spec.js index 511aba0acaf..932a19f2f00 100644 --- a/spec/frontend/pipelines/graph_shared/links_layer_spec.js +++ b/spec/frontend/pipelines/graph_shared/links_layer_spec.js @@ -96,23 +96,19 @@ describe('links layer component', () => { }); describe('performance metrics', () => { + const metricsPath = '/root/project/-/ci/prometheus_metrics/histograms.json'; let markAndMeasure; let reportToSentry; let reportPerformance; let mock; beforeEach(() => { - mock = new MockAdapter(axios); jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => cb()); markAndMeasure = jest.spyOn(perfUtils, 'performanceMarkAndMeasure'); reportToSentry = jest.spyOn(sentryUtils, 'reportToSentry'); reportPerformance = jest.spyOn(Api, 'reportPerformance'); }); - afterEach(() => { - mock.restore(); - }); - describe('with no metrics config object', () => { beforeEach(() => { createComponent(); @@ -164,7 +160,6 @@ describe('links layer component', () => { }); describe('with metrics path and collect set to true', () => { - const metricsPath = '/root/project/-/ci/prometheus_metrics/histograms.json'; const duration = 875; const numLinks = 7; const totalGroups = 8; @@ -204,6 +199,9 @@ describe('links layer component', () => { describe('with duration and no error', () => { beforeEach(() => { + mock = new MockAdapter(axios); + mock.onPost(metricsPath).reply(200, {}); + jest.spyOn(window.performance, 'getEntriesByName').mockImplementation(() => { return [{ duration }]; }); @@ -218,6 +216,10 @@ describe('links layer component', () => { }); }); + afterEach(() => { + mock.restore(); + }); + it('it calls reportPerformance with expected arguments', () => { expect(markAndMeasure).toHaveBeenCalled(); expect(reportPerformance).toHaveBeenCalled(); diff --git a/spec/graphql/mutations/namespace/package_settings/update_spec.rb b/spec/graphql/mutations/namespace/package_settings/update_spec.rb index bd0d38cb49f..978c81fadfa 100644 --- a/spec/graphql/mutations/namespace/package_settings/update_spec.rb +++ b/spec/graphql/mutations/namespace/package_settings/update_spec.rb @@ -25,7 +25,9 @@ RSpec.describe Mutations::Namespace::PackageSettings::Update do end RSpec.shared_examples 'updating the namespace package setting' do - it_behaves_like 'updating the namespace package setting attributes', from: { maven_duplicates_allowed: true, maven_duplicate_exception_regex: 'SNAPSHOT' }, to: { maven_duplicates_allowed: false, maven_duplicate_exception_regex: 'RELEASE' } + it_behaves_like 'updating the namespace package setting attributes', + from: { maven_duplicates_allowed: true, maven_duplicate_exception_regex: 'SNAPSHOT', generic_duplicates_allowed: true, generic_duplicate_exception_regex: 'foo' }, + to: { maven_duplicates_allowed: false, maven_duplicate_exception_regex: 'RELEASE', generic_duplicates_allowed: false, generic_duplicate_exception_regex: 'bar' } it_behaves_like 'returning a success' @@ -56,7 +58,13 @@ RSpec.describe Mutations::Namespace::PackageSettings::Update do context 'with existing namespace package setting' do let_it_be(:package_settings) { create(:namespace_package_setting, namespace: namespace) } - let_it_be(:params) { { namespace_path: namespace.full_path, maven_duplicates_allowed: false, maven_duplicate_exception_regex: 'RELEASE' } } + let_it_be(:params) do + { namespace_path: namespace.full_path, + maven_duplicates_allowed: false, + maven_duplicate_exception_regex: 'RELEASE', + generic_duplicates_allowed: false, + generic_duplicate_exception_regex: 'bar' } + end where(:user_role, :shared_examples_name) do :maintainer | 'updating the namespace package setting' diff --git a/spec/lib/gitlab/usage_data_counters/kubernetes_agent_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/kubernetes_agent_counter_spec.rb index 8f9a3e0cd9e..e7edb8b9cf1 100644 --- a/spec/lib/gitlab/usage_data_counters/kubernetes_agent_counter_spec.rb +++ b/spec/lib/gitlab/usage_data_counters/kubernetes_agent_counter_spec.rb @@ -3,21 +3,57 @@ require 'spec_helper' RSpec.describe Gitlab::UsageDataCounters::KubernetesAgentCounter do - it_behaves_like 'a redis usage counter', 'Kubernetes Agent', :gitops_sync + described_class::KNOWN_EVENTS.each do |event| + it_behaves_like 'a redis usage counter', 'Kubernetes Agent', event + it_behaves_like 'a redis usage counter with totals', :kubernetes_agent, event => 1 + end + + describe '.increment_event_counts' do + let(:events) do + { + 'gitops_sync' => 1, + 'k8s_api_proxy_request' => 2 + } + end - it_behaves_like 'a redis usage counter with totals', :kubernetes_agent, gitops_sync: 1 + subject { described_class.increment_event_counts(events) } - describe '.increment_gitops_sync' do - it 'increments the gtops_sync counter by the new increment amount' do - described_class.increment_gitops_sync(7) - described_class.increment_gitops_sync(2) - described_class.increment_gitops_sync(0) + it 'increments the specified counters by the new increment amount' do + described_class.increment_event_counts(events) + described_class.increment_event_counts(events) + described_class.increment_event_counts(events) - expect(described_class.totals).to eq(kubernetes_agent_gitops_sync: 9) + expect(described_class.totals).to eq(kubernetes_agent_gitops_sync: 3, kubernetes_agent_k8s_api_proxy_request: 6) end - it 'raises for negative numbers' do - expect { described_class.increment_gitops_sync(-1) }.to raise_error(ArgumentError) + context 'event is unknown' do + let(:events) do + { + 'gitops_sync' => 1, + 'other_event' => 2 + } + end + + it 'raises an ArgumentError' do + expect(described_class).not_to receive(:increment_by) + + expect { subject }.to raise_error(ArgumentError, 'unknown event other_event') + end + end + + context 'increment is negative' do + let(:events) do + { + 'gitops_sync' => -1, + 'k8s_api_proxy_request' => 2 + } + end + + it 'raises an ArgumentError' do + expect(described_class).not_to receive(:increment_by) + + expect { subject }.to raise_error(ArgumentError, 'gitops_sync count must be greater than or equal to zero') + end end end end diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 9be0ca71bcb..306537b69d1 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -774,6 +774,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do subject { described_class.usage_counters } it { is_expected.to include(:kubernetes_agent_gitops_sync) } + it { is_expected.to include(:kubernetes_agent_k8s_api_proxy_request) } it { is_expected.to include(:static_site_editor_views) } it { is_expected.to include(:package_events_i_package_pull_package) } it { is_expected.to include(:package_events_i_package_delete_package_by_user) } diff --git a/spec/models/namespace/package_setting_spec.rb b/spec/models/namespace/package_setting_spec.rb index a6dcb316796..4308c8c06bc 100644 --- a/spec/models/namespace/package_setting_spec.rb +++ b/spec/models/namespace/package_setting_spec.rb @@ -14,9 +14,12 @@ RSpec.describe Namespace::PackageSetting do it { is_expected.to allow_value(true).for(:maven_duplicates_allowed) } it { is_expected.to allow_value(false).for(:maven_duplicates_allowed) } it { is_expected.not_to allow_value(nil).for(:maven_duplicates_allowed) } + it { is_expected.to allow_value(true).for(:generic_duplicates_allowed) } + it { is_expected.to allow_value(false).for(:generic_duplicates_allowed) } + it { is_expected.not_to allow_value(nil).for(:generic_duplicates_allowed) } end - describe '#maven_duplicate_exception_regex' do + describe 'regex values' do let_it_be(:package_settings) { create(:namespace_package_setting) } subject { package_settings } @@ -24,12 +27,14 @@ RSpec.describe Namespace::PackageSetting do valid_regexps = %w[SNAPSHOT .* v.+ v10.1.* (?:v.+|SNAPSHOT|TEMP)] invalid_regexps = ['[', '(?:v.+|SNAPSHOT|TEMP'] - valid_regexps.each do |valid_regexp| - it { is_expected.to allow_value(valid_regexp).for(:maven_duplicate_exception_regex) } - end + [:maven_duplicate_exception_regex, :generic_duplicate_exception_regex].each do |attribute| + valid_regexps.each do |valid_regexp| + it { is_expected.to allow_value(valid_regexp).for(attribute) } + end - invalid_regexps.each do |invalid_regexp| - it { is_expected.not_to allow_value(invalid_regexp).for(:maven_duplicate_exception_regex) } + invalid_regexps.each do |invalid_regexp| + it { is_expected.not_to allow_value(invalid_regexp).for(attribute) } + end end end end @@ -41,7 +46,7 @@ RSpec.describe Namespace::PackageSetting do context 'package types with package_settings' do # As more package types gain settings they will be added to this list - [:maven_package].each do |format| + [:maven_package, :generic_package].each do |format| let_it_be(:package) { create(format, name: 'foo', version: 'beta') } # rubocop:disable Rails/SaveBang let_it_be(:package_type) { package.package_type } let_it_be(:package_setting) { package.project.namespace.package_settings } @@ -70,7 +75,7 @@ RSpec.describe Namespace::PackageSetting do end context 'package types without package_settings' do - [:npm_package, :conan_package, :nuget_package, :pypi_package, :composer_package, :generic_package, :golang_package, :debian_package].each do |format| + [:npm_package, :conan_package, :nuget_package, :pypi_package, :composer_package, :golang_package, :debian_package].each do |format| let_it_be(:package) { create(format) } # rubocop:disable Rails/SaveBang let_it_be(:package_setting) { package.project.namespace.package_settings } diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index a739f523008..7748846f6a5 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1006,19 +1006,58 @@ RSpec.describe Repository do end end - context 'when specifying a path with wildcard' do - let(:path) { 'files/*/*.png' } + context 'when specifying a wildcard path' do + let(:path) { '*.md' } + + it 'returns files matching the path in the root folder' do + expect(result).to contain_exactly('CONTRIBUTING.md', + 'MAINTENANCE.md', + 'PROCESS.md', + 'README.md') + end + end + + context 'when specifying a wildcard path for all' do + let(:path) { '**.md' } + + it 'returns all matching files in all folders' do + expect(result).to contain_exactly('CONTRIBUTING.md', + 'MAINTENANCE.md', + 'PROCESS.md', + 'README.md', + 'files/markdown/ruby-style-guide.md', + 'with space/README.md') + end + end + + context 'when specifying a path to subfolders using two asterisks and a slash' do + let(:path) { 'files/**/*.md' } it 'returns all files matching the path' do - expect(result).to contain_exactly('files/images/logo-black.png', - 'files/images/logo-white.png') + expect(result).to contain_exactly('files/markdown/ruby-style-guide.md') + end + end + + context 'when specifying a wildcard path to subfolder with just two asterisks' do + let(:path) { 'files/**.md' } + + it 'returns all files in the matching path' do + expect(result).to contain_exactly('files/markdown/ruby-style-guide.md') + end + end + + context 'when specifying a wildcard path to subfolder with one asterisk' do + let(:path) { 'files/*/*.md' } + + it 'returns all files in the matching path' do + expect(result).to contain_exactly('files/markdown/ruby-style-guide.md') end end - context 'when specifying an extension with wildcard' do - let(:path) { '*.rb' } + context 'when specifying a wildcard path for an unknown number of subfolder levels' do + let(:path) { '**/*.rb' } - it 'returns all files matching the extension' do + it 'returns all matched files in all subfolders' do expect(result).to contain_exactly('encoding/russian.rb', 'files/ruby/popen.rb', 'files/ruby/regex.rb', @@ -1026,6 +1065,14 @@ RSpec.describe Repository do end end + context 'when specifying a wildcard path to one level of subfolders' do + let(:path) { '*/*.rb' } + + it 'returns all matched files in one subfolder' do + expect(result).to contain_exactly('encoding/russian.rb') + end + end + context 'when sending regexp' do let(:path) { '.*\.rb' } diff --git a/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb b/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb index 749373e7b8d..202e7e7c333 100644 --- a/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb +++ b/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb @@ -12,7 +12,9 @@ RSpec.describe 'Updating the package settings' do { namespace_path: namespace.full_path, maven_duplicates_allowed: false, - maven_duplicate_exception_regex: 'foo-.*' + maven_duplicate_exception_regex: 'foo-.*', + generic_duplicates_allowed: false, + generic_duplicate_exception_regex: 'bar-.*' } end @@ -22,6 +24,8 @@ RSpec.describe 'Updating the package settings' do packageSettings { mavenDuplicatesAllowed mavenDuplicateExceptionRegex + genericDuplicatesAllowed + genericDuplicateExceptionRegex } errors QL @@ -40,6 +44,8 @@ RSpec.describe 'Updating the package settings' do expect(mutation_response['errors']).to be_empty expect(package_settings_response['mavenDuplicatesAllowed']).to eq(params[:maven_duplicates_allowed]) expect(package_settings_response['mavenDuplicateExceptionRegex']).to eq(params[:maven_duplicate_exception_regex]) + expect(package_settings_response['genericDuplicatesAllowed']).to eq(params[:generic_duplicates_allowed]) + expect(package_settings_response['genericDuplicateExceptionRegex']).to eq(params[:generic_duplicate_exception_regex]) end end @@ -69,8 +75,8 @@ RSpec.describe 'Updating the package settings' do RSpec.shared_examples 'accepting the mutation request updating the package settings' do it_behaves_like 'updating the namespace package setting attributes', - from: { maven_duplicates_allowed: true, maven_duplicate_exception_regex: 'SNAPSHOT' }, - to: { maven_duplicates_allowed: false, maven_duplicate_exception_regex: 'foo-.*' } + from: { maven_duplicates_allowed: true, maven_duplicate_exception_regex: 'SNAPSHOT', generic_duplicates_allowed: true, generic_duplicate_exception_regex: 'foo' }, + to: { maven_duplicates_allowed: false, maven_duplicate_exception_regex: 'foo-.*', generic_duplicates_allowed: false, generic_duplicate_exception_regex: 'bar-.*' } it_behaves_like 'returning a success' it_behaves_like 'rejecting invalid regex' diff --git a/spec/requests/api/internal/kubernetes_spec.rb b/spec/requests/api/internal/kubernetes_spec.rb index 47d0c872eb6..7a2cec974b9 100644 --- a/spec/requests/api/internal/kubernetes_spec.rb +++ b/spec/requests/api/internal/kubernetes_spec.rb @@ -67,26 +67,26 @@ RSpec.describe API::Internal::Kubernetes do context 'is authenticated for an agent' do let!(:agent_token) { create(:cluster_agent_token) } - it 'returns no_content for valid gitops_sync_count' do - send_request(params: { gitops_sync_count: 10 }) + it 'returns no_content for valid events' do + send_request(params: { gitops_sync_count: 10, k8s_api_proxy_request_count: 5 }) expect(response).to have_gitlab_http_status(:no_content) end - it 'returns no_content 0 gitops_sync_count' do - send_request(params: { gitops_sync_count: 0 }) + it 'returns no_content for counts of zero' do + send_request(params: { gitops_sync_count: 0, k8s_api_proxy_request_count: 0 }) expect(response).to have_gitlab_http_status(:no_content) end it 'returns 400 for non number' do - send_request(params: { gitops_sync_count: 'string' }) + send_request(params: { gitops_sync_count: 'string', k8s_api_proxy_request_count: 1 }) expect(response).to have_gitlab_http_status(:bad_request) end it 'returns 400 for negative number' do - send_request(params: { gitops_sync_count: '-1' }) + send_request(params: { gitops_sync_count: -1, k8s_api_proxy_request_count: 1 }) expect(response).to have_gitlab_http_status(:bad_request) end diff --git a/spec/services/namespaces/package_settings/update_service_spec.rb b/spec/services/namespaces/package_settings/update_service_spec.rb index fa0c58e4c9b..030bc03038e 100644 --- a/spec/services/namespaces/package_settings/update_service_spec.rb +++ b/spec/services/namespaces/package_settings/update_service_spec.rb @@ -32,7 +32,9 @@ RSpec.describe ::Namespaces::PackageSettings::UpdateService do end shared_examples 'updating the namespace package setting' do - it_behaves_like 'updating the namespace package setting attributes', from: { maven_duplicates_allowed: true, maven_duplicate_exception_regex: 'SNAPSHOT' }, to: { maven_duplicates_allowed: false, maven_duplicate_exception_regex: 'RELEASE' } + it_behaves_like 'updating the namespace package setting attributes', + from: { maven_duplicates_allowed: true, maven_duplicate_exception_regex: 'SNAPSHOT', generic_duplicates_allowed: true, generic_duplicate_exception_regex: 'foo' }, + to: { maven_duplicates_allowed: false, maven_duplicate_exception_regex: 'RELEASE', generic_duplicates_allowed: false, generic_duplicate_exception_regex: 'bar' } it_behaves_like 'returning a success' @@ -60,7 +62,12 @@ RSpec.describe ::Namespaces::PackageSettings::UpdateService do context 'with existing namespace package setting' do let_it_be(:package_settings) { create(:namespace_package_setting, namespace: namespace) } - let_it_be(:params) { { maven_duplicates_allowed: false, maven_duplicate_exception_regex: 'RELEASE' } } + let_it_be(:params) do + { maven_duplicates_allowed: false, + maven_duplicate_exception_regex: 'RELEASE', + generic_duplicates_allowed: false, + generic_duplicate_exception_regex: 'bar' } + end where(:user_role, :shared_examples_name) do :maintainer | 'updating the namespace package setting' diff --git a/spec/services/packages/generic/create_package_file_service_spec.rb b/spec/services/packages/generic/create_package_file_service_spec.rb index 10c54369f26..1c9eb53cfc7 100644 --- a/spec/services/packages/generic/create_package_file_service_spec.rb +++ b/spec/services/packages/generic/create_package_file_service_spec.rb @@ -6,13 +6,16 @@ RSpec.describe Packages::Generic::CreatePackageFileService do let_it_be(:project) { create(:project) } let_it_be(:user) { create(:user) } let_it_be(:pipeline) { create(:ci_pipeline, user: user) } + let_it_be(:file_name) { 'myfile.tar.gz.1' } + let(:build) { double('build', pipeline: pipeline) } describe '#execute' do + let_it_be(:package) { create(:generic_package, project: project) } + let(:sha256) { '440e5e148a25331bbd7991575f7d54933c0ebf6cc735a18ee5066ac1381bb590' } let(:temp_file) { Tempfile.new("test") } let(:file) { UploadedFile.new(temp_file.path, sha256: sha256) } - let(:package) { create(:generic_package, project: project) } let(:package_service) { double } let(:params) do @@ -20,7 +23,7 @@ RSpec.describe Packages::Generic::CreatePackageFileService do package_name: 'mypackage', package_version: '0.0.1', file: file, - file_name: 'myfile.tar.gz.1', + file_name: file_name, build: build } end @@ -34,7 +37,7 @@ RSpec.describe Packages::Generic::CreatePackageFileService do } end - subject { described_class.new(project, user, params).execute } + subject(:execute_service) { described_class.new(project, user, params).execute } before do FileUtils.touch(temp_file) @@ -47,14 +50,14 @@ RSpec.describe Packages::Generic::CreatePackageFileService do end it 'creates package file', :aggregate_failures do - expect { subject }.to change { package.package_files.count }.by(1) + expect { execute_service }.to change { package.package_files.count }.by(1) .and change { Packages::PackageFileBuildInfo.count }.by(1) package_file = package.package_files.last aggregate_failures do expect(package_file.package.status).to eq('default') expect(package_file.package).to eq(package) - expect(package_file.file_name).to eq('myfile.tar.gz.1') + expect(package_file.file_name).to eq(file_name) expect(package_file.size).to eq(file.size) expect(package_file.file_sha256).to eq(sha256) end @@ -65,7 +68,7 @@ RSpec.describe Packages::Generic::CreatePackageFileService do let(:package_params) { super().merge(status: 'hidden') } it 'updates an existing packages status' do - expect { subject }.to change { package.package_files.count }.by(1) + expect { execute_service }.to change { package.package_files.count }.by(1) .and change { Packages::PackageFileBuildInfo.count }.by(1) package_file = package.package_files.last @@ -76,5 +79,32 @@ RSpec.describe Packages::Generic::CreatePackageFileService do end it_behaves_like 'assigns build to package file' + + context 'with existing package' do + before do + create(:package_file, package: package, file_name: file_name) + end + + it { expect { execute_service }.to change { project.package_files.count }.by(1) } + + context 'when duplicates are not allowed' do + before do + package.project.namespace.package_settings.update!(generic_duplicates_allowed: false) + end + + it 'does not allow duplicates' do + expect { execute_service }.to raise_error(::Packages::DuplicatePackageError) + .and change { project.package_files.count }.by(0) + end + + context 'when the package name matches the exception regex' do + before do + package.project.namespace.package_settings.update!(generic_duplicate_exception_regex: '.*') + end + + it { expect { execute_service }.to change { project.package_files.count }.by(1) } + end + end + end end end diff --git a/spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb b/spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb index 8398dd3c453..f7a6bd3676a 100644 --- a/spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb +++ b/spec/support/shared_examples/services/namespace_package_settings_shared_examples.rb @@ -7,6 +7,8 @@ RSpec.shared_examples 'updating the namespace package setting attributes' do |fr expect { subject } .to change { namespace.package_settings.reload.maven_duplicates_allowed }.from(from[:maven_duplicates_allowed]).to(to[:maven_duplicates_allowed]) .and change { namespace.package_settings.reload.maven_duplicate_exception_regex }.from(from[:maven_duplicate_exception_regex]).to(to[:maven_duplicate_exception_regex]) + .and change { namespace.package_settings.reload.generic_duplicates_allowed }.from(from[:generic_duplicates_allowed]).to(to[:generic_duplicates_allowed]) + .and change { namespace.package_settings.reload.generic_duplicate_exception_regex }.from(from[:generic_duplicate_exception_regex]).to(to[:generic_duplicate_exception_regex]) end end @@ -26,6 +28,8 @@ RSpec.shared_examples 'creating the namespace package setting' do expect(namespace.package_setting_relation.maven_duplicates_allowed).to eq(package_settings[:maven_duplicates_allowed]) expect(namespace.package_setting_relation.maven_duplicate_exception_regex).to eq(package_settings[:maven_duplicate_exception_regex]) + expect(namespace.package_setting_relation.generic_duplicates_allowed).to eq(package_settings[:generic_duplicates_allowed]) + expect(namespace.package_setting_relation.generic_duplicate_exception_regex).to eq(package_settings[:generic_duplicate_exception_regex]) end it_behaves_like 'returning a success' |