Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
1707659118
commit
a158bebe03
|
|
@ -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}`);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
module Packages
|
||||
DuplicatePackageError = Class.new(StandardError)
|
||||
|
||||
def self.table_name_prefix
|
||||
'packages_'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add setting to allow or disallow duplicates for generic packages
|
||||
merge_request: 60664
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add kubernetes_agent_proxy_request to usage ping
|
||||
merge_request: 60978
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
c2b5ad6786e1c71ccff391b03fcd0635dfd42d69484443291a692cef9f3ffda5
|
||||
|
|
@ -0,0 +1 @@
|
|||
e0898e4e439cde4e3b84808e7505490fe956cf17922f5c779b3384997d36cafd
|
||||
|
|
@ -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))
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
|
||||
|
|
|
|||
|
|
@ -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**.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
- Before you commit, in both unstaged and staged state.
|
||||
- After you committed.
|
||||
|
||||
- Undo changes after they are pushed to a remote repository:
|
||||
|
||||
- [Without history modification](#undo-remote-changes-without-changing-history) (preferred way).
|
||||
- [With history modification](#undo-remote-changes-with-modifying-history) (requires
|
||||
- [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).
|
||||
- [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).
|
||||
|
||||
### Branching strategy
|
||||
|
||||
[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.
|
||||
|
||||
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.
|
||||
|
||||
[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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## Undo local changes
|
||||
|
||||
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.
|
||||
Until you push your changes to a remote repository, changes
|
||||
you make in Git are only in your local development environment.
|
||||
|
||||
### Unstaged local changes (before you commit)
|
||||
### Undo unstaged local changes before you commit
|
||||
|
||||
When a change is made, but not added to the staged tree, Git
|
||||
proposes a solution to discard changes to the file.
|
||||
When you make a change, but have not yet staged it, you can undo your work.
|
||||
|
||||
Suppose you edited a file to change the content using your favorite editor:
|
||||
1. Confirm that the file is unstaged (that you did not use `git add <file>`) by running `git status`:
|
||||
|
||||
```shell
|
||||
vim <file>
|
||||
```
|
||||
```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)
|
||||
|
||||
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:
|
||||
modified: <file>
|
||||
no changes added to commit (use "git add" and/or "git commit -a")
|
||||
```
|
||||
|
||||
```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)
|
||||
1. Choose an option and undo your changes:
|
||||
|
||||
modified: <file>
|
||||
no changes added to commit (use "git add" and/or "git commit -a")
|
||||
```
|
||||
- To overwrite local changes:
|
||||
|
||||
At this point there are 3 options to undo the local changes you have:
|
||||
```shell
|
||||
git checkout -- <file>
|
||||
```
|
||||
|
||||
- Discard all local changes, but save them for possible re-use [later](#quickly-save-local-changes):
|
||||
- To save local changes so you can [re-use them later](#quickly-save-local-changes):
|
||||
|
||||
```shell
|
||||
git stash
|
||||
```
|
||||
```shell
|
||||
git stash
|
||||
```
|
||||
|
||||
- Discarding local changes (permanently) to a file:
|
||||
- To discard local changes to all files, permanently:
|
||||
|
||||
```shell
|
||||
git checkout -- <file>
|
||||
```
|
||||
```shell
|
||||
git reset --hard
|
||||
```
|
||||
|
||||
- Discard all local changes to all files permanently:
|
||||
### Undo staged local changes before you commit
|
||||
|
||||
```shell
|
||||
git reset --hard
|
||||
```
|
||||
If you added a file to staging, you can undo it.
|
||||
|
||||
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).
|
||||
1. Confirm that the file is staged (that you used `git add <file>`) by running `git status`:
|
||||
|
||||
```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)
|
||||
|
||||
new file: <file>
|
||||
```
|
||||
|
||||
1. Choose an option and undo your changes:
|
||||
|
||||
- To unstage the file but keep your changes:
|
||||
|
||||
```shell
|
||||
git restore --staged <file>
|
||||
```
|
||||
|
||||
- To unstage everything but keep your changes:
|
||||
|
||||
```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:
|
||||
|
||||
```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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
it_behaves_like 'a redis usage counter with totals', :kubernetes_agent, gitops_sync: 1
|
||||
|
||||
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)
|
||||
|
||||
expect(described_class.totals).to eq(kubernetes_agent_gitops_sync: 9)
|
||||
describe '.increment_event_counts' do
|
||||
let(:events) do
|
||||
{
|
||||
'gitops_sync' => 1,
|
||||
'k8s_api_proxy_request' => 2
|
||||
}
|
||||
end
|
||||
|
||||
it 'raises for negative numbers' do
|
||||
expect { described_class.increment_gitops_sync(-1) }.to raise_error(ArgumentError)
|
||||
subject { described_class.increment_event_counts(events) }
|
||||
|
||||
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: 3, kubernetes_agent_k8s_api_proxy_request: 6)
|
||||
end
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
||||
|
|
|
|||
|
|
@ -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 all files matching the path' do
|
||||
expect(result).to contain_exactly('files/images/logo-black.png',
|
||||
'files/images/logo-white.png')
|
||||
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 an extension with wildcard' do
|
||||
let(:path) { '*.rb' }
|
||||
context 'when specifying a wildcard path for all' do
|
||||
let(:path) { '**.md' }
|
||||
|
||||
it 'returns all files matching the extension' do
|
||||
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/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 a wildcard path for an unknown number of subfolder levels' do
|
||||
let(:path) { '**/*.rb' }
|
||||
|
||||
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' }
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
Loading…
Reference in New Issue