Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
25096948f0
commit
276941b2c4
|
@ -1,5 +1,6 @@
|
|||
<script>
|
||||
import { GlDropdown, GlDropdownItem, GlIcon, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
|
||||
import { formatTime } from '~/lib/utils/datetime_utility';
|
||||
import { __, s__, sprintf } from '~/locale';
|
||||
import eventHub from '../event_hub';
|
||||
|
@ -37,7 +38,7 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
onClickAction(action) {
|
||||
async onClickAction(action) {
|
||||
if (action.scheduledAt) {
|
||||
const confirmationMessage = sprintf(
|
||||
s__(
|
||||
|
@ -45,9 +46,10 @@ export default {
|
|||
),
|
||||
{ jobName: action.name },
|
||||
);
|
||||
// https://gitlab.com/gitlab-org/gitlab-foss/issues/52156
|
||||
// eslint-disable-next-line no-alert
|
||||
if (!window.confirm(confirmationMessage)) {
|
||||
|
||||
const confirmed = await confirmAction(confirmationMessage);
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ class Packages::Pypi::Metadatum < ApplicationRecord
|
|||
belongs_to :package, -> { where(package_type: :pypi) }, inverse_of: :pypi_metadatum
|
||||
|
||||
validates :package, presence: true
|
||||
validates :required_python, length: { maximum: 255 }, allow_blank: true
|
||||
validates :required_python, length: { maximum: 255 }, allow_nil: false
|
||||
|
||||
validate :pypi_package_type
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ module Packages
|
|||
::Packages::Package.transaction do
|
||||
meta = Packages::Pypi::Metadatum.new(
|
||||
package: created_package,
|
||||
required_python: params[:requires_python]
|
||||
required_python: params[:requires_python] || ''
|
||||
)
|
||||
|
||||
unless meta.valid?
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
- unless Gitlab::Metrics.metrics_folder_present?
|
||||
.form-text.text-muted
|
||||
%strong.cred= _("WARNING:")
|
||||
= _("Environment variable %{code_start}%{environment_variable}%{code_end} does not exist or is not pointing to a valid directory.").html_safe % { environment_variable: prometheus_multiproc_dir, code_start: '<code>'.html_safe, code_end: '</code>'.html_safe }
|
||||
= _("Environment variable %{environment_variable} does not exist or is not pointing to a valid directory.").html_safe % { environment_variable: '<code>prometheus_multiproc_dir</code>'.html_safe }
|
||||
= link_to sprite_icon('question-o'), help_page_path('administration/monitoring/prometheus/gitlab_metrics', anchor: 'metrics-shared-directory')
|
||||
.form-group
|
||||
= f.label :metrics_method_call_threshold, _('Method call threshold (ms)'), class: 'label-bold'
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
.text-secondary
|
||||
= s_('ProjectSettings|Every merge creates a merge commit.')
|
||||
%br
|
||||
= s_('ProjectSettings|Fast-forward merges only.')
|
||||
= s_('ProjectSettings|Merging is only allowed when the source branch is up-to-date with its target.')
|
||||
%br
|
||||
= s_('ProjectSettings|When there is a merge conflict, the user is given the option to rebase.')
|
||||
= s_('ProjectSettings|When semi-linear merge is not possible, the user is given the option to rebase.')
|
||||
|
||||
.form-check.mb-2
|
||||
= form.radio_button :merge_method, :ff, class: "js-merge-method-radio form-check-input", data: { qa_selector: 'merge_ff_radio' }
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddDefaultToRequiredPythonOnPackagesPypiMetadata < Gitlab::Database::Migration[1.0]
|
||||
def up
|
||||
change_column_default(:packages_pypi_metadata, :required_python, '')
|
||||
end
|
||||
|
||||
def down
|
||||
change_column_default(:packages_pypi_metadata, :required_python, nil)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DropOldIndexSecurityCiBuildsOnNameAndIdParserFeatures < Gitlab::Database::Migration[1.0]
|
||||
TABLE = "ci_builds"
|
||||
COLUMNS = %i[name id]
|
||||
INDEX_NAME = "index_security_ci_builds_on_name_and_id_parser_features_old"
|
||||
CONSTRAINTS = "(name::text = ANY (ARRAY['container_scanning'::character varying::text,
|
||||
'dast'::character varying::text,
|
||||
'dependency_scanning'::character varying::text,
|
||||
'license_management'::character varying::text,
|
||||
'sast'::character varying::text,
|
||||
'secret_detection'::character varying::text,
|
||||
'coverage_fuzzing'::character varying::text,
|
||||
'license_scanning'::character varying::text])
|
||||
) AND type::text = 'Ci::Build'::text"
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
remove_concurrent_index(TABLE, COLUMNS, name: INDEX_NAME, where: CONSTRAINTS)
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index(TABLE, COLUMNS, name: INDEX_NAME, where: CONSTRAINTS)
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
483f8299688a6e24fa77867b7dab8a2dad0c2b7ebe43c56c81c02ab1e0dc4674
|
|
@ -0,0 +1 @@
|
|||
c30b1b36ec83df1b4fdf0c3c28656b158beab4f2188875898182c2dfbd073c80
|
|
@ -18325,7 +18325,7 @@ ALTER SEQUENCE packages_packages_id_seq OWNED BY packages_packages.id;
|
|||
|
||||
CREATE TABLE packages_pypi_metadata (
|
||||
package_id bigint NOT NULL,
|
||||
required_python text,
|
||||
required_python text DEFAULT ''::text,
|
||||
CONSTRAINT check_0d9aed55b2 CHECK ((required_python IS NOT NULL)),
|
||||
CONSTRAINT check_379019d5da CHECK ((char_length(required_python) <= 255))
|
||||
);
|
||||
|
@ -28939,8 +28939,6 @@ CREATE INDEX index_secure_ci_builds_on_user_id_name_created_at ON ci_builds USIN
|
|||
|
||||
CREATE INDEX index_security_ci_builds_on_name_and_id_parser_features ON ci_builds USING btree (name, id) WHERE (((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('sast'::character varying)::text, ('secret_detection'::character varying)::text, ('coverage_fuzzing'::character varying)::text, ('license_scanning'::character varying)::text, ('apifuzzer_fuzz'::character varying)::text, ('apifuzzer_fuzz_dnd'::character varying)::text])) AND ((type)::text = 'Ci::Build'::text));
|
||||
|
||||
CREATE INDEX index_security_ci_builds_on_name_and_id_parser_features_old ON ci_builds USING btree (name, id) WHERE (((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('sast'::character varying)::text, ('secret_detection'::character varying)::text, ('coverage_fuzzing'::character varying)::text, ('license_scanning'::character varying)::text])) AND ((type)::text = 'Ci::Build'::text));
|
||||
|
||||
CREATE INDEX index_security_findings_on_confidence ON security_findings USING btree (confidence);
|
||||
|
||||
CREATE INDEX index_security_findings_on_project_fingerprint ON security_findings USING btree (project_fingerprint);
|
||||
|
|
|
@ -78,3 +78,184 @@ token is generated when the event destination is created and cannot be changed.
|
|||
|
||||
Each streamed event contains a random alphanumeric identifier for the `X-Gitlab-Event-Streaming-Token` HTTP header that can be verified against
|
||||
the destination's value when [listing streaming destinations](#list-currently-enabled-streaming-destinations).
|
||||
|
||||
## Audit event streaming on Git operations
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/332747) in GitLab 14.9 [with a flag](../administration/feature_flags.md) named `audit_event_streaming_git_operations`. Disabled by default.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](feature_flags.md) named `audit_event_streaming_git_operations`. On GitLab.com, this feature is not available.
|
||||
|
||||
Streaming audit events can be sent when signed-in users push or pull a project's remote Git repositories:
|
||||
|
||||
- [Using SSH](../ssh/index.md).
|
||||
- Using HTTP or HTTPS.
|
||||
- Using the **Download** button (**{download}**) in GitLab UI.
|
||||
|
||||
Audit events are not captured for users that are not signed in. For example, when downloading a public project.
|
||||
|
||||
To configure streaming audit events for Git operations, see [Add a new event streaming destination](#add-a-new-event-streaming-destination).
|
||||
|
||||
### Request headers
|
||||
|
||||
Request headers are formatted as follows:
|
||||
|
||||
```plaintext
|
||||
POST /logs HTTP/1.1
|
||||
Host: <DESTINATION_HOST>
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
X-Gitlab-Event-Streaming-Token: <DESTINATION_TOKEN>
|
||||
```
|
||||
|
||||
### Example responses for SSH events
|
||||
|
||||
Fetch:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"author_id": 1,
|
||||
"entity_id": 29,
|
||||
"entity_type": "Project",
|
||||
"details": {
|
||||
"author_name": "Administrator",
|
||||
"target_id": 29,
|
||||
"target_type": "Project",
|
||||
"target_details": "example-project",
|
||||
"custom_message": {
|
||||
"protocol": "ssh",
|
||||
"action": "git-upload-pack"
|
||||
},
|
||||
"ip_address": "127.0.0.1",
|
||||
"entity_path": "example-group/example-project"
|
||||
},
|
||||
"ip_address": "127.0.0.1",
|
||||
"author_name": "Administrator",
|
||||
"entity_path": "example-group/example-project",
|
||||
"target_details": "example-project",
|
||||
"created_at": "2022-02-23T06:21:05.283Z",
|
||||
"target_type": "Project",
|
||||
"target_id": 29
|
||||
}
|
||||
```
|
||||
|
||||
Push:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"author_id": 1,
|
||||
"entity_id": 29,
|
||||
"entity_type": "Project",
|
||||
"details": {
|
||||
"author_name": "Administrator",
|
||||
"target_id": 29,
|
||||
"target_type": "Project",
|
||||
"target_details": "example-project",
|
||||
"custom_message": {
|
||||
"protocol": "ssh",
|
||||
"action": "git-receive-pack"
|
||||
},
|
||||
"ip_address": "127.0.0.1",
|
||||
"entity_path": "example-group/example-project"
|
||||
},
|
||||
"ip_address": "127.0.0.1",
|
||||
"author_name": "Administrator",
|
||||
"entity_path": "example-group/example-project",
|
||||
"target_details": "example-project",
|
||||
"created_at": "2022-02-23T06:23:08.746Z",
|
||||
"target_type": "Project",
|
||||
"target_id": 29
|
||||
}
|
||||
```
|
||||
|
||||
### Example responses for HTTP and HTTPS events
|
||||
|
||||
Fetch:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"author_id": 1,
|
||||
"entity_id": 29,
|
||||
"entity_type": "Project",
|
||||
"details": {
|
||||
"author_name": "Administrator",
|
||||
"target_id": 29,
|
||||
"target_type": "Project",
|
||||
"target_details": "example-project",
|
||||
"custom_message": {
|
||||
"protocol": "http",
|
||||
"action": "git-upload-pack"
|
||||
},
|
||||
"ip_address": "127.0.0.1",
|
||||
"entity_path": "example-group/example-project"
|
||||
},
|
||||
"ip_address": "127.0.0.1",
|
||||
"author_name": "Administrator",
|
||||
"entity_path": "example-group/example-project",
|
||||
"target_details": "example-project",
|
||||
"created_at": "2022-02-23T06:25:43.938Z",
|
||||
"target_type": "Project",
|
||||
"target_id": 29
|
||||
}
|
||||
```
|
||||
|
||||
Push:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"author_id": 1,
|
||||
"entity_id": 29,
|
||||
"entity_type": "Project",
|
||||
"details": {
|
||||
"author_name": "Administrator",
|
||||
"target_id": 29,
|
||||
"target_type": "Project",
|
||||
"target_details": "example-project",
|
||||
"custom_message": {
|
||||
"protocol": "http",
|
||||
"action": "git-receive-pack"
|
||||
},
|
||||
"ip_address": "127.0.0.1",
|
||||
"entity_path": "example-group/example-project"
|
||||
},
|
||||
"ip_address": "127.0.0.1",
|
||||
"author_name": "Administrator",
|
||||
"entity_path": "example-group/example-project",
|
||||
"target_details": "example-project",
|
||||
"created_at": "2022-02-23T06:26:29.294Z",
|
||||
"target_type": "Project",
|
||||
"target_id": 29
|
||||
}
|
||||
```
|
||||
|
||||
### Example responses for events from GitLab UI download button
|
||||
|
||||
Fetch:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"author_id": 99,
|
||||
"entity_id": 29,
|
||||
"entity_type": "Project",
|
||||
"details": {
|
||||
"custom_message": "Repository Download Started",
|
||||
"author_name": "example_username",
|
||||
"target_id": 29,
|
||||
"target_type": "Project",
|
||||
"target_details": "example-group/example-project",
|
||||
"ip_address": "127.0.0.1",
|
||||
"entity_path": "example-group/example-project"
|
||||
},
|
||||
"ip_address": "127.0.0.1",
|
||||
"author_name": "example_username",
|
||||
"entity_path": "example-group/example-project",
|
||||
"target_details": "example-group/example-project",
|
||||
"created_at": "2022-02-23T06:27:17.873Z",
|
||||
"target_type": "Project",
|
||||
"target_id": 29
|
||||
}
|
||||
```
|
||||
|
|
|
@ -610,3 +610,45 @@ Possible solutions:
|
|||
|
||||
- Provision larger VMs to gain access to larger network traffic allowances.
|
||||
- Use your cloud service's monitoring and logging to check that the Praefect nodes are not exhausting their traffic allowances.
|
||||
|
||||
## Profiling Gitaly
|
||||
|
||||
Gitaly exposes several of Golang's built-in performance profiling tools on the Prometheus listen port. For example, if Prometheus is listening
|
||||
on port `9236` of the GitLab server:
|
||||
|
||||
- Get a list of running `goroutines` and their backtraces:
|
||||
|
||||
```shell
|
||||
curl --output goroutines.txt "http://<gitaly_server>:9236/debug/pprof/goroutine?debug=2"
|
||||
```
|
||||
|
||||
- Run a CPU profile for 30 seconds:
|
||||
|
||||
```shell
|
||||
curl --output cpu.bin "http://<gitaly_server>:9236/debug/pprof/profile"
|
||||
```
|
||||
|
||||
- Profile heap memory usage:
|
||||
|
||||
```shell
|
||||
curl --output heap.bin "http://<gitaly_server>:9236/debug/pprof/heap"
|
||||
```
|
||||
|
||||
- Record a 5 second execution trace. This will impact Gitaly's performance while running:
|
||||
|
||||
```shell
|
||||
curl --output trace.bin "http://<gitaly_server>:9236/debug/pprof/trace?seconds=5"
|
||||
```
|
||||
|
||||
On a host with `go` installed, the CPU profile and heap profile can be viewed in a browser:
|
||||
|
||||
```shell
|
||||
go tool pprof -http=:8001 cpu.bin
|
||||
go tool pprof -http=:8001 heap.bin
|
||||
```
|
||||
|
||||
Execution traces can be viewed by running:
|
||||
|
||||
```shell
|
||||
go tool trace heap.bin
|
||||
```
|
||||
|
|
|
@ -175,6 +175,7 @@ PUT projects/:id/packages/pypi
|
|||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | string | yes | The ID or full path of the project. |
|
||||
| `requires_python` | string | no | The PyPI required version. |
|
||||
|
||||
```shell
|
||||
curl --request PUT \
|
||||
|
|
|
@ -43,12 +43,7 @@ To create and enable a feature flag:
|
|||
1. Enter a name that starts with a letter and contains only lowercase letters, digits, underscores (`_`),
|
||||
or dashes (`-`), and does not end with a dash (`-`) or underscore (`_`).
|
||||
1. Optional. Enter a description (255 characters maximum).
|
||||
1. Enter details about how the flag should be applied:
|
||||
- In GitLab 13.0 and earlier, add **Environment specs**. For each environment,
|
||||
include the **Status** (default enabled) and [**Rollout strategy**](#rollout-strategy-legacy)
|
||||
(defaults to **All users**).
|
||||
- In GitLab 13.1 and later, add Feature Flag [**Strategies**](#feature-flag-strategies).
|
||||
For each strategy, include the **Type** (defaults to [**All users**](#all-users))
|
||||
1. Add Feature Flag [**Strategies**](#feature-flag-strategies) to define how the flag should be applied. For each strategy, include the **Type** (defaults to [**All users**](#all-users))
|
||||
and **Environments** (defaults to all environments).
|
||||
1. Select **Create feature flag**.
|
||||
|
||||
|
@ -235,35 +230,6 @@ To search for code references of a feature flag:
|
|||
1. Select **More actions** (**{ellipsis_v}**).
|
||||
1. Select **Search code references**.
|
||||
|
||||
## Rollout strategy (legacy)
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8240) in GitLab 12.2.
|
||||
> - [Made read-only](https://gitlab.com/gitlab-org/gitlab/-/issues/220228) in GitLab 13.4.
|
||||
|
||||
In GitLab 13.0 and earlier, the **Rollout strategy** setting affects which users experience
|
||||
the feature as enabled. Choose the percentage of users that the feature is enabled
|
||||
for. The rollout strategy has no effect if the environment spec is disabled.
|
||||
|
||||
It can be set to:
|
||||
|
||||
- All users
|
||||
- [Percent of users](#percent-of-users)
|
||||
- Optionally, you can click the **Include additional user IDs** checkbox and add a list
|
||||
of specific users IDs to enable the feature for.
|
||||
- [User IDs](#user-ids)
|
||||
|
||||
## Legacy feature flag migration
|
||||
|
||||
Legacy feature flags became read-only in GitLab 13.4. GitLab 14.0 removes support for legacy feature
|
||||
flags. You must migrate your legacy feature flags to the new version. To do so, follow these steps:
|
||||
|
||||
1. Take a screenshot of the legacy flag for tracking.
|
||||
1. Delete the flag through the API or UI (you don't need to alter the code).
|
||||
1. Create a new feature flag with the same name as the legacy flag you deleted. Make sure the
|
||||
strategies and environments match the deleted flag.
|
||||
|
||||
See [this video tutorial](https://www.youtube.com/watch?v=CAJY2IGep7Y) for help with this migration.
|
||||
|
||||
## Disable a feature flag for a specific environment
|
||||
|
||||
In [GitLab 13.0 and earlier](https://gitlab.com/gitlab-org/gitlab/-/issues/8621),
|
||||
|
|
|
@ -104,7 +104,7 @@ Optionally, you can [customize the one-liner installation command](#customize-th
|
|||
|
||||
By default, the one-liner command generated by GitLab:
|
||||
|
||||
- Creates a namespace for the deployment (`gitlab-kubernetes-agent`).
|
||||
- Creates a namespace for the deployment (`gitlab-agent`).
|
||||
- Sets up a service account with `cluster-admin` rights (see [how to restrict this service account](#customize-the-permissions-for-the-agentk-service-account)).
|
||||
- Creates a `Secret` resource for the agent's registration token.
|
||||
- Creates a `Deployment` resource for the `agentk` pod.
|
||||
|
|
|
@ -371,7 +371,7 @@ create a new group and transfer projects to it instead.
|
|||
To change your group path (group URL):
|
||||
|
||||
1. Go to your group's **Settings > General** page.
|
||||
1. Expand the **Path, transfer, remove** section.
|
||||
1. Expand the **Advanced** section.
|
||||
1. Under **Change group URL**, enter a new name.
|
||||
1. Select **Change group URL**.
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@ Personal access tokens are:
|
|||
- Used with a GitLab username to authenticate with GitLab features that require usernames. For example,
|
||||
[GitLab managed Terraform state backend](../infrastructure/iac/terraform_state.md#using-a-gitlab-managed-terraform-state-backend-as-a-remote-data-source)
|
||||
and [Docker container registry](../packages/container_registry/index.md#authenticate-with-the-container-registry),
|
||||
- Similar to [project access tokens](../project/settings/project_access_tokens.md), but are attached
|
||||
to a user rather than a project.
|
||||
- Similar to [project access tokens](../project/settings/project_access_tokens.md) and [group access tokens](../group/settings/group_access_tokens.md), but are attached
|
||||
to a user rather than a project or group.
|
||||
|
||||
NOTE:
|
||||
Though required, GitLab usernames are ignored when authenticating with a personal access token.
|
||||
|
|
|
@ -92,6 +92,8 @@ module API
|
|||
payload[:git_config_options] << "receive.maxInputSize=#{receive_max_input_size.megabytes}"
|
||||
end
|
||||
|
||||
send_git_audit_streaming_event(protocol: params[:protocol], action: params[:action])
|
||||
|
||||
response_with_status(**payload)
|
||||
when ::Gitlab::GitAccessResult::CustomAction
|
||||
response_with_status(code: 300, payload: check_result.payload, gl_console_messages: check_result.console_messages)
|
||||
|
@ -100,6 +102,10 @@ module API
|
|||
end
|
||||
end
|
||||
|
||||
def send_git_audit_streaming_event(msg)
|
||||
# Defined in EE
|
||||
end
|
||||
|
||||
def access_check!(actor, params)
|
||||
access_checker = access_checker_for(actor, params[:protocol])
|
||||
access_checker.check(params[:action], params[:changes]).tap do |result|
|
||||
|
|
|
@ -170,9 +170,9 @@ module API
|
|||
|
||||
params do
|
||||
requires :content, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
|
||||
requires :requires_python, type: String
|
||||
requires :name, type: String
|
||||
requires :version, type: String
|
||||
optional :requires_python, type: String
|
||||
optional :md5_digest, type: String
|
||||
optional :sha256_digest, type: String
|
||||
end
|
||||
|
|
|
@ -9,7 +9,10 @@ module Banzai
|
|||
html.sub(Gitlab::FrontMatter::PATTERN) do |_match|
|
||||
lang = $~[:lang].presence || lang_mapping[$~[:delim]]
|
||||
|
||||
["```#{lang}:frontmatter", $~[:front_matter].strip!, "```", "\n"].join("\n")
|
||||
before = $~[:before]
|
||||
before = "\n#{before}" if $~[:encoding].presence
|
||||
|
||||
"#{before}```#{lang}:frontmatter\n#{$~[:front_matter]}```\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,12 +11,12 @@ module Gitlab
|
|||
DELIM = Regexp.union(DELIM_LANG.keys)
|
||||
|
||||
PATTERN = %r{
|
||||
\A(?:[^\r\n]*coding:[^\r\n]*\R)? # optional encoding line
|
||||
\s*
|
||||
\A(?<encoding>[^\r\n]*coding:[^\r\n]*\R)? # optional encoding line
|
||||
(?<before>\s*)
|
||||
^(?<delim>#{DELIM})[ \t]*(?<lang>\S*)\R # opening front matter marker (optional language specifier)
|
||||
(?<front_matter>.*?) # front matter block content (not greedy)
|
||||
^(\k<delim> | \.{3}) # closing front matter marker
|
||||
\s*
|
||||
[^\S\r\n]*(\R|\z)
|
||||
}mx.freeze
|
||||
end
|
||||
end
|
||||
|
|
|
@ -109,7 +109,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def parse_front_matter_block
|
||||
wiki_content.match(Gitlab::FrontMatter::PATTERN) { |m| Block.new(*m.captures) } || Block.new
|
||||
wiki_content.match(Gitlab::FrontMatter::PATTERN) { |m| Block.new(m[:delim], m[:lang], m[:front_matter]) } || Block.new
|
||||
end
|
||||
|
||||
def strip_front_matter_block
|
||||
|
|
|
@ -13840,7 +13840,7 @@ msgstr ""
|
|||
msgid "Environment scope"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environment variable %{code_start}%{environment_variable}%{code_end} does not exist or is not pointing to a valid directory."
|
||||
msgid "Environment variable %{environment_variable} does not exist or is not pointing to a valid directory."
|
||||
msgstr ""
|
||||
|
||||
msgid "Environment variables are configured by your administrator to be %{link_start}protected%{link_end} by default."
|
||||
|
@ -28886,6 +28886,9 @@ msgstr ""
|
|||
msgid "ProjectSettings|Merge suggestions"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectSettings|Merging is only allowed when the source branch is up-to-date with its target."
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectSettings|No merge commits are created."
|
||||
msgstr ""
|
||||
|
||||
|
@ -29057,6 +29060,9 @@ msgstr ""
|
|||
msgid "ProjectSettings|When merge request pipelines are enabled in the CI/CD configuration file, pipelines validate the combined results of the source and target branches. %{link_start}How to configure merge request pipelines?%{link_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectSettings|When semi-linear merge is not possible, the user is given the option to rebase."
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectSettings|When there is a merge conflict, the user is given the option to rebase."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ module QA
|
|||
end
|
||||
|
||||
def marked_for_deletion?
|
||||
!parse_body(api_get_from(api_get_path.to_s))[:marked_for_deletion_on].nil?
|
||||
reload!.api_response[:marked_for_deletion_on].present?
|
||||
end
|
||||
|
||||
# Get group badges
|
||||
|
|
|
@ -34,9 +34,8 @@ module QA
|
|||
def remove_all_via_api!
|
||||
instance.each_resource do |reuse_as, resource|
|
||||
next QA::Runtime::Logger.debug("#{resource.class.name} reused as :#{reuse_as} has already been removed.") unless resource.exists?
|
||||
next if resource.respond_to?(:marked_for_deletion?) && resource.marked_for_deletion?
|
||||
|
||||
if resource.reload!.api_resource[:marked_for_deletion_on].present?
|
||||
if resource.respond_to?(:marked_for_deletion?) && resource.marked_for_deletion?
|
||||
next QA::Runtime::Logger.debug("#{resource.class.name} reused as :#{reuse_as} is already scheduled to be removed.")
|
||||
end
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Environments page', :js do
|
||||
include Spec::Support::Helpers::ModalHelpers
|
||||
|
||||
let(:project) { create(:project) }
|
||||
let(:user) { create(:user) }
|
||||
let(:role) { :developer }
|
||||
|
@ -346,7 +348,9 @@ RSpec.describe 'Environments page', :js do
|
|||
context 'when user played a delayed job immediately' do
|
||||
before do
|
||||
find(actions_button_selector).click
|
||||
accept_confirm { find(action_link_selector).click }
|
||||
accept_gl_confirm do
|
||||
find(action_link_selector).click
|
||||
end
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
|
|
|
@ -7,8 +7,15 @@ import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
|
|||
import EnvironmentActions from '~/environments/components/environment_actions.vue';
|
||||
import eventHub from '~/environments/event_hub';
|
||||
import actionMutation from '~/environments/graphql/mutations/action.mutation.graphql';
|
||||
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
|
||||
jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal', () => {
|
||||
return {
|
||||
confirmAction: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const scheduledJobAction = {
|
||||
name: 'scheduled action',
|
||||
playPath: `${TEST_HOST}/scheduled/job/action`,
|
||||
|
@ -50,7 +57,7 @@ describe('EnvironmentActions Component', () => {
|
|||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
confirmAction.mockReset();
|
||||
});
|
||||
|
||||
it('should render a dropdown button with 2 icons', () => {
|
||||
|
@ -105,7 +112,7 @@ describe('EnvironmentActions Component', () => {
|
|||
let emitSpy;
|
||||
|
||||
const clickAndConfirm = async ({ confirm = true } = {}) => {
|
||||
jest.spyOn(window, 'confirm').mockImplementation(() => confirm);
|
||||
confirmAction.mockResolvedValueOnce(confirm);
|
||||
|
||||
findDropdownItem(scheduledJobAction).vm.$emit('click');
|
||||
await nextTick();
|
||||
|
@ -124,7 +131,7 @@ describe('EnvironmentActions Component', () => {
|
|||
});
|
||||
|
||||
it('emits postAction event', () => {
|
||||
expect(window.confirm).toHaveBeenCalled();
|
||||
expect(confirmAction).toHaveBeenCalled();
|
||||
expect(emitSpy).toHaveBeenCalledWith({ endpoint: scheduledJobAction.playPath });
|
||||
});
|
||||
|
||||
|
@ -134,13 +141,13 @@ describe('EnvironmentActions Component', () => {
|
|||
});
|
||||
|
||||
describe('when postAction event is denied', () => {
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
createComponentWithScheduledJobs({ mountFn: mount });
|
||||
clickAndConfirm({ confirm: false });
|
||||
});
|
||||
|
||||
it('does not emit postAction event if confirmation is cancelled', () => {
|
||||
expect(window.confirm).toHaveBeenCalled();
|
||||
expect(confirmAction).toHaveBeenCalled();
|
||||
expect(emitSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -105,6 +105,56 @@ RSpec.describe Banzai::Filter::FrontMatterFilter do
|
|||
end
|
||||
end
|
||||
|
||||
context 'source position mapping' do
|
||||
it 'keeps spaces before and after' do
|
||||
content = <<~MD
|
||||
|
||||
|
||||
---
|
||||
|
||||
foo: :foo_symbol
|
||||
|
||||
---
|
||||
|
||||
|
||||
# Header
|
||||
MD
|
||||
|
||||
output = filter(content)
|
||||
|
||||
expect(output).to eq <<~MD
|
||||
|
||||
|
||||
```yaml:frontmatter
|
||||
|
||||
foo: :foo_symbol
|
||||
|
||||
```
|
||||
|
||||
|
||||
# Header
|
||||
MD
|
||||
end
|
||||
|
||||
it 'keeps an empty line in place of the encoding' do
|
||||
content = <<~MD
|
||||
# encoding: UTF-8
|
||||
---
|
||||
foo: :foo_symbol
|
||||
---
|
||||
MD
|
||||
|
||||
output = filter(content)
|
||||
|
||||
expect(output).to eq <<~MD
|
||||
|
||||
```yaml:frontmatter
|
||||
foo: :foo_symbol
|
||||
```
|
||||
MD
|
||||
end
|
||||
end
|
||||
|
||||
context 'on content without front matter' do
|
||||
it 'returns the content unmodified' do
|
||||
content = <<~MD
|
||||
|
@ -119,7 +169,7 @@ RSpec.describe Banzai::Filter::FrontMatterFilter do
|
|||
|
||||
context 'on front matter without content' do
|
||||
it 'converts YAML front matter to a fenced code block' do
|
||||
content = <<~MD
|
||||
content = <<~MD.rstrip
|
||||
---
|
||||
foo: :foo_symbol
|
||||
bar: :bar_symbol
|
||||
|
@ -134,7 +184,6 @@ RSpec.describe Banzai::Filter::FrontMatterFilter do
|
|||
foo: :foo_symbol
|
||||
bar: :bar_symbol
|
||||
```
|
||||
|
||||
MD
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,9 @@ RSpec.describe Packages::Pypi::Metadatum, type: :model do
|
|||
|
||||
describe 'validations' do
|
||||
it { is_expected.to validate_presence_of(:package) }
|
||||
it { is_expected.to allow_value('').for(:required_python) }
|
||||
it { is_expected.not_to allow_value(nil).for(:required_python) }
|
||||
it { is_expected.not_to allow_value('a' * 256).for(:required_python) }
|
||||
|
||||
describe '#pypi_package_type' do
|
||||
it 'will not allow a package with a different package_type' do
|
||||
|
|
|
@ -185,6 +185,14 @@ RSpec.describe API::PypiPackages do
|
|||
|
||||
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
|
||||
end
|
||||
|
||||
context 'without requires_python' do
|
||||
let(:token) { personal_access_token.token }
|
||||
let(:user_headers) { basic_auth_header(user.username, token) }
|
||||
let(:headers) { user_headers.merge(workhorse_headers) }
|
||||
|
||||
it_behaves_like 'PyPI package creation', :developer, :created, true
|
||||
end
|
||||
end
|
||||
|
||||
context 'with required_python too big' do
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Packages::Pypi::CreatePackageService do
|
||||
RSpec.describe Packages::Pypi::CreatePackageService, :aggregate_failures do
|
||||
include PackagesManagerApiSpecHelpers
|
||||
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
@ -39,6 +39,18 @@ RSpec.describe Packages::Pypi::CreatePackageService do
|
|||
end
|
||||
end
|
||||
|
||||
context 'without required_python' do
|
||||
before do
|
||||
params.delete(:requires_python)
|
||||
end
|
||||
|
||||
it 'creates the package' do
|
||||
expect { subject }.to change { Packages::Package.pypi.count }.by(1)
|
||||
|
||||
expect(created_package.pypi_metadatum.required_python).to eq ''
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an invalid metadata' do
|
||||
let(:requires_python) { 'x' * 256 }
|
||||
|
||||
|
@ -73,7 +85,7 @@ RSpec.describe Packages::Pypi::CreatePackageService do
|
|||
.and raise_error(/File name has already been taken/)
|
||||
end
|
||||
|
||||
context 'with a pending_destruction package', :aggregate_failures do
|
||||
context 'with a pending_destruction package' do
|
||||
before do
|
||||
Packages::Package.pypi.last.pending_destruction!
|
||||
end
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'sends git audit streaming event' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
stub_licensed_features(external_audit_events: true)
|
||||
end
|
||||
|
||||
subject {}
|
||||
|
||||
context 'for public groups and projects' do
|
||||
let(:group) { create(:group, :public) }
|
||||
let(:project) { create(:project, :public, :repository, namespace: group) }
|
||||
|
||||
before do
|
||||
group.external_audit_event_destinations.create!(destination_url: 'http://example.com')
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
context 'when user not logged in' do
|
||||
let(:key) { create(:key) }
|
||||
|
||||
before do
|
||||
if request
|
||||
request.headers.merge! auth_env(user.username, nil, nil)
|
||||
end
|
||||
end
|
||||
it 'sends the audit streaming event' do
|
||||
expect(AuditEvents::AuditEventStreamingWorker).not_to receive(:perform_async)
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for private groups and projects' do
|
||||
let(:group) { create(:group, :private) }
|
||||
let(:project) { create(:project, :private, :repository, namespace: group) }
|
||||
|
||||
before do
|
||||
group.external_audit_event_destinations.create!(destination_url: 'http://example.com')
|
||||
project.add_developer(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'when user logged in' do
|
||||
let(:key) { create(:key, user: user) }
|
||||
|
||||
before do
|
||||
if request
|
||||
password = user.try(:password) || user.try(:token)
|
||||
request.headers.merge! auth_env(user.username, password, nil)
|
||||
end
|
||||
end
|
||||
it 'sends the audit streaming event' do
|
||||
expect(AuditEvents::AuditEventStreamingWorker).to receive(:perform_async).once
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue