diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-09 03:09:18 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-09 03:09:18 +0000 |
commit | 276941b2c436678b956025ae2958e11ccbeac55d (patch) | |
tree | 5e88dd0b0e167972b4433b31c4381d7be66187e0 | |
parent | 25096948f0f1837d0217cbd45394aa875756f054 (diff) | |
download | gitlab-ce-276941b2c436678b956025ae2958e11ccbeac55d.tar.gz |
Add latest changes from gitlab-org/gitlab@master
32 files changed, 458 insertions, 71 deletions
diff --git a/app/assets/javascripts/environments/components/environment_actions.vue b/app/assets/javascripts/environments/components/environment_actions.vue index 98c95507168..c7e024aadec 100644 --- a/app/assets/javascripts/environments/components/environment_actions.vue +++ b/app/assets/javascripts/environments/components/environment_actions.vue @@ -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; } } diff --git a/app/models/packages/pypi/metadatum.rb b/app/models/packages/pypi/metadatum.rb index 2e4d61eaf53..ff247fedb59 100644 --- a/app/models/packages/pypi/metadatum.rb +++ b/app/models/packages/pypi/metadatum.rb @@ -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 diff --git a/app/services/packages/pypi/create_package_service.rb b/app/services/packages/pypi/create_package_service.rb index b988c191734..5d7e967ceb0 100644 --- a/app/services/packages/pypi/create_package_service.rb +++ b/app/services/packages/pypi/create_package_service.rb @@ -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? diff --git a/app/views/admin/application_settings/_prometheus.html.haml b/app/views/admin/application_settings/_prometheus.html.haml index 08befa59952..11830fac336 100644 --- a/app/views/admin/application_settings/_prometheus.html.haml +++ b/app/views/admin/application_settings/_prometheus.html.haml @@ -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' diff --git a/app/views/projects/_merge_request_merge_method_settings.html.haml b/app/views/projects/_merge_request_merge_method_settings.html.haml index 778586a592e..250f7e94e84 100644 --- a/app/views/projects/_merge_request_merge_method_settings.html.haml +++ b/app/views/projects/_merge_request_merge_method_settings.html.haml @@ -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' } diff --git a/db/migrate/20220304152729_add_default_to_required_python_on_packages_pypi_metadata.rb b/db/migrate/20220304152729_add_default_to_required_python_on_packages_pypi_metadata.rb new file mode 100644 index 00000000000..56297565cb4 --- /dev/null +++ b/db/migrate/20220304152729_add_default_to_required_python_on_packages_pypi_metadata.rb @@ -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 diff --git a/db/post_migrate/20220308000205_drop_old_index_security_ci_builds_on_name_and_id_parser_features.rb b/db/post_migrate/20220308000205_drop_old_index_security_ci_builds_on_name_and_id_parser_features.rb new file mode 100644 index 00000000000..4b895c291d8 --- /dev/null +++ b/db/post_migrate/20220308000205_drop_old_index_security_ci_builds_on_name_and_id_parser_features.rb @@ -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 diff --git a/db/schema_migrations/20220304152729 b/db/schema_migrations/20220304152729 new file mode 100644 index 00000000000..021d4e5ad27 --- /dev/null +++ b/db/schema_migrations/20220304152729 @@ -0,0 +1 @@ +483f8299688a6e24fa77867b7dab8a2dad0c2b7ebe43c56c81c02ab1e0dc4674
\ No newline at end of file diff --git a/db/schema_migrations/20220308000205 b/db/schema_migrations/20220308000205 new file mode 100644 index 00000000000..27caf959eb9 --- /dev/null +++ b/db/schema_migrations/20220308000205 @@ -0,0 +1 @@ +c30b1b36ec83df1b4fdf0c3c28656b158beab4f2188875898182c2dfbd073c80
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index bbeecb69858..0a4b3bec84a 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -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); diff --git a/doc/administration/audit_event_streaming.md b/doc/administration/audit_event_streaming.md index 3bdc67e5a69..4f2fc8c26e2 100644 --- a/doc/administration/audit_event_streaming.md +++ b/doc/administration/audit_event_streaming.md @@ -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 +} +``` diff --git a/doc/administration/gitaly/troubleshooting.md b/doc/administration/gitaly/troubleshooting.md index b2783d3bab3..516af4ca469 100644 --- a/doc/administration/gitaly/troubleshooting.md +++ b/doc/administration/gitaly/troubleshooting.md @@ -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 +``` diff --git a/doc/api/packages/pypi.md b/doc/api/packages/pypi.md index 592b976da59..5ed162a3d05 100644 --- a/doc/api/packages/pypi.md +++ b/doc/api/packages/pypi.md @@ -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 \ diff --git a/doc/operations/feature_flags.md b/doc/operations/feature_flags.md index 95472f03f7f..ab4f000743c 100644 --- a/doc/operations/feature_flags.md +++ b/doc/operations/feature_flags.md @@ -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), diff --git a/doc/user/clusters/agent/install/index.md b/doc/user/clusters/agent/install/index.md index 253bae8f576..869c9e33d13 100644 --- a/doc/user/clusters/agent/install/index.md +++ b/doc/user/clusters/agent/install/index.md @@ -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. diff --git a/doc/user/group/index.md b/doc/user/group/index.md index 61ac8a65f90..00fa51eddc9 100644 --- a/doc/user/group/index.md +++ b/doc/user/group/index.md @@ -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**. diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md index 10d718fdf1a..1fbbe438370 100644 --- a/doc/user/profile/personal_access_tokens.md +++ b/doc/user/profile/personal_access_tokens.md @@ -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. diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb index 48157a91477..9c527f28d44 100644 --- a/lib/api/internal/base.rb +++ b/lib/api/internal/base.rb @@ -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| diff --git a/lib/api/pypi_packages.rb b/lib/api/pypi_packages.rb index 706c0702fce..86f36d4fc00 100644 --- a/lib/api/pypi_packages.rb +++ b/lib/api/pypi_packages.rb @@ -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 diff --git a/lib/banzai/filter/front_matter_filter.rb b/lib/banzai/filter/front_matter_filter.rb index 705400a5497..c788137e122 100644 --- a/lib/banzai/filter/front_matter_filter.rb +++ b/lib/banzai/filter/front_matter_filter.rb @@ -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 diff --git a/lib/gitlab/front_matter.rb b/lib/gitlab/front_matter.rb index 5c5c74ca1a0..093501e860b 100644 --- a/lib/gitlab/front_matter.rb +++ b/lib/gitlab/front_matter.rb @@ -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 diff --git a/lib/gitlab/wiki_pages/front_matter_parser.rb b/lib/gitlab/wiki_pages/front_matter_parser.rb index 0ceec39782c..071b0dde619 100644 --- a/lib/gitlab/wiki_pages/front_matter_parser.rb +++ b/lib/gitlab/wiki_pages/front_matter_parser.rb @@ -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 diff --git a/locale/gitlab.pot b/locale/gitlab.pot index beef1cbc9bc..30c90b53efc 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -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 "" diff --git a/qa/qa/resource/group_base.rb b/qa/qa/resource/group_base.rb index db6272ff749..889197a0373 100644 --- a/qa/qa/resource/group_base.rb +++ b/qa/qa/resource/group_base.rb @@ -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 diff --git a/qa/qa/resource/reusable_collection.rb b/qa/qa/resource/reusable_collection.rb index 27d34b16e2f..99a55800d1c 100644 --- a/qa/qa/resource/reusable_collection.rb +++ b/qa/qa/resource/reusable_collection.rb @@ -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 diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb index 3b83c25b629..ca9aebf4336 100644 --- a/spec/features/projects/environments/environments_spec.rb +++ b/spec/features/projects/environments/environments_spec.rb @@ -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 diff --git a/spec/frontend/environments/environment_actions_spec.js b/spec/frontend/environments/environment_actions_spec.js index 336c207428e..ada79e2d415 100644 --- a/spec/frontend/environments/environment_actions_spec.js +++ b/spec/frontend/environments/environment_actions_spec.js @@ -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(); }); }); diff --git a/spec/lib/banzai/filter/front_matter_filter_spec.rb b/spec/lib/banzai/filter/front_matter_filter_spec.rb index 1562c388296..f3543ab9582 100644 --- a/spec/lib/banzai/filter/front_matter_filter_spec.rb +++ b/spec/lib/banzai/filter/front_matter_filter_spec.rb @@ -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 diff --git a/spec/models/packages/pypi/metadatum_spec.rb b/spec/models/packages/pypi/metadatum_spec.rb index 2c9893ef8f3..6c83c4ed143 100644 --- a/spec/models/packages/pypi/metadatum_spec.rb +++ b/spec/models/packages/pypi/metadatum_spec.rb @@ -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 diff --git a/spec/requests/api/pypi_packages_spec.rb b/spec/requests/api/pypi_packages_spec.rb index fcd2d56e655..078db4f1509 100644 --- a/spec/requests/api/pypi_packages_spec.rb +++ b/spec/requests/api/pypi_packages_spec.rb @@ -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 diff --git a/spec/services/packages/pypi/create_package_service_spec.rb b/spec/services/packages/pypi/create_package_service_spec.rb index 3d0c10724d4..f84a77f80f7 100644 --- a/spec/services/packages/pypi/create_package_service_spec.rb +++ b/spec/services/packages/pypi/create_package_service_spec.rb @@ -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 diff --git a/spec/support/shared_examples/sends_git_audit_streaming_event_shared_examples.rb b/spec/support/shared_examples/sends_git_audit_streaming_event_shared_examples.rb new file mode 100644 index 00000000000..2c2be0152a0 --- /dev/null +++ b/spec/support/shared_examples/sends_git_audit_streaming_event_shared_examples.rb @@ -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 |