Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
163b6c3c80
commit
1d659e434d
47 changed files with 197 additions and 173 deletions
|
@ -26,7 +26,17 @@ const isValidDateString = (dateString) => {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !Number.isNaN(Date.parse(dateformat(dateString, 'isoUtcDateTime')));
|
let isoFormatted;
|
||||||
|
try {
|
||||||
|
isoFormatted = dateformat(dateString, 'isoUtcDateTime');
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof TypeError) {
|
||||||
|
// not a valid date string
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
return !Number.isNaN(Date.parse(isoFormatted));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRangeDirection = ({ direction = DEFAULT_DIRECTION, anchorDate, minDate, maxDate }) => {
|
const handleRangeDirection = ({ direction = DEFAULT_DIRECTION, anchorDate, minDate, maxDate }) => {
|
||||||
|
|
|
@ -55,6 +55,8 @@ module Ci
|
||||||
has_one :runner_session, class_name: 'Ci::BuildRunnerSession', validate: true, inverse_of: :build
|
has_one :runner_session, class_name: 'Ci::BuildRunnerSession', validate: true, inverse_of: :build
|
||||||
has_one :trace_metadata, class_name: 'Ci::BuildTraceMetadata', inverse_of: :build
|
has_one :trace_metadata, class_name: 'Ci::BuildTraceMetadata', inverse_of: :build
|
||||||
|
|
||||||
|
has_many :terraform_state_versions, class_name: 'Terraform::StateVersion', dependent: :nullify, inverse_of: :build, foreign_key: :ci_build_id # rubocop:disable Cop/ActiveRecordDependent
|
||||||
|
|
||||||
accepts_nested_attributes_for :runner_session, update_only: true
|
accepts_nested_attributes_for :runner_session, update_only: true
|
||||||
accepts_nested_attributes_for :job_variables
|
accepts_nested_attributes_for :job_variables
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
:urgency: :low
|
:urgency: :low
|
||||||
:resource_boundary: :unknown
|
:resource_boundary: :unknown
|
||||||
:weight: 1
|
:weight: 1
|
||||||
:idempotent:
|
:idempotent: true
|
||||||
:tags: []
|
:tags: []
|
||||||
- :name: authorized_project_update:authorized_project_update_user_refresh_with_low_urgency
|
- :name: authorized_project_update:authorized_project_update_user_refresh_with_low_urgency
|
||||||
:worker_name: AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker
|
:worker_name: AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker
|
||||||
|
|
|
@ -19,11 +19,10 @@ module AuthorizedProjectUpdate
|
||||||
feature_category :authentication_and_authorization
|
feature_category :authentication_and_authorization
|
||||||
urgency :low
|
urgency :low
|
||||||
queue_namespace :authorized_project_update
|
queue_namespace :authorized_project_update
|
||||||
# This job will not be deduplicated since it is marked with
|
|
||||||
# `data_consistency :delayed` and not `idempotent!`
|
|
||||||
# See https://gitlab.com/gitlab-org/gitlab/-/issues/325291
|
|
||||||
deduplicate :until_executing, including_scheduled: true
|
deduplicate :until_executing, including_scheduled: true
|
||||||
data_consistency :delayed
|
data_consistency :delayed
|
||||||
|
idempotent!
|
||||||
|
|
||||||
def perform(start_user_id, end_user_id)
|
def perform(start_user_id, end_user_id)
|
||||||
User.where(id: start_user_id..end_user_id).find_each do |user| # rubocop: disable CodeReuse/ActiveRecord
|
User.where(id: start_user_id..end_user_id).find_each do |user| # rubocop: disable CodeReuse/ActiveRecord
|
||||||
|
|
|
@ -11,8 +11,8 @@ raise "Counter cache is not disabled" if
|
||||||
ActsAsTaggableOn::Tagging.reflections["tag"].options[:counter_cache]
|
ActsAsTaggableOn::Tagging.reflections["tag"].options[:counter_cache]
|
||||||
|
|
||||||
ActsAsTaggableOn::Tagging.include IgnorableColumns
|
ActsAsTaggableOn::Tagging.include IgnorableColumns
|
||||||
ActsAsTaggableOn::Tagging.ignore_column :id_convert_to_bigint, remove_with: '14.2', remove_after: '2021-08-22'
|
ActsAsTaggableOn::Tagging.ignore_column :id_convert_to_bigint, remove_with: '14.5', remove_after: '2021-10-22'
|
||||||
ActsAsTaggableOn::Tagging.ignore_column :taggable_id_convert_to_bigint, remove_with: '14.2', remove_after: '2021-08-22'
|
ActsAsTaggableOn::Tagging.ignore_column :taggable_id_convert_to_bigint, remove_with: '14.5', remove_after: '2021-10-22'
|
||||||
|
|
||||||
# The tags and taggings are supposed to be part of `gitlab_ci`
|
# The tags and taggings are supposed to be part of `gitlab_ci`
|
||||||
ActsAsTaggableOn::Tag.gitlab_schema = :gitlab_ci
|
ActsAsTaggableOn::Tag.gitlab_schema = :gitlab_ci
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class DropTemporaryColumnsAndTriggersForTaggings < Gitlab::Database::Migration[1.0]
|
||||||
|
enable_lock_retries!
|
||||||
|
|
||||||
|
TABLE = 'taggings'
|
||||||
|
COLUMNS = %w(id taggable_id)
|
||||||
|
|
||||||
|
# rubocop:disable Migration/WithLockRetriesDisallowedMethod
|
||||||
|
def up
|
||||||
|
cleanup_conversion_of_integer_to_bigint(TABLE, COLUMNS)
|
||||||
|
end
|
||||||
|
# rubocop:enable Migration/WithLockRetriesDisallowedMethod
|
||||||
|
|
||||||
|
def down
|
||||||
|
restore_conversion_of_integer_to_bigint(TABLE, COLUMNS)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,15 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RemoveCiBuildsForeignKeyFromTerraformStateVersions < Gitlab::Database::Migration[1.0]
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
with_lock_retries do
|
||||||
|
remove_foreign_key_if_exists(:terraform_state_versions, :ci_builds)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
add_concurrent_foreign_key(:terraform_state_versions, :ci_builds, column: :ci_build_id, on_delete: :nullify)
|
||||||
|
end
|
||||||
|
end
|
1
db/schema_migrations/20210906130643
Normal file
1
db/schema_migrations/20210906130643
Normal file
|
@ -0,0 +1 @@
|
||||||
|
c707c0879e439de95f24f8fe2084388276a46c5e0ee30e4134a43e22ca19b4ec
|
1
db/schema_migrations/20210920232025
Normal file
1
db/schema_migrations/20210920232025
Normal file
|
@ -0,0 +1 @@
|
||||||
|
12dfb473067fc836cd435474405c3ca978d159a13e975f7663fe22c078731fd1
|
|
@ -86,16 +86,6 @@ BEGIN
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
CREATE FUNCTION trigger_aebe8b822ad3() RETURNS trigger
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
BEGIN
|
|
||||||
NEW."id_convert_to_bigint" := NEW."id";
|
|
||||||
NEW."taggable_id_convert_to_bigint" := NEW."taggable_id";
|
|
||||||
RETURN NEW;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE TABLE audit_events (
|
CREATE TABLE audit_events (
|
||||||
id bigint NOT NULL,
|
id bigint NOT NULL,
|
||||||
author_id integer NOT NULL,
|
author_id integer NOT NULL,
|
||||||
|
@ -19415,9 +19405,7 @@ CREATE SEQUENCE system_note_metadata_id_seq
|
||||||
ALTER SEQUENCE system_note_metadata_id_seq OWNED BY system_note_metadata.id;
|
ALTER SEQUENCE system_note_metadata_id_seq OWNED BY system_note_metadata.id;
|
||||||
|
|
||||||
CREATE TABLE taggings (
|
CREATE TABLE taggings (
|
||||||
id_convert_to_bigint integer DEFAULT 0 NOT NULL,
|
|
||||||
tag_id integer,
|
tag_id integer,
|
||||||
taggable_id_convert_to_bigint integer,
|
|
||||||
taggable_type character varying,
|
taggable_type character varying,
|
||||||
tagger_id integer,
|
tagger_id integer,
|
||||||
tagger_type character varying,
|
tagger_type character varying,
|
||||||
|
@ -27344,8 +27332,6 @@ ALTER INDEX product_analytics_events_experimental_pkey ATTACH PARTITION gitlab_p
|
||||||
|
|
||||||
CREATE TRIGGER trigger_91dc388a5fe6 BEFORE INSERT OR UPDATE ON dep_ci_build_trace_sections FOR EACH ROW EXECUTE FUNCTION trigger_91dc388a5fe6();
|
CREATE TRIGGER trigger_91dc388a5fe6 BEFORE INSERT OR UPDATE ON dep_ci_build_trace_sections FOR EACH ROW EXECUTE FUNCTION trigger_91dc388a5fe6();
|
||||||
|
|
||||||
CREATE TRIGGER trigger_aebe8b822ad3 BEFORE INSERT OR UPDATE ON taggings FOR EACH ROW EXECUTE FUNCTION trigger_aebe8b822ad3();
|
|
||||||
|
|
||||||
CREATE TRIGGER trigger_delete_project_namespace_on_project_delete AFTER DELETE ON projects FOR EACH ROW WHEN ((old.project_namespace_id IS NOT NULL)) EXECUTE FUNCTION delete_associated_project_namespace();
|
CREATE TRIGGER trigger_delete_project_namespace_on_project_delete AFTER DELETE ON projects FOR EACH ROW WHEN ((old.project_namespace_id IS NOT NULL)) EXECUTE FUNCTION delete_associated_project_namespace();
|
||||||
|
|
||||||
CREATE TRIGGER trigger_has_external_issue_tracker_on_delete AFTER DELETE ON integrations FOR EACH ROW WHEN ((((old.category)::text = 'issue_tracker'::text) AND (old.active = true) AND (old.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_issue_tracker();
|
CREATE TRIGGER trigger_has_external_issue_tracker_on_delete AFTER DELETE ON integrations FOR EACH ROW WHEN ((((old.category)::text = 'issue_tracker'::text) AND (old.active = true) AND (old.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_issue_tracker();
|
||||||
|
@ -27385,9 +27371,6 @@ ALTER TABLE ONLY service_desk_settings
|
||||||
ALTER TABLE ONLY design_management_designs_versions
|
ALTER TABLE ONLY design_management_designs_versions
|
||||||
ADD CONSTRAINT fk_03c671965c FOREIGN KEY (design_id) REFERENCES design_management_designs(id) ON DELETE CASCADE;
|
ADD CONSTRAINT fk_03c671965c FOREIGN KEY (design_id) REFERENCES design_management_designs(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
ALTER TABLE ONLY terraform_state_versions
|
|
||||||
ADD CONSTRAINT fk_04b91e4a9f FOREIGN KEY (ci_build_id) REFERENCES ci_builds(id) ON DELETE SET NULL;
|
|
||||||
|
|
||||||
ALTER TABLE ONLY issues
|
ALTER TABLE ONLY issues
|
||||||
ADD CONSTRAINT fk_05f1e72feb FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL;
|
ADD CONSTRAINT fk_05f1e72feb FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL;
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ exceptions:
|
||||||
- DNS
|
- DNS
|
||||||
- DOM
|
- DOM
|
||||||
- DSA
|
- DSA
|
||||||
|
- DSL
|
||||||
- DVCS
|
- DVCS
|
||||||
- ECDSA
|
- ECDSA
|
||||||
- ECS
|
- ECS
|
||||||
|
|
|
@ -12,7 +12,7 @@ The GitLab Pages feature must be enabled to use these endpoints. Find out more a
|
||||||
|
|
||||||
## List all Pages domains
|
## List all Pages domains
|
||||||
|
|
||||||
Get a list of all Pages domains. The user must have admin permissions.
|
Get a list of all Pages domains. The user must have the administrator role.
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
GET /pages/domains
|
GET /pages/domains
|
||||||
|
|
|
@ -95,6 +95,6 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://git
|
||||||
- `204: No Content` if successfully revoked.
|
- `204: No Content` if successfully revoked.
|
||||||
- `400 Bad Request` if not revoked successfully.
|
- `400 Bad Request` if not revoked successfully.
|
||||||
|
|
||||||
## Create a personal access token (admin only)
|
## Create a personal access token (administrator only)
|
||||||
|
|
||||||
See the [Users API documentation](users.md#create-a-personal-access-token) for information on creating a personal access token.
|
See the [Users API documentation](users.md#create-a-personal-access-token) for information on creating a personal access token.
|
||||||
|
|
|
@ -86,7 +86,7 @@ Example response:
|
||||||
## List all runners **(FREE SELF)**
|
## List all runners **(FREE SELF)**
|
||||||
|
|
||||||
Get a list of all runners in the GitLab instance (specific and shared). Access
|
Get a list of all runners in the GitLab instance (specific and shared). Access
|
||||||
is restricted to users with `admin` privileges.
|
is restricted to users with the administrator role.
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
GET /runners/all
|
GET /runners/all
|
||||||
|
|
|
@ -50,7 +50,7 @@ and check if their values are what you expect.
|
||||||
## GitLab CI/CD documentation
|
## GitLab CI/CD documentation
|
||||||
|
|
||||||
The [complete `.gitlab-ci.yml` reference](yaml/index.md) contains a full list of
|
The [complete `.gitlab-ci.yml` reference](yaml/index.md) contains a full list of
|
||||||
every keyword you may need to use to configure your pipelines.
|
every keyword you can use to configure your pipelines.
|
||||||
|
|
||||||
You can also look at a large number of pipeline configuration [examples](examples/index.md)
|
You can also look at a large number of pipeline configuration [examples](examples/index.md)
|
||||||
and [templates](examples/index.md#cicd-templates).
|
and [templates](examples/index.md#cicd-templates).
|
||||||
|
@ -76,7 +76,7 @@ if you are using that type:
|
||||||
|
|
||||||
### Troubleshooting Guides for CI/CD features
|
### Troubleshooting Guides for CI/CD features
|
||||||
|
|
||||||
There are troubleshooting guides available for some CI/CD features and related topics:
|
Troubleshooting guides are available for some CI/CD features and related topics:
|
||||||
|
|
||||||
- [Container Registry](../user/packages/container_registry/index.md#troubleshooting-the-gitlab-container-registry)
|
- [Container Registry](../user/packages/container_registry/index.md#troubleshooting-the-gitlab-container-registry)
|
||||||
- [GitLab Runner](https://docs.gitlab.com/runner/faq/)
|
- [GitLab Runner](https://docs.gitlab.com/runner/faq/)
|
||||||
|
@ -118,7 +118,7 @@ Two pipelines can run when pushing a commit to a branch that has an open merge r
|
||||||
associated with it. Usually one pipeline is a merge request pipeline, and the other
|
associated with it. Usually one pipeline is a merge request pipeline, and the other
|
||||||
is a branch pipeline.
|
is a branch pipeline.
|
||||||
|
|
||||||
This is usually caused by the `rules` configuration, and there are several ways to
|
This situation is usually caused by the `rules` configuration, and there are several ways to
|
||||||
[prevent duplicate pipelines](jobs/job_control.md#avoid-duplicate-pipelines).
|
[prevent duplicate pipelines](jobs/job_control.md#avoid-duplicate-pipelines).
|
||||||
|
|
||||||
#### A job is not in the pipeline
|
#### A job is not in the pipeline
|
||||||
|
@ -168,7 +168,7 @@ a branch to its remote repository. To illustrate the problem, suppose you've had
|
||||||
1. A new pipeline starts running on the `example` branch again, however,
|
1. A new pipeline starts running on the `example` branch again, however,
|
||||||
the previous pipeline (2) fails because of `fatal: reference is not a tree:` error.
|
the previous pipeline (2) fails because of `fatal: reference is not a tree:` error.
|
||||||
|
|
||||||
This is because the previous pipeline cannot find a checkout-SHA (which is associated with the pipeline record)
|
This occurs because the previous pipeline cannot find a checkout-SHA (which is associated with the pipeline record)
|
||||||
from the `example` branch that the commit history has already been overwritten by the force-push.
|
from the `example` branch that the commit history has already been overwritten by the force-push.
|
||||||
Similarly, [Pipelines for merged results](pipelines/pipelines_for_merged_results.md)
|
Similarly, [Pipelines for merged results](pipelines/pipelines_for_merged_results.md)
|
||||||
might have failed intermittently due to [the same reason](pipelines/pipelines_for_merged_results.md#intermittently-pipelines-fail-by-fatal-reference-is-not-a-tree-error).
|
might have failed intermittently due to [the same reason](pipelines/pipelines_for_merged_results.md#intermittently-pipelines-fail-by-fatal-reference-is-not-a-tree-error).
|
||||||
|
@ -199,6 +199,7 @@ latest commit yet. This might be because:
|
||||||
- You are not using CI/CD pipelines in your project.
|
- You are not using CI/CD pipelines in your project.
|
||||||
- You are using CI/CD pipelines in your project, but your configuration prevented a pipeline from running on the source branch for your merge request.
|
- You are using CI/CD pipelines in your project, but your configuration prevented a pipeline from running on the source branch for your merge request.
|
||||||
- The latest pipeline was deleted (this is a [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/214323)).
|
- The latest pipeline was deleted (this is a [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/214323)).
|
||||||
|
- The source branch of the merge request is on a private fork.
|
||||||
|
|
||||||
After the pipeline is created, the message updates with the pipeline status.
|
After the pipeline is created, the message updates with the pipeline status.
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,7 @@ Renders the enforcement checkbox.
|
||||||
| `attribute` | Name of the setting. For example, `:delayed_project_removal`. | `String` or `Symbol` | `true` |
|
| `attribute` | Name of the setting. For example, `:delayed_project_removal`. | `String` or `Symbol` | `true` |
|
||||||
| `group` | Current group. | [`Group`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/group.rb) | `true` |
|
| `group` | Current group. | [`Group`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/group.rb) | `true` |
|
||||||
| `form` | [Rails FormBuilder object](https://apidock.com/rails/ActionView/Helpers/FormBuilder). | [`ActionView::Helpers::FormBuilder`](https://apidock.com/rails/ActionView/Helpers/FormBuilder) | `true` |
|
| `form` | [Rails FormBuilder object](https://apidock.com/rails/ActionView/Helpers/FormBuilder). | [`ActionView::Helpers::FormBuilder`](https://apidock.com/rails/ActionView/Helpers/FormBuilder) | `true` |
|
||||||
| `setting_locked` | If the setting is locked by an ancestor group or admin setting. Can be calculated with [`cascading_namespace_setting_locked?`](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/helpers/namespaces_helper.rb#L86). | `Boolean` | `true` |
|
| `setting_locked` | If the setting is locked by an ancestor group or administrator setting. Can be calculated with [`cascading_namespace_setting_locked?`](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/helpers/namespaces_helper.rb#L86). | `Boolean` | `true` |
|
||||||
| `help_text` | Text shown below the checkbox. | `String` | `false` (Subgroups cannot change this setting.) |
|
| `help_text` | Text shown below the checkbox. | `String` | `false` (Subgroups cannot change this setting.) |
|
||||||
|
|
||||||
[`_setting_label_checkbox.html.haml`](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/views/shared/namespaces/cascading_settings/_setting_label_checkbox.html.haml)
|
[`_setting_label_checkbox.html.haml`](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/views/shared/namespaces/cascading_settings/_setting_label_checkbox.html.haml)
|
||||||
|
@ -147,7 +147,7 @@ Renders the label for a checkbox setting.
|
||||||
| `attribute` | Name of the setting. For example, `:delayed_project_removal`. | `String` or `Symbol` | `true` |
|
| `attribute` | Name of the setting. For example, `:delayed_project_removal`. | `String` or `Symbol` | `true` |
|
||||||
| `group` | Current group. | [`Group`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/group.rb) | `true` |
|
| `group` | Current group. | [`Group`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/group.rb) | `true` |
|
||||||
| `form` | [Rails FormBuilder object](https://apidock.com/rails/ActionView/Helpers/FormBuilder). | [`ActionView::Helpers::FormBuilder`](https://apidock.com/rails/ActionView/Helpers/FormBuilder) | `true` |
|
| `form` | [Rails FormBuilder object](https://apidock.com/rails/ActionView/Helpers/FormBuilder). | [`ActionView::Helpers::FormBuilder`](https://apidock.com/rails/ActionView/Helpers/FormBuilder) | `true` |
|
||||||
| `setting_locked` | If the setting is locked by an ancestor group or admin setting. Can be calculated with [`cascading_namespace_setting_locked?`](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/helpers/namespaces_helper.rb#L86). | `Boolean` | `true` |
|
| `setting_locked` | If the setting is locked by an ancestor group or administrator setting. Can be calculated with [`cascading_namespace_setting_locked?`](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/helpers/namespaces_helper.rb#L86). | `Boolean` | `true` |
|
||||||
| `settings_path_helper` | Lambda function that generates a path to the ancestor setting. For example, `settings_path_helper: -> (locked_ancestor) { edit_group_path(locked_ancestor, anchor: 'js-permissions-settings') }` | `Lambda` | `true` |
|
| `settings_path_helper` | Lambda function that generates a path to the ancestor setting. For example, `settings_path_helper: -> (locked_ancestor) { edit_group_path(locked_ancestor, anchor: 'js-permissions-settings') }` | `Lambda` | `true` |
|
||||||
| `help_text` | Text shown below the checkbox. | `String` | `false` (`nil`) |
|
| `help_text` | Text shown below the checkbox. | `String` | `false` (`nil`) |
|
||||||
|
|
||||||
|
|
|
@ -216,6 +216,6 @@ This is due to a [n+1 calls limit being set for development setups](gitaly.md#to
|
||||||
|
|
||||||
Many of the tests also require a GitLab Personal Access Token. This is due to numerous endpoints themselves requiring authentication.
|
Many of the tests also require a GitLab Personal Access Token. This is due to numerous endpoints themselves requiring authentication.
|
||||||
|
|
||||||
[The official GitLab docs detail how to create this token](../user/profile/personal_access_tokens.md#create-a-personal-access-token). The tests require that the token is generated by an admin user and that it has the `API` and `read_repository` permissions.
|
[The official GitLab docs detail how to create this token](../user/profile/personal_access_tokens.md#create-a-personal-access-token). The tests require that the token is generated by an administrator and that it has the `API` and `read_repository` permissions.
|
||||||
|
|
||||||
Details on how to use the Access Token with each type of test are found in their respective documentation.
|
Details on how to use the Access Token with each type of test are found in their respective documentation.
|
||||||
|
|
|
@ -45,7 +45,7 @@ many groups or projects, and the access level (including guest, developer, or
|
||||||
maintainer) to groups and projects determines what users can see and
|
maintainer) to groups and projects determines what users can see and
|
||||||
what they can access.
|
what they can access.
|
||||||
|
|
||||||
Users with admin access can access all projects and even impersonate
|
Users with the administrator role can access all projects and even impersonate
|
||||||
users.
|
users.
|
||||||
|
|
||||||
#### Sharding and partitioning
|
#### Sharding and partitioning
|
||||||
|
|
|
@ -15,7 +15,7 @@ token via `GITLAB_QA_ADMIN_ACCESS_TOKEN` (recommended), or provide `GITLAB_ADMIN
|
||||||
and `GITLAB_ADMIN_PASSWORD`.
|
and `GITLAB_ADMIN_PASSWORD`.
|
||||||
|
|
||||||
Please be sure to include the tag `:requires_admin` so that the test can be skipped in environments
|
Please be sure to include the tag `:requires_admin` so that the test can be skipped in environments
|
||||||
where admin access is not available.
|
where administrator access is not available.
|
||||||
|
|
||||||
WARNING:
|
WARNING:
|
||||||
You are strongly advised to [enable feature flags only for a group, project, user](../../feature_flags/index.md#feature-actors),
|
You are strongly advised to [enable feature flags only for a group, project, user](../../feature_flags/index.md#feature-actors),
|
||||||
|
|
|
@ -45,7 +45,7 @@ docker run \
|
||||||
|
|
||||||
Jenkins is available on `http://localhost:8080`.
|
Jenkins is available on `http://localhost:8080`.
|
||||||
|
|
||||||
Admin username is `admin` and password is `password`.
|
Administrator username is `admin` and password is `password`.
|
||||||
|
|
||||||
It is worth noting that this is not an orchestrated test. It is [tagged with the `:orchestrated` meta](https://gitlab.com/gitlab-org/gitlab/-/blob/163c8a8c814db26d11e104d1cb2dcf02eb567dbe/qa/qa/specs/features/ee/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb#L5)
|
It is worth noting that this is not an orchestrated test. It is [tagged with the `:orchestrated` meta](https://gitlab.com/gitlab-org/gitlab/-/blob/163c8a8c814db26d11e104d1cb2dcf02eb567dbe/qa/qa/specs/features/ee/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb#L5)
|
||||||
only to prevent it from running in the pipelines for live environments such as Staging.
|
only to prevent it from running in the pipelines for live environments such as Staging.
|
||||||
|
@ -167,9 +167,9 @@ The following includes more information on the command:
|
||||||
|
|
||||||
-`QA_DEBUG` - Set to `true` to verbosely log page object actions.
|
-`QA_DEBUG` - Set to `true` to verbosely log page object actions.
|
||||||
-`WEBDRIVER_HEADLESS` - When running locally, set to `false` to allow browser tests to be visible - watch your tests being run.
|
-`WEBDRIVER_HEADLESS` - When running locally, set to `false` to allow browser tests to be visible - watch your tests being run.
|
||||||
-`GITLAB_ADMIN_USERNAME` - Admin username to use when adding a license.
|
-`GITLAB_ADMIN_USERNAME` - Administrator username to use when adding a license.
|
||||||
-`GITLAB_ADMIN_PASSWORD` - Admin password to use when adding a license.
|
-`GITLAB_ADMIN_PASSWORD` - Administrator password to use when adding a license.
|
||||||
-`GITLAB_QA_ACCESS_TOKEN` and `GITLAB_QA_ADMIN_ACCESS_TOKEN` - A valid personal access token with the `api` scope. This is used for API access during tests, and is used in the version that staging is currently running. The `ADMIN_ACCESS_TOKEN` is from a user with admin access. Used for API access as an admin during tests.
|
-`GITLAB_QA_ACCESS_TOKEN` and `GITLAB_QA_ADMIN_ACCESS_TOKEN` - A valid personal access token with the `api` scope. This is used for API access during tests, and is used in the version that staging is currently running. The `ADMIN_ACCESS_TOKEN` is from a user with administrator access. Used for API access as an administrator during tests.
|
||||||
-`CLUSTER_API_URL` - Use the address `https://kubernetes.docker.internal:6443` . This address is used to enable the cluster to be network accessible while deploying using Auto DevOps.
|
-`CLUSTER_API_URL` - Use the address `https://kubernetes.docker.internal:6443` . This address is used to enable the cluster to be network accessible while deploying using Auto DevOps.
|
||||||
-`https://[YOUR-PORT].qa-tunnel.gitlab.info/` - The address of your local GDK
|
-`https://[YOUR-PORT].qa-tunnel.gitlab.info/` - The address of your local GDK
|
||||||
-`qa/specs/features/browser_ui/8_monitor/all_monitor_core_features_spec.rb` - The path to the monitor core specs
|
-`qa/specs/features/browser_ui/8_monitor/all_monitor_core_features_spec.rb` - The path to the monitor core specs
|
||||||
|
@ -410,7 +410,7 @@ Tests that are tagged with `:ldap_tls` and `:ldap_no_tls` meta are orchestrated
|
||||||
|
|
||||||
These tests spin up a Docker container [(`osixia/openldap`)](https://hub.docker.com/r/osixia/openldap) running an instance of [OpenLDAP](https://www.openldap.org/).
|
These tests spin up a Docker container [(`osixia/openldap`)](https://hub.docker.com/r/osixia/openldap) running an instance of [OpenLDAP](https://www.openldap.org/).
|
||||||
The container uses fixtures [checked into the GitLab-QA repository](https://gitlab.com/gitlab-org/gitlab-qa/-/tree/9ffb9ad3be847a9054967d792d6772a74220fb42/fixtures/ldap) to create
|
The container uses fixtures [checked into the GitLab-QA repository](https://gitlab.com/gitlab-org/gitlab-qa/-/tree/9ffb9ad3be847a9054967d792d6772a74220fb42/fixtures/ldap) to create
|
||||||
base data such as users and groups including the admin group. The password for [all users](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/9ffb9ad3be847a9054967d792d6772a74220fb42/fixtures/ldap/2_add_users.ldif) including [the `tanuki` user](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/9ffb9ad3be847a9054967d792d6772a74220fb42/fixtures/ldap/tanuki.ldif) is `password`.
|
base data such as users and groups including the administrator group. The password for [all users](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/9ffb9ad3be847a9054967d792d6772a74220fb42/fixtures/ldap/2_add_users.ldif) including [the `tanuki` user](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/9ffb9ad3be847a9054967d792d6772a74220fb42/fixtures/ldap/tanuki.ldif) is `password`.
|
||||||
|
|
||||||
A GitLab instance is also created in a Docker container based on our [LDAP setup](../../../administration/auth/ldap/index.md) documentation.
|
A GitLab instance is also created in a Docker container based on our [LDAP setup](../../../administration/auth/ldap/index.md) documentation.
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ Project maintainers and owners can add or enable a deploy key for a project repo
|
||||||
|
|
||||||
## Runner registration tokens
|
## Runner registration tokens
|
||||||
|
|
||||||
Runner registration tokens are used to [register](https://docs.gitlab.com/runner/register/) a [runner](https://docs.gitlab.com/runner/) with GitLab. Group or project owners or instance admins can obtain them through the GitLab user interface. The registration token is limited to runner registration and has no further scope.
|
Runner registration tokens are used to [register](https://docs.gitlab.com/runner/register/) a [runner](https://docs.gitlab.com/runner/) with GitLab. Group or project owners or instance administrators can obtain them through the GitLab user interface. The registration token is limited to runner registration and has no further scope.
|
||||||
|
|
||||||
You can use the runner registration token to add runners that execute jobs in a project or group. The runner has access to the project's code, so be careful when assigning project and group-level permissions.
|
You can use the runner registration token to add runners that execute jobs in a project or group. The runner has access to the project's code, so be careful when assigning project and group-level permissions.
|
||||||
|
|
||||||
|
|
|
@ -715,7 +715,7 @@ The banner can be disabled for:
|
||||||
Feature.enable(:auto_devops_banner_disabled)
|
Feature.enable(:auto_devops_banner_disabled)
|
||||||
```
|
```
|
||||||
|
|
||||||
- Through the REST API with an admin access token:
|
- Through the REST API with an administrator access token:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl --data "value=true" --header "PRIVATE-TOKEN: <personal_access_token>" "https://gitlab.example.com/api/v4/features/auto_devops_banner_disabled"
|
curl --data "value=true" --header "PRIVATE-TOKEN: <personal_access_token>" "https://gitlab.example.com/api/v4/features/auto_devops_banner_disabled"
|
||||||
|
|
|
@ -136,9 +136,9 @@ pending_job_classes.each { |job_class| Gitlab::BackgroundMigration.steal(job_cla
|
||||||
|
|
||||||
## Dealing with running CI/CD pipelines and jobs
|
## Dealing with running CI/CD pipelines and jobs
|
||||||
|
|
||||||
If you upgrade your GitLab instance while the GitLab Runner is processing jobs, the trace updates will fail. Once GitLab is back online, then the trace updates should self-heal. However, depending on the error, the GitLab Runner will either retry or eventually terminate job handling.
|
If you upgrade your GitLab instance while the GitLab Runner is processing jobs, the trace updates fail. When GitLab is back online, the trace updates should self-heal. However, depending on the error, the GitLab Runner either retries or eventually terminates job handling.
|
||||||
|
|
||||||
As for the artifacts, the GitLab Runner will attempt to upload them three times, after which the job will eventually fail.
|
As for the artifacts, the GitLab Runner attempts to upload them three times, after which the job eventually fails.
|
||||||
|
|
||||||
To address the above two scenario's, it is advised to do the following prior to upgrading:
|
To address the above two scenario's, it is advised to do the following prior to upgrading:
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ upgrade paths.
|
||||||
Upgrading the *major* version requires more attention.
|
Upgrading the *major* version requires more attention.
|
||||||
Backward-incompatible changes and migrations are reserved for major versions.
|
Backward-incompatible changes and migrations are reserved for major versions.
|
||||||
Follow the directions carefully as we
|
Follow the directions carefully as we
|
||||||
cannot guarantee that upgrading between major versions will be seamless.
|
cannot guarantee that upgrading between major versions is seamless.
|
||||||
|
|
||||||
It is required to follow the following upgrade steps to ensure a successful *major* version upgrade:
|
It is required to follow the following upgrade steps to ensure a successful *major* version upgrade:
|
||||||
|
|
||||||
|
@ -402,7 +402,7 @@ Git 2.31.x and later is required. We recommend you use the
|
||||||
### 13.9.0
|
### 13.9.0
|
||||||
|
|
||||||
We've detected an issue [with a column rename](https://gitlab.com/gitlab-org/gitlab/-/issues/324160)
|
We've detected an issue [with a column rename](https://gitlab.com/gitlab-org/gitlab/-/issues/324160)
|
||||||
that will prevent upgrades to GitLab 13.9.0, 13.9.1, 13.9.2 and 13.9.3 when following the zero-downtime steps. It is necessary
|
that prevents upgrades to GitLab 13.9.0, 13.9.1, 13.9.2, and 13.9.3 when following the zero-downtime steps. It is necessary
|
||||||
to perform the following additional steps for the zero-downtime upgrade:
|
to perform the following additional steps for the zero-downtime upgrade:
|
||||||
|
|
||||||
1. Before running the final `sudo gitlab-rake db:migrate` command on the deploy node,
|
1. Before running the final `sudo gitlab-rake db:migrate` command on the deploy node,
|
||||||
|
@ -423,7 +423,7 @@ to perform the following additional steps for the zero-downtime upgrade:
|
||||||
```
|
```
|
||||||
|
|
||||||
If you have already run the final `sudo gitlab-rake db:migrate` command on the deploy node and have
|
If you have already run the final `sudo gitlab-rake db:migrate` command on the deploy node and have
|
||||||
encountered the [column rename issue](https://gitlab.com/gitlab-org/gitlab/-/issues/324160), you will
|
encountered the [column rename issue](https://gitlab.com/gitlab-org/gitlab/-/issues/324160), you
|
||||||
see the following error:
|
see the following error:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
|
|
@ -22,7 +22,7 @@ All Geo sites have the following settings:
|
||||||
| Setting | Description |
|
| Setting | Description |
|
||||||
| --------| ----------- |
|
| --------| ----------- |
|
||||||
| Primary | This marks a Geo site as **primary** site. There can be only one **primary** site. |
|
| Primary | This marks a Geo site as **primary** site. There can be only one **primary** site. |
|
||||||
| Name | The unique identifier for the Geo site. It's highly recommended to use a physical location as a name. Good examples are "London Office" or "us-east-1". Avoid words like "primary", "secondary", "Geo", or "DR". This makes the failover process easier because the physical location does not change, but the Geo site role can. All nodes in a single Geo site use the same site name. Nodes use the `gitlab_rails['geo_node_name']` setting in `/etc/gitlab/gitlab.rb` to lookup their Geo site record in the PostgreSQL database. If `gitlab_rails['geo_node_name']` is not set, then the node's `external_url` with trailing slash is used as fallback. The value of `Name` is case-sensitive, and most characters are allowed. |
|
| Name | The unique identifier for the Geo site. It's highly recommended to use a physical location as a name. Good examples are "London Office" or "us-east-1". Avoid words like "primary", "secondary", "Geo", or "DR". This makes the failover process easier because the physical location does not change, but the Geo site role can. All nodes in a single Geo site use the same site name. Nodes use the `gitlab_rails['geo_node_name']` setting in `/etc/gitlab/gitlab.rb` to lookup their Geo site record in the PostgreSQL database. If `gitlab_rails['geo_node_name']` is not set, the node's `external_url` with trailing slash is used as fallback. The value of `Name` is case-sensitive, and most characters are allowed. |
|
||||||
| URL | The instance's user-facing URL. |
|
| URL | The instance's user-facing URL. |
|
||||||
|
|
||||||
The site you're currently browsing is indicated with a blue `Current` label, and
|
The site you're currently browsing is indicated with a blue `Current` label, and
|
||||||
|
@ -35,32 +35,32 @@ the **primary** node is listed first as `Primary site`.
|
||||||
| Setting | Description |
|
| Setting | Description |
|
||||||
|---------------------------|-------------|
|
|---------------------------|-------------|
|
||||||
| Selective synchronization | Enable Geo [selective sync](../../administration/geo/replication/configuration.md#selective-synchronization) for this **secondary** site. |
|
| Selective synchronization | Enable Geo [selective sync](../../administration/geo/replication/configuration.md#selective-synchronization) for this **secondary** site. |
|
||||||
| Repository sync capacity | Number of concurrent requests this **secondary** site will make to the **primary** site when backfilling repositories. |
|
| Repository sync capacity | Number of concurrent requests this **secondary** site makes to the **primary** site when backfilling repositories. |
|
||||||
| File sync capacity | Number of concurrent requests this **secondary** site will make to the **primary** site when backfilling files. |
|
| File sync capacity | Number of concurrent requests this **secondary** site makes to the **primary** site when backfilling files. |
|
||||||
|
|
||||||
## Geo backfill
|
## Geo backfill
|
||||||
|
|
||||||
**Secondary** sites are notified of changes to repositories and files by the **primary** site,
|
**Secondary** sites are notified of changes to repositories and files by the **primary** site,
|
||||||
and will always attempt to synchronize those changes as quickly as possible.
|
and always attempt to synchronize those changes as quickly as possible.
|
||||||
|
|
||||||
Backfill is the act of populating the **secondary** site with repositories and files that
|
Backfill is the act of populating the **secondary** site with repositories and files that
|
||||||
existed *before* the **secondary** site was added to the database. Since there may be
|
existed *before* the **secondary** site was added to the database. Because there may be
|
||||||
extremely large numbers of repositories and files, it's infeasible to attempt to
|
extremely large numbers of repositories and files, it's not feasible to attempt to
|
||||||
download them all at once, so GitLab places an upper limit on the concurrency of
|
download them all at once; so, GitLab places an upper limit on the concurrency of
|
||||||
these operations.
|
these operations.
|
||||||
|
|
||||||
How long the backfill takes is a function of the maximum concurrency, but higher
|
How long the backfill takes is dependent on the maximum concurrency, but higher
|
||||||
values place more strain on the **primary** site. From [GitLab 10.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3107),
|
values place more strain on the **primary** site. From [GitLab 10.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3107),
|
||||||
the limits are configurable. If your **primary** site has lots of surplus capacity,
|
the limits are configurable. If your **primary** site has lots of surplus capacity,
|
||||||
you can increase the values to complete backfill in a shorter time. If it's
|
you can increase the values to complete backfill in a shorter time. If it's
|
||||||
under heavy load and backfill is reducing its availability for normal requests,
|
under heavy load and backfill reduces its availability for normal requests,
|
||||||
you can decrease them.
|
you can decrease them.
|
||||||
|
|
||||||
## Using a different URL for synchronization
|
## Using a different URL for synchronization
|
||||||
|
|
||||||
The **primary** site's Internal URL is used by **secondary** sites to contact it
|
The **primary** site's Internal URL is used by **secondary** sites to contact it
|
||||||
(to sync repositories, for example). The name Internal URL distinguishes it from
|
(to sync repositories, for example). The name Internal URL distinguishes it from
|
||||||
[External URL](https://docs.gitlab.com/omnibus/settings/configuration.html#configuring-the-external-url-for-gitlab)
|
[External URL](https://docs.gitlab.com/omnibus/settings/configuration.html#configuring-the-external-url-for-gitlab),
|
||||||
which is used by users. Internal URL does not need to be a private address.
|
which is used by users. Internal URL does not need to be a private address.
|
||||||
|
|
||||||
Internal URL defaults to external URL, but you can also customize it:
|
Internal URL defaults to external URL, but you can also customize it:
|
||||||
|
@ -79,21 +79,21 @@ terminated at the load balancer.
|
||||||
|
|
||||||
WARNING:
|
WARNING:
|
||||||
Starting with GitLab 13.3 and [until 13.11](https://gitlab.com/gitlab-org/gitlab/-/issues/325522),
|
Starting with GitLab 13.3 and [until 13.11](https://gitlab.com/gitlab-org/gitlab/-/issues/325522),
|
||||||
using an internal URL that is not accessible to the users will result in the
|
if you use an internal URL that is not accessible to the users, the
|
||||||
OAuth authorization flow not working properly, as the users will get redirected
|
OAuth authorization flow does not work properly, because users are redirected
|
||||||
to the internal URL instead of the external one.
|
to the internal URL instead of the external one.
|
||||||
|
|
||||||
## Multiple secondary sites behind a load balancer
|
## Multiple secondary sites behind a load balancer
|
||||||
|
|
||||||
In GitLab 11.11, **secondary** sites can use identical external URLs as long as
|
In GitLab 11.11, **secondary** sites can use identical external URLs if
|
||||||
a unique `name` is set for each Geo site. The `gitlab.rb` setting
|
a unique `name` is set for each Geo site. The `gitlab.rb` setting
|
||||||
`gitlab_rails['geo_node_name']` must:
|
`gitlab_rails['geo_node_name']` must:
|
||||||
|
|
||||||
- Be set for each GitLab instance that runs `puma`, `sidekiq`, or `geo_logcursor`.
|
- Be set for each GitLab instance that runs `puma`, `sidekiq`, or `geo_logcursor`.
|
||||||
- Match a Geo site name.
|
- Match a Geo site name.
|
||||||
|
|
||||||
The load balancer must use sticky sessions in order to avoid authentication
|
The load balancer must use sticky sessions to avoid authentication
|
||||||
failures and cross site request errors.
|
failures and cross-site request errors.
|
||||||
|
|
||||||
<!-- ## Troubleshooting
|
<!-- ## Troubleshooting
|
||||||
|
|
||||||
|
|
|
@ -10,13 +10,13 @@ type: reference, howto
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/323423) in GitLab 13.12.
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/323423) in GitLab 13.12.
|
||||||
|
|
||||||
WARNING:
|
WARNING:
|
||||||
This product is an early access and is considered a [beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta) feature.
|
This product is in an early-access stage and is considered a [beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta) feature.
|
||||||
|
|
||||||
GitLab DAST's new browser-based crawler is a crawl engine built by GitLab to test Single Page Applications (SPAs) and traditional web applications.
|
GitLab DAST's new browser-based crawler is a crawl engine built by GitLab to test Single Page Applications (SPAs) and traditional web applications.
|
||||||
Due to the reliance of modern web applications on JavaScript, handling SPAs or applications that are dependent on JavaScript is paramount to ensuring proper coverage of an application for Dynamic Application Security Testing (DAST).
|
Due to the reliance of modern web applications on JavaScript, handling SPAs or applications that are dependent on JavaScript is paramount to ensuring proper coverage of an application for Dynamic Application Security Testing (DAST).
|
||||||
|
|
||||||
The browser-based crawler works by loading the target application into a specially-instrumented Chromium browser. A snapshot of the page is taken prior to a search to find any actions that a user might perform,
|
The browser-based crawler works by loading the target application into a specially-instrumented Chromium browser. A snapshot of the page is taken before a search to find any actions that a user might perform,
|
||||||
such as clicking on a link or filling in a form. For each action found, the crawler will execute it, take a new snapshot and determine what in the page changed from the previous snapshot.
|
such as clicking on a link or filling in a form. For each action found, the crawler executes it, takes a new snapshot, and determines what in the page changed from the previous snapshot.
|
||||||
Crawling continues by taking more snapshots and finding subsequent actions.
|
Crawling continues by taking more snapshots and finding subsequent actions.
|
||||||
|
|
||||||
The benefit of crawling by following user actions in a browser is that the crawler can interact with the target application much like a real user would, identifying complex flows that traditional web crawlers don't understand. This results in better coverage of the website.
|
The benefit of crawling by following user actions in a browser is that the crawler can interact with the target application much like a real user would, identifying complex flows that traditional web crawlers don't understand. This results in better coverage of the website.
|
||||||
|
@ -57,17 +57,17 @@ The browser-based crawler can be configured using CI/CD variables.
|
||||||
| `DAST_BROWSER_IGNORED_HOSTS` | List of strings | `site.com,another.com` | Hostnames included in this variable are accessed but not reported against. |
|
| `DAST_BROWSER_IGNORED_HOSTS` | List of strings | `site.com,another.com` | Hostnames included in this variable are accessed but not reported against. |
|
||||||
| `DAST_BROWSER_MAX_ACTIONS` | number | `10000` | The maximum number of actions that the crawler performs. For example, clicking a link, or filling a form. |
|
| `DAST_BROWSER_MAX_ACTIONS` | number | `10000` | The maximum number of actions that the crawler performs. For example, clicking a link, or filling a form. |
|
||||||
| `DAST_BROWSER_MAX_DEPTH` | number | `10` | The maximum number of chained actions that the crawler takes. For example, `Click -> Form Fill -> Click` is a depth of three. |
|
| `DAST_BROWSER_MAX_DEPTH` | number | `10` | The maximum number of chained actions that the crawler takes. For example, `Click -> Form Fill -> Click` is a depth of three. |
|
||||||
| `DAST_BROWSER_NUMBER_OF_BROWSERS` | number | `3` | The maximum number of concurrent browser instances to use. For shared runners on GitLab.com we recommended a maximum of three. Private runners with more resources may benefit from a higher number, but will likely produce little benefit after five to seven instances. |
|
| `DAST_BROWSER_NUMBER_OF_BROWSERS` | number | `3` | The maximum number of concurrent browser instances to use. For shared runners on GitLab.com, we recommended a maximum of three. Private runners with more resources may benefit from a higher number, but are likely to produce little benefit after five to seven instances. |
|
||||||
| `DAST_BROWSER_COOKIES` | dictionary | `abtesting_group:3,region:locked` | A cookie name and value to be added to every request. |
|
| `DAST_BROWSER_COOKIES` | dictionary | `abtesting_group:3,region:locked` | A cookie name and value to be added to every request. |
|
||||||
| `DAST_BROWSER_LOG` | List of strings | `brows:debug,auth:debug` | A list of modules and their intended log level. |
|
| `DAST_BROWSER_LOG` | List of strings | `brows:debug,auth:debug` | A list of modules and their intended log level. |
|
||||||
| `DAST_BROWSER_NAVIGATION_TIMEOUT` | [Duration string](https://golang.org/pkg/time/#ParseDuration) | `15s` | The maximum amount of time to wait for a browser to navigate from one page to another |
|
| `DAST_BROWSER_NAVIGATION_TIMEOUT` | [Duration string](https://golang.org/pkg/time/#ParseDuration) | `15s` | The maximum amount of time to wait for a browser to navigate from one page to another. |
|
||||||
| `DAST_BROWSER_ACTION_TIMEOUT` | [Duration string](https://golang.org/pkg/time/#ParseDuration) | `7s` | The maximum amount of time to wait for a browser to complete an action |
|
| `DAST_BROWSER_ACTION_TIMEOUT` | [Duration string](https://golang.org/pkg/time/#ParseDuration) | `7s` | The maximum amount of time to wait for a browser to complete an action. |
|
||||||
| `DAST_BROWSER_STABILITY_TIMEOUT` | [Duration string](https://golang.org/pkg/time/#ParseDuration) | `7s` | The maximum amount of time to wait for a browser to consider a page loaded and ready for analysis |
|
| `DAST_BROWSER_STABILITY_TIMEOUT` | [Duration string](https://golang.org/pkg/time/#ParseDuration) | `7s` | The maximum amount of time to wait for a browser to consider a page loaded and ready for analysis. |
|
||||||
| `DAST_BROWSER_NAVIGATION_STABILITY_TIMEOUT` | [Duration string](https://golang.org/pkg/time/#ParseDuration) | `7s` | The maximum amount of time to wait for a browser to consider a page loaded and ready for analysis after a navigation completes |
|
| `DAST_BROWSER_NAVIGATION_STABILITY_TIMEOUT` | [Duration string](https://golang.org/pkg/time/#ParseDuration) | `7s` | The maximum amount of time to wait for a browser to consider a page loaded and ready for analysis after a navigation completes. |
|
||||||
| `DAST_BROWSER_ACTION_STABILITY_TIMEOUT` | [Duration string](https://golang.org/pkg/time/#ParseDuration) | `800ms` | The maximum amount of time to wait for a browser to consider a page loaded and ready for analysis after completing an action |
|
| `DAST_BROWSER_ACTION_STABILITY_TIMEOUT` | [Duration string](https://golang.org/pkg/time/#ParseDuration) | `800ms` | The maximum amount of time to wait for a browser to consider a page loaded and ready for analysis after completing an action. |
|
||||||
| `DAST_BROWSER_SEARCH_ELEMENT_TIMEOUT` | [Duration string](https://golang.org/pkg/time/#ParseDuration) | `3s` | The maximum amount of time to allow the browser to search for new elements or navigations |
|
| `DAST_BROWSER_SEARCH_ELEMENT_TIMEOUT` | [Duration string](https://golang.org/pkg/time/#ParseDuration) | `3s` | The maximum amount of time to allow the browser to search for new elements or navigations. |
|
||||||
| `DAST_BROWSER_EXTRACT_ELEMENT_TIMEOUT` | [Duration string](https://golang.org/pkg/time/#ParseDuration) | `5s` | The maximum amount of time to allow the browser to extract newly found elements or navigations |
|
| `DAST_BROWSER_EXTRACT_ELEMENT_TIMEOUT` | [Duration string](https://golang.org/pkg/time/#ParseDuration) | `5s` | The maximum amount of time to allow the browser to extract newly found elements or navigations. |
|
||||||
| `DAST_BROWSER_ELEMENT_TIMEOUT` | [Duration string](https://golang.org/pkg/time/#ParseDuration) | `600ms` | The maximum amount of time to wait for an element before determining it is ready for analysis |
|
| `DAST_BROWSER_ELEMENT_TIMEOUT` | [Duration string](https://golang.org/pkg/time/#ParseDuration) | `600ms` | The maximum amount of time to wait for an element before determining it is ready for analysis. |
|
||||||
|
|
||||||
The [DAST variables](index.md#available-cicd-variables) `SECURE_ANALYZERS_PREFIX`, `DAST_FULL_SCAN_ENABLED`, `DAST_AUTO_UPDATE_ADDONS`, `DAST_EXCLUDE_RULES`, `DAST_REQUEST_HEADERS`, `DAST_HTML_REPORT`, `DAST_MARKDOWN_REPORT`, `DAST_XML_REPORT`,
|
The [DAST variables](index.md#available-cicd-variables) `SECURE_ANALYZERS_PREFIX`, `DAST_FULL_SCAN_ENABLED`, `DAST_AUTO_UPDATE_ADDONS`, `DAST_EXCLUDE_RULES`, `DAST_REQUEST_HEADERS`, `DAST_HTML_REPORT`, `DAST_MARKDOWN_REPORT`, `DAST_XML_REPORT`,
|
||||||
`DAST_AUTH_URL`, `DAST_USERNAME`, `DAST_PASSWORD`, `DAST_USERNAME_FIELD`, `DAST_PASSWORD_FIELD`, `DAST_FIRST_SUBMIT_FIELD`, `DAST_SUBMIT_FIELD`, `DAST_EXCLUDE_URLS`, `DAST_AUTH_VERIFICATION_URL`, `DAST_BROWSER_AUTH_VERIFICATION_SELECTOR`, `DAST_BROWSER_AUTH_VERIFICATION_LOGIN_FORM`, `DAST_BROWSER_AUTH_REPORT`,
|
`DAST_AUTH_URL`, `DAST_USERNAME`, `DAST_PASSWORD`, `DAST_USERNAME_FIELD`, `DAST_PASSWORD_FIELD`, `DAST_FIRST_SUBMIT_FIELD`, `DAST_SUBMIT_FIELD`, `DAST_EXCLUDE_URLS`, `DAST_AUTH_VERIFICATION_URL`, `DAST_BROWSER_AUTH_VERIFICATION_SELECTOR`, `DAST_BROWSER_AUTH_VERIFICATION_LOGIN_FORM`, `DAST_BROWSER_AUTH_REPORT`,
|
||||||
|
@ -80,27 +80,27 @@ While the browser-based crawler crawls modern web applications efficiently, vuln
|
||||||
The crawler runs the target website in a browser with DAST/ZAP configured as the proxy server. This ensures that all requests and responses made by the browser are passively scanned by DAST/ZAP.
|
The crawler runs the target website in a browser with DAST/ZAP configured as the proxy server. This ensures that all requests and responses made by the browser are passively scanned by DAST/ZAP.
|
||||||
When running a full scan, active vulnerability checks executed by DAST/ZAP do not use a browser. This difference in how vulnerabilities are checked can cause issues that require certain features of the target website to be disabled to ensure the scan works as intended.
|
When running a full scan, active vulnerability checks executed by DAST/ZAP do not use a browser. This difference in how vulnerabilities are checked can cause issues that require certain features of the target website to be disabled to ensure the scan works as intended.
|
||||||
|
|
||||||
For example, for a target website that contains forms with Anti-CSRF tokens, a passive scan will scan as intended because the browser displays pages/forms as if a user is viewing the page.
|
For example, for a target website that contains forms with Anti-CSRF tokens, a passive scan works as intended because the browser displays pages and forms as if a user is viewing the page.
|
||||||
However, active vulnerability checks run in a full scan will not be able to submit forms containing Anti-CSRF tokens. In such cases we recommend you disable Anti-CSRF tokens when running a full scan.
|
However, active vulnerability checks that run in a full scan cannot submit forms containing Anti-CSRF tokens. In such cases, we recommend you disable Anti-CSRF tokens when running a full scan.
|
||||||
|
|
||||||
## Managing scan time
|
## Managing scan time
|
||||||
|
|
||||||
It is expected that running the browser-based crawler will result in better coverage for many web applications, when compared to the normal GitLab DAST solution.
|
It is expected that running the browser-based crawler results in better coverage for many web applications, when compared to the normal GitLab DAST solution.
|
||||||
This can come at a cost of increased scan time.
|
This can come at a cost of increased scan time.
|
||||||
|
|
||||||
You can manage the trade-off between coverage and scan time with the following measures:
|
You can manage the trade-off between coverage and scan time with the following measures:
|
||||||
|
|
||||||
- Limit the number of actions executed by the browser with the [variable](#available-cicd-variables) `DAST_BROWSER_MAX_ACTIONS`. The default is `10,000`.
|
- Limit the number of actions executed by the browser with the [variable](#available-cicd-variables) `DAST_BROWSER_MAX_ACTIONS`. The default is `10,000`.
|
||||||
- Limit the page depth that the browser-based crawler will check coverage on with the [variable](#available-cicd-variables) `DAST_BROWSER_MAX_DEPTH`. The crawler uses a breadth-first search strategy, so pages with smaller depth are crawled first. The default is `10`.
|
- Limit the page depth that the browser-based crawler will check coverage on with the [variable](#available-cicd-variables) `DAST_BROWSER_MAX_DEPTH`. The crawler uses a breadth-first search strategy, so pages with smaller depth are crawled first. The default is `10`.
|
||||||
- Vertically scaling the runner and using a higher number of browsers with [variable](#available-cicd-variables) `DAST_BROWSER_NUMBER_OF_BROWSERS`. The default is `3`.
|
- Vertically scale the runner and use a higher number of browsers with [variable](#available-cicd-variables) `DAST_BROWSER_NUMBER_OF_BROWSERS`. The default is `3`.
|
||||||
|
|
||||||
## Timeouts
|
## Timeouts
|
||||||
|
|
||||||
Due to poor network conditions or heavy application load, the default timeouts may not be applicable to your application.
|
Due to poor network conditions or heavy application load, the default timeouts may not be applicable to your application.
|
||||||
|
|
||||||
Browser-based scans offer the ability to adjust various timeouts to ensure it continues smoothly as it transitions from one page to the next. These values are configured using a [Duration string](https://golang.org/pkg/time/#ParseDuration) which allow you to configure durations with a prefix: `m` for minutes, `s` for seconds, and `ms` for milliseconds.
|
Browser-based scans offer the ability to adjust various timeouts to ensure it continues smoothly as it transitions from one page to the next. These values are configured using a [Duration string](https://golang.org/pkg/time/#ParseDuration), which allow you to configure durations with a prefix: `m` for minutes, `s` for seconds, and `ms` for milliseconds.
|
||||||
|
|
||||||
Navigations, or the act of loading a new page, usually require the most amount of time as they are
|
Navigations, or the act of loading a new page, usually require the most amount of time because they are
|
||||||
loading multiple new resources such as JavaScript or CSS files. Depending on the size of these resources, or the speed at which they are returned, the default `DAST_BROWSER_NAVIGATION_TIMEOUT` may not be sufficient.
|
loading multiple new resources such as JavaScript or CSS files. Depending on the size of these resources, or the speed at which they are returned, the default `DAST_BROWSER_NAVIGATION_TIMEOUT` may not be sufficient.
|
||||||
|
|
||||||
Stability timeouts, such as those configurable with `DAST_BROWSER_NAVIGATION_STABILITY_TIMEOUT`, `DAST_BROWSER_STABILITY_TIMEOUT`, and `DAST_BROWSER_ACTION_STABILITY_TIMEOUT` can also be configured. Stability timeouts determine when browser-based scans consider
|
Stability timeouts, such as those configurable with `DAST_BROWSER_NAVIGATION_STABILITY_TIMEOUT`, `DAST_BROWSER_STABILITY_TIMEOUT`, and `DAST_BROWSER_ACTION_STABILITY_TIMEOUT` can also be configured. Stability timeouts determine when browser-based scans consider
|
||||||
|
@ -110,11 +110,11 @@ a page fully loaded. Browser-based scans consider a page loaded when:
|
||||||
1. There are no open or outstanding requests that are deemed important, such as JavaScript and CSS. Media files are usually deemed unimportant.
|
1. There are no open or outstanding requests that are deemed important, such as JavaScript and CSS. Media files are usually deemed unimportant.
|
||||||
1. Depending on whether the browser executed a navigation, was forcibly transitioned, or action:
|
1. Depending on whether the browser executed a navigation, was forcibly transitioned, or action:
|
||||||
|
|
||||||
- There are no new Document Object Model (DOM) modification events after the `DAST_BROWSER_NAVIGATION_STABILITY_TIMEOUT`, `DAST_BROWSER_STABILITY_TIMEOUT` or `DAST_BROWSER_ACTION_STABILITY_TIMEOUT` durations
|
- There are no new Document Object Model (DOM) modification events after the `DAST_BROWSER_NAVIGATION_STABILITY_TIMEOUT`, `DAST_BROWSER_STABILITY_TIMEOUT`, or `DAST_BROWSER_ACTION_STABILITY_TIMEOUT` durations.
|
||||||
|
|
||||||
After these events have occurred, browser-based scans consider the page loaded and ready and attempt the next action.
|
After these events have occurred, browser-based scans consider the page loaded and ready, and attempt the next action.
|
||||||
|
|
||||||
If your application experiences latency or returns many navigation failures, consider adjusting the timeout values such in this example:
|
If your application experiences latency or returns many navigation failures, consider adjusting the timeout values such as in this example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
include:
|
include:
|
||||||
|
@ -132,7 +132,7 @@ dast:
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE:
|
NOTE:
|
||||||
Adjusting these values may impact scan time as they adjust how long each browser waits for various activities to complete.
|
Adjusting these values may impact scan time because they adjust how long each browser waits for various activities to complete.
|
||||||
|
|
||||||
## Debugging scans using logging
|
## Debugging scans using logging
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ The modules that can be configured for logging are as follows:
|
||||||
| Log module | Component overview |
|
| Log module | Component overview |
|
||||||
| ---------- | ----------- |
|
| ---------- | ----------- |
|
||||||
| `AUTH` | Used for creating an authenticated scan. |
|
| `AUTH` | Used for creating an authenticated scan. |
|
||||||
| `BROWS` | Used for querying the state/page of the browser. |
|
| `BROWS` | Used for querying the state or page of the browser. |
|
||||||
| `BPOOL` | The set of browsers that are leased out for crawling. |
|
| `BPOOL` | The set of browsers that are leased out for crawling. |
|
||||||
| `CRAWL` | Used for the core crawler algorithm. |
|
| `CRAWL` | Used for the core crawler algorithm. |
|
||||||
| `DATAB` | Used for persisting data to the internal database. |
|
| `DATAB` | Used for persisting data to the internal database. |
|
||||||
|
|
|
@ -434,7 +434,7 @@ mentioned in the [permissions table above](#project-members-permissions) (they
|
||||||
are unable to browse the project's repository, for example).
|
are unable to browse the project's repository, for example).
|
||||||
|
|
||||||
NOTE:
|
NOTE:
|
||||||
To prevent a guest user from creating projects, as an admin, you can edit the
|
To prevent a guest user from creating projects, as an administrator, you can edit the
|
||||||
user's profile to mark the user as [external](#external-users).
|
user's profile to mark the user as [external](#external-users).
|
||||||
Beware though that even if a user is external, if they already have Reporter or
|
Beware though that even if a user is external, if they already have Reporter or
|
||||||
higher permissions in any project or group, they are **not** counted as a
|
higher permissions in any project or group, they are **not** counted as a
|
||||||
|
@ -519,7 +519,7 @@ In GitLab 11.0, the Master role was renamed to Maintainer.
|
||||||
This table shows granted privileges for jobs triggered by specific types of
|
This table shows granted privileges for jobs triggered by specific types of
|
||||||
users:
|
users:
|
||||||
|
|
||||||
| Action | Guest, Reporter | Developer |Maintainer| Admin |
|
| Action | Guest, Reporter | Developer |Maintainer| Administrator |
|
||||||
|---------------------------------------------|-----------------|-------------|----------|---------|
|
|---------------------------------------------|-----------------|-------------|----------|---------|
|
||||||
| Run CI job | | ✓ | ✓ | ✓ |
|
| Run CI job | | ✓ | ✓ | ✓ |
|
||||||
| Clone source and LFS from current project | | ✓ | ✓ | ✓ |
|
| Clone source and LFS from current project | | ✓ | ✓ | ✓ |
|
||||||
|
@ -555,7 +555,7 @@ for more information.
|
||||||
|
|
||||||
## LDAP users permissions
|
## LDAP users permissions
|
||||||
|
|
||||||
In GitLab 8.15 and later, LDAP user permissions can now be manually overridden by an admin user.
|
In GitLab 8.15 and later, LDAP user permissions can now be manually overridden by an administrator.
|
||||||
Read through the documentation on [LDAP users permissions](group/index.md#manage-group-memberships-via-ldap) to learn more.
|
Read through the documentation on [LDAP users permissions](group/index.md#manage-group-memberships-via-ldap) to learn more.
|
||||||
|
|
||||||
## Project aliases
|
## Project aliases
|
||||||
|
|
|
@ -122,7 +122,7 @@ module.exports = (path, options = {}) => {
|
||||||
'^.+\\.(md|zip|png)$': 'jest-raw-loader',
|
'^.+\\.(md|zip|png)$': 'jest-raw-loader',
|
||||||
},
|
},
|
||||||
transformIgnorePatterns: [
|
transformIgnorePatterns: [
|
||||||
'node_modules/(?!(@gitlab/ui|@gitlab/favicon-overlay|bootstrap-vue|three|monaco-editor|monaco-yaml|fast-mersenne-twister|prosemirror-markdown)/)',
|
'node_modules/(?!(@gitlab/ui|@gitlab/favicon-overlay|bootstrap-vue|three|monaco-editor|monaco-yaml|fast-mersenne-twister|prosemirror-markdown|dateformat)/)',
|
||||||
],
|
],
|
||||||
timers: 'fake',
|
timers: 'fake',
|
||||||
testEnvironment: '<rootDir>/spec/frontend/environment.js',
|
testEnvironment: '<rootDir>/spec/frontend/environment.js',
|
||||||
|
|
|
@ -253,6 +253,7 @@ semgrep-sast:
|
||||||
- '**/*.ts'
|
- '**/*.ts'
|
||||||
- '**/*.tsx'
|
- '**/*.tsx'
|
||||||
- '**/*.c'
|
- '**/*.c'
|
||||||
|
- '**/*.go'
|
||||||
|
|
||||||
sobelow-sast:
|
sobelow-sast:
|
||||||
extends: .sast-analyzer
|
extends: .sast-analyzer
|
||||||
|
|
|
@ -27,23 +27,20 @@ variables:
|
||||||
# (SAST, Dependency Scanning, ...)
|
# (SAST, Dependency Scanning, ...)
|
||||||
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
|
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
|
||||||
|
|
||||||
.dast_base:
|
dast:
|
||||||
stage: dast
|
stage: dast
|
||||||
image:
|
image:
|
||||||
name: "$SECURE_ANALYZERS_PREFIX/dast:$DAST_VERSION"
|
name: "$SECURE_ANALYZERS_PREFIX/dast:$DAST_VERSION"
|
||||||
variables:
|
variables:
|
||||||
GIT_STRATEGY: none
|
GIT_STRATEGY: none
|
||||||
|
allow_failure: true
|
||||||
script:
|
script:
|
||||||
- export DAST_WEBSITE=${DAST_WEBSITE:-$(cat environment_url.txt)}
|
- export DAST_WEBSITE=${DAST_WEBSITE:-$(cat environment_url.txt)}
|
||||||
- if [ -z "$DAST_WEBSITE$DAST_API_SPECIFICATION" ]; then echo "Either DAST_WEBSITE or DAST_API_SPECIFICATION must be set. See https://docs.gitlab.com/ee/user/application_security/dast/#configuration for more details." && exit 1; fi
|
- if [ -z "$DAST_WEBSITE$DAST_API_SPECIFICATION" ]; then echo "Either DAST_WEBSITE or DAST_API_SPECIFICATION must be set. See https://docs.gitlab.com/ee/user/application_security/dast/#configuration for more details." && exit 1; fi
|
||||||
- /analyze
|
- /analyze
|
||||||
allow_failure: true
|
|
||||||
artifacts:
|
artifacts:
|
||||||
reports:
|
reports:
|
||||||
dast: gl-dast-report.json
|
dast: gl-dast-report.json
|
||||||
|
|
||||||
dast:
|
|
||||||
extends: .dast_base
|
|
||||||
rules:
|
rules:
|
||||||
- if: $DAST_DISABLED
|
- if: $DAST_DISABLED
|
||||||
when: never
|
when: never
|
||||||
|
|
|
@ -27,23 +27,20 @@ variables:
|
||||||
# (SAST, Dependency Scanning, ...)
|
# (SAST, Dependency Scanning, ...)
|
||||||
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
|
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
|
||||||
|
|
||||||
.dast_base:
|
dast:
|
||||||
stage: dast
|
stage: dast
|
||||||
image:
|
image:
|
||||||
name: "$SECURE_ANALYZERS_PREFIX/dast:$DAST_VERSION"
|
name: "$SECURE_ANALYZERS_PREFIX/dast:$DAST_VERSION"
|
||||||
variables:
|
variables:
|
||||||
GIT_STRATEGY: none
|
GIT_STRATEGY: none
|
||||||
|
allow_failure: true
|
||||||
script:
|
script:
|
||||||
- export DAST_WEBSITE=${DAST_WEBSITE:-$(cat environment_url.txt)}
|
- export DAST_WEBSITE=${DAST_WEBSITE:-$(cat environment_url.txt)}
|
||||||
- if [ -z "$DAST_WEBSITE$DAST_API_SPECIFICATION" ]; then echo "Either DAST_WEBSITE or DAST_API_SPECIFICATION must be set. See https://docs.gitlab.com/ee/user/application_security/dast/#configuration for more details." && exit 1; fi
|
- if [ -z "$DAST_WEBSITE$DAST_API_SPECIFICATION" ]; then echo "Either DAST_WEBSITE or DAST_API_SPECIFICATION must be set. See https://docs.gitlab.com/ee/user/application_security/dast/#configuration for more details." && exit 1; fi
|
||||||
- /analyze
|
- /analyze
|
||||||
allow_failure: true
|
|
||||||
artifacts:
|
artifacts:
|
||||||
reports:
|
reports:
|
||||||
dast: gl-dast-report.json
|
dast: gl-dast-report.json
|
||||||
|
|
||||||
dast:
|
|
||||||
extends: .dast_base
|
|
||||||
rules:
|
rules:
|
||||||
- if: $DAST_DISABLED
|
- if: $DAST_DISABLED
|
||||||
when: never
|
when: never
|
||||||
|
|
|
@ -140,6 +140,7 @@ module Gitlab
|
||||||
def idempotent?
|
def idempotent?
|
||||||
return false unless worker_klass
|
return false unless worker_klass
|
||||||
return false unless worker_klass.respond_to?(:idempotent?)
|
return false unless worker_klass.respond_to?(:idempotent?)
|
||||||
|
return false unless preserve_wal_location? || !worker_klass.utilizes_load_balancing_capabilities?
|
||||||
|
|
||||||
worker_klass.idempotent?
|
worker_klass.idempotent?
|
||||||
end
|
end
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
"@gitlab/favicon-overlay": "2.0.0",
|
"@gitlab/favicon-overlay": "2.0.0",
|
||||||
"@gitlab/svgs": "1.212.0",
|
"@gitlab/svgs": "1.212.0",
|
||||||
"@gitlab/tributejs": "1.0.0",
|
"@gitlab/tributejs": "1.0.0",
|
||||||
"@gitlab/ui": "32.11.0",
|
"@gitlab/ui": "32.11.1",
|
||||||
"@gitlab/visual-review-tools": "1.6.1",
|
"@gitlab/visual-review-tools": "1.6.1",
|
||||||
"@rails/actioncable": "6.1.3-2",
|
"@rails/actioncable": "6.1.3-2",
|
||||||
"@rails/ujs": "6.1.3-2",
|
"@rails/ujs": "6.1.3-2",
|
||||||
|
@ -120,7 +120,7 @@
|
||||||
"d3": "^5.16.0",
|
"d3": "^5.16.0",
|
||||||
"d3-sankey": "^0.12.3",
|
"d3-sankey": "^0.12.3",
|
||||||
"d3-selection": "^1.2.0",
|
"d3-selection": "^1.2.0",
|
||||||
"dateformat": "^4.5.1",
|
"dateformat": "^5.0.1",
|
||||||
"deckar01-task_list": "^2.3.1",
|
"deckar01-task_list": "^2.3.1",
|
||||||
"diff": "^3.4.0",
|
"diff": "^3.4.0",
|
||||||
"dompurify": "^2.3.3",
|
"dompurify": "^2.3.3",
|
||||||
|
|
|
@ -84,6 +84,7 @@ RSpec.describe 'Database schema' do
|
||||||
subscriptions: %w[user_id subscribable_id],
|
subscriptions: %w[user_id subscribable_id],
|
||||||
suggestions: %w[commit_id],
|
suggestions: %w[commit_id],
|
||||||
taggings: %w[tag_id taggable_id tagger_id],
|
taggings: %w[tag_id taggable_id tagger_id],
|
||||||
|
terraform_state_versions: %w[ci_build_id],
|
||||||
timelogs: %w[user_id],
|
timelogs: %w[user_id],
|
||||||
todos: %w[target_id commit_id],
|
todos: %w[target_id commit_id],
|
||||||
uploads: %w[model_id],
|
uploads: %w[model_id],
|
||||||
|
|
|
@ -52,13 +52,13 @@ exports[`Alert integration settings form default state should match the default
|
||||||
block="true"
|
block="true"
|
||||||
category="primary"
|
category="primary"
|
||||||
clearalltext="Clear all"
|
clearalltext="Clear all"
|
||||||
|
clearalltextclass="gl-px-5"
|
||||||
data-qa-selector="incident_templates_dropdown"
|
data-qa-selector="incident_templates_dropdown"
|
||||||
headertext=""
|
headertext=""
|
||||||
hideheaderborder="true"
|
hideheaderborder="true"
|
||||||
highlighteditemstitle="Selected"
|
highlighteditemstitle="Selected"
|
||||||
highlighteditemstitleclass="gl-px-5"
|
highlighteditemstitleclass="gl-px-5"
|
||||||
id="alert-integration-settings-issue-template"
|
id="alert-integration-settings-issue-template"
|
||||||
showhighlighteditemstitle="true"
|
|
||||||
size="medium"
|
size="medium"
|
||||||
text="selecte_tmpl"
|
text="selecte_tmpl"
|
||||||
variant="default"
|
variant="default"
|
||||||
|
|
|
@ -46,21 +46,7 @@ exports[`Remove cluster confirmation modal renders splitbutton with modal includ
|
||||||
>
|
>
|
||||||
<!---->
|
<!---->
|
||||||
|
|
||||||
<div
|
<!---->
|
||||||
class="gl-display-flex gl-flex-direction-row gl-justify-content-space-between gl-align-items-center gl-px-5"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="gl-display-flex"
|
|
||||||
>
|
|
||||||
<!---->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="gl-display-flex"
|
|
||||||
>
|
|
||||||
<!---->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="gl-new-dropdown-contents"
|
class="gl-new-dropdown-contents"
|
||||||
|
|
|
@ -11,14 +11,7 @@ exports[`content_editor/components/toolbar_link_button renders dropdown componen
|
||||||
<ul role=\\"menu\\" tabindex=\\"-1\\" class=\\"dropdown-menu\\">
|
<ul role=\\"menu\\" tabindex=\\"-1\\" class=\\"dropdown-menu\\">
|
||||||
<div class=\\"gl-new-dropdown-inner\\">
|
<div class=\\"gl-new-dropdown-inner\\">
|
||||||
<!---->
|
<!---->
|
||||||
<div class=\\"gl-display-flex gl-flex-direction-row gl-justify-content-space-between gl-align-items-center gl-px-5\\">
|
<!---->
|
||||||
<div class=\\"gl-display-flex\\">
|
|
||||||
<!---->
|
|
||||||
</div>
|
|
||||||
<div class=\\"gl-display-flex\\">
|
|
||||||
<!---->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class=\\"gl-new-dropdown-contents\\">
|
<div class=\\"gl-new-dropdown-contents\\">
|
||||||
<!---->
|
<!---->
|
||||||
<li role=\\"presentation\\" class=\\"gl-px-3!\\">
|
<li role=\\"presentation\\" class=\\"gl-px-3!\\">
|
||||||
|
|
|
@ -4,13 +4,13 @@ exports[`Design management design version dropdown component renders design vers
|
||||||
<gl-dropdown-stub
|
<gl-dropdown-stub
|
||||||
category="primary"
|
category="primary"
|
||||||
clearalltext="Clear all"
|
clearalltext="Clear all"
|
||||||
|
clearalltextclass="gl-px-5"
|
||||||
headertext=""
|
headertext=""
|
||||||
hideheaderborder="true"
|
hideheaderborder="true"
|
||||||
highlighteditemstitle="Selected"
|
highlighteditemstitle="Selected"
|
||||||
highlighteditemstitleclass="gl-px-5"
|
highlighteditemstitleclass="gl-px-5"
|
||||||
issueiid=""
|
issueiid=""
|
||||||
projectpath=""
|
projectpath=""
|
||||||
showhighlighteditemstitle="true"
|
|
||||||
size="small"
|
size="small"
|
||||||
text="Showing latest version"
|
text="Showing latest version"
|
||||||
variant="default"
|
variant="default"
|
||||||
|
@ -85,13 +85,13 @@ exports[`Design management design version dropdown component renders design vers
|
||||||
<gl-dropdown-stub
|
<gl-dropdown-stub
|
||||||
category="primary"
|
category="primary"
|
||||||
clearalltext="Clear all"
|
clearalltext="Clear all"
|
||||||
|
clearalltextclass="gl-px-5"
|
||||||
headertext=""
|
headertext=""
|
||||||
hideheaderborder="true"
|
hideheaderborder="true"
|
||||||
highlighteditemstitle="Selected"
|
highlighteditemstitle="Selected"
|
||||||
highlighteditemstitleclass="gl-px-5"
|
highlighteditemstitleclass="gl-px-5"
|
||||||
issueiid=""
|
issueiid=""
|
||||||
projectpath=""
|
projectpath=""
|
||||||
showhighlighteditemstitle="true"
|
|
||||||
size="small"
|
size="small"
|
||||||
text="Showing latest version"
|
text="Showing latest version"
|
||||||
variant="default"
|
variant="default"
|
||||||
|
|
|
@ -127,21 +127,7 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
|
||||||
>
|
>
|
||||||
<!---->
|
<!---->
|
||||||
|
|
||||||
<div
|
<!---->
|
||||||
class="gl-display-flex gl-flex-direction-row gl-justify-content-space-between gl-align-items-center gl-px-5"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="gl-display-flex"
|
|
||||||
>
|
|
||||||
<!---->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="gl-display-flex"
|
|
||||||
>
|
|
||||||
<!---->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="gl-new-dropdown-contents"
|
class="gl-new-dropdown-contents"
|
||||||
|
@ -272,21 +258,7 @@ exports[`JiraImportForm table body shows correct information in each cell 1`] =
|
||||||
>
|
>
|
||||||
<!---->
|
<!---->
|
||||||
|
|
||||||
<div
|
<!---->
|
||||||
class="gl-display-flex gl-flex-direction-row gl-justify-content-space-between gl-align-items-center gl-px-5"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="gl-display-flex"
|
|
||||||
>
|
|
||||||
<!---->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="gl-display-flex"
|
|
||||||
>
|
|
||||||
<!---->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="gl-new-dropdown-contents"
|
class="gl-new-dropdown-contents"
|
||||||
|
|
|
@ -37,6 +37,7 @@ exports[`Dashboard template matches the default snapshot 1`] = `
|
||||||
category="primary"
|
category="primary"
|
||||||
class="flex-grow-1"
|
class="flex-grow-1"
|
||||||
clearalltext="Clear all"
|
clearalltext="Clear all"
|
||||||
|
clearalltextclass="gl-px-5"
|
||||||
data-qa-selector="environments_dropdown"
|
data-qa-selector="environments_dropdown"
|
||||||
headertext=""
|
headertext=""
|
||||||
hideheaderborder="true"
|
hideheaderborder="true"
|
||||||
|
@ -44,7 +45,6 @@ exports[`Dashboard template matches the default snapshot 1`] = `
|
||||||
highlighteditemstitleclass="gl-px-5"
|
highlighteditemstitleclass="gl-px-5"
|
||||||
id="monitor-environments-dropdown"
|
id="monitor-environments-dropdown"
|
||||||
menu-class="monitor-environment-dropdown-menu"
|
menu-class="monitor-environment-dropdown-menu"
|
||||||
showhighlighteditemstitle="true"
|
|
||||||
size="medium"
|
size="medium"
|
||||||
text="production"
|
text="production"
|
||||||
toggleclass="dropdown-menu-toggle"
|
toggleclass="dropdown-menu-toggle"
|
||||||
|
|
|
@ -12,11 +12,11 @@ exports[`Code Coverage when fetching data is successful matches the snapshot 1`]
|
||||||
<gl-dropdown-stub
|
<gl-dropdown-stub
|
||||||
category="primary"
|
category="primary"
|
||||||
clearalltext="Clear all"
|
clearalltext="Clear all"
|
||||||
|
clearalltextclass="gl-px-5"
|
||||||
headertext=""
|
headertext=""
|
||||||
hideheaderborder="true"
|
hideheaderborder="true"
|
||||||
highlighteditemstitle="Selected"
|
highlighteditemstitle="Selected"
|
||||||
highlighteditemstitleclass="gl-px-5"
|
highlighteditemstitleclass="gl-px-5"
|
||||||
showhighlighteditemstitle="true"
|
|
||||||
size="medium"
|
size="medium"
|
||||||
text="rspec"
|
text="rspec"
|
||||||
variant="default"
|
variant="default"
|
||||||
|
|
|
@ -4,12 +4,12 @@ exports[`Clone Dropdown Button rendering matches the snapshot 1`] = `
|
||||||
<gl-dropdown-stub
|
<gl-dropdown-stub
|
||||||
category="primary"
|
category="primary"
|
||||||
clearalltext="Clear all"
|
clearalltext="Clear all"
|
||||||
|
clearalltextclass="gl-px-5"
|
||||||
headertext=""
|
headertext=""
|
||||||
hideheaderborder="true"
|
hideheaderborder="true"
|
||||||
highlighteditemstitle="Selected"
|
highlighteditemstitle="Selected"
|
||||||
highlighteditemstitleclass="gl-px-5"
|
highlighteditemstitleclass="gl-px-5"
|
||||||
right="true"
|
right="true"
|
||||||
showhighlighteditemstitle="true"
|
|
||||||
size="medium"
|
size="medium"
|
||||||
text="Clone"
|
text="Clone"
|
||||||
variant="info"
|
variant="info"
|
||||||
|
|
|
@ -4,12 +4,12 @@ exports[`SplitButton renders actionItems 1`] = `
|
||||||
<gl-dropdown-stub
|
<gl-dropdown-stub
|
||||||
category="primary"
|
category="primary"
|
||||||
clearalltext="Clear all"
|
clearalltext="Clear all"
|
||||||
|
clearalltextclass="gl-px-5"
|
||||||
headertext=""
|
headertext=""
|
||||||
hideheaderborder="true"
|
hideheaderborder="true"
|
||||||
highlighteditemstitle="Selected"
|
highlighteditemstitle="Selected"
|
||||||
highlighteditemstitleclass="gl-px-5"
|
highlighteditemstitleclass="gl-px-5"
|
||||||
menu-class=""
|
menu-class=""
|
||||||
showhighlighteditemstitle="true"
|
|
||||||
size="medium"
|
size="medium"
|
||||||
split="true"
|
split="true"
|
||||||
text="professor"
|
text="professor"
|
||||||
|
|
|
@ -472,6 +472,26 @@ RSpec.describe Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob, :clean_gi
|
||||||
expect(duplicate_job).to be_idempotent
|
expect(duplicate_job).to be_idempotent
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when worker class is utilizing load balancing capabilities' do
|
||||||
|
before do
|
||||||
|
allow(AuthorizedProjectsWorker).to receive(:utilizes_load_balancing_capabilities?).and_return(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns true' do
|
||||||
|
expect(duplicate_job).to be_idempotent
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when preserve_latest_wal_locations_for_idempotent_jobs feature flag is disabled' do
|
||||||
|
before do
|
||||||
|
stub_feature_flags(preserve_latest_wal_locations_for_idempotent_jobs: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false' do
|
||||||
|
expect(duplicate_job).not_to be_idempotent
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def existing_wal_location_key(idempotency_key, config_name)
|
def existing_wal_location_key(idempotency_key, config_name)
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
require_migration!('drop_temporary_columns_and_triggers_for_taggings')
|
||||||
|
|
||||||
|
RSpec.describe DropTemporaryColumnsAndTriggersForTaggings do
|
||||||
|
let(:taggings_table) { table(:taggings) }
|
||||||
|
|
||||||
|
it 'correctly migrates up and down' do
|
||||||
|
reversible_migration do |migration|
|
||||||
|
migration.before -> {
|
||||||
|
expect(taggings_table.column_names).to include('id_convert_to_bigint')
|
||||||
|
expect(taggings_table.column_names).to include('taggable_id_convert_to_bigint')
|
||||||
|
}
|
||||||
|
|
||||||
|
migration.after -> {
|
||||||
|
taggings_table.reset_column_information
|
||||||
|
expect(taggings_table.column_names).not_to include('id_convert_to_bigint')
|
||||||
|
expect(taggings_table.column_names).not_to include('taggable_id_convert_to_bigint')
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -29,6 +29,7 @@ RSpec.describe Ci::Build do
|
||||||
it { is_expected.to have_one(:deployment) }
|
it { is_expected.to have_one(:deployment) }
|
||||||
it { is_expected.to have_one(:runner_session) }
|
it { is_expected.to have_one(:runner_session) }
|
||||||
it { is_expected.to have_one(:trace_metadata) }
|
it { is_expected.to have_one(:trace_metadata) }
|
||||||
|
it { is_expected.to have_many(:terraform_state_versions).dependent(:nullify).inverse_of(:build) }
|
||||||
|
|
||||||
it { is_expected.to validate_presence_of(:ref) }
|
it { is_expected.to validate_presence_of(:ref) }
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ RSpec.describe Ci::RetryBuildService do
|
||||||
job_artifacts_network_referee job_artifacts_dotenv
|
job_artifacts_network_referee job_artifacts_dotenv
|
||||||
job_artifacts_cobertura needs job_artifacts_accessibility
|
job_artifacts_cobertura needs job_artifacts_accessibility
|
||||||
job_artifacts_requirements job_artifacts_coverage_fuzzing
|
job_artifacts_requirements job_artifacts_coverage_fuzzing
|
||||||
job_artifacts_api_fuzzing].freeze
|
job_artifacts_api_fuzzing terraform_state_versions].freeze
|
||||||
|
|
||||||
ignore_accessors =
|
ignore_accessors =
|
||||||
%i[type lock_version target_url base_tags trace_sections
|
%i[type lock_version target_url base_tags trace_sections
|
||||||
|
@ -88,6 +88,7 @@ RSpec.describe Ci::RetryBuildService do
|
||||||
|
|
||||||
create(:ci_job_variable, job: build)
|
create(:ci_job_variable, job: build)
|
||||||
create(:ci_build_need, build: build)
|
create(:ci_build_need, build: build)
|
||||||
|
create(:terraform_state_version, build: build)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'clone accessors' do
|
describe 'clone accessors' do
|
||||||
|
|
20
yarn.lock
20
yarn.lock
|
@ -974,15 +974,15 @@
|
||||||
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
|
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
|
||||||
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
|
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
|
||||||
|
|
||||||
"@gitlab/ui@32.11.0":
|
"@gitlab/ui@32.11.1":
|
||||||
version "32.11.0"
|
version "32.11.1"
|
||||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-32.11.0.tgz#8c4a1724c1733a243f96e4a4813ae7f348502ba6"
|
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-32.11.1.tgz#05b1587cb3df06abdebe9f06d744c5f18c90a0bb"
|
||||||
integrity sha512-EqP5Ub/IWEi5ErX0txx5vsd6hF7d7dOT5GqaRX6rVaLsUhWLYQZ8ld2yEl5Hx7FLki1t3uag17KII5FcvRTDLg==
|
integrity sha512-LzqEA2aiaqN39qwqNw039Hv9abFsYZJu0RpXikx6/OKCYwVRvynja7oRMwkB2Q+xLOb7YfOoQweWUk1jo6hElw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/standalone" "^7.0.0"
|
"@babel/standalone" "^7.0.0"
|
||||||
bootstrap-vue "2.18.1"
|
bootstrap-vue "2.18.1"
|
||||||
copy-to-clipboard "^3.0.8"
|
copy-to-clipboard "^3.0.8"
|
||||||
dompurify "^2.3.2"
|
dompurify "^2.3.3"
|
||||||
echarts "^4.9.0"
|
echarts "^4.9.0"
|
||||||
highlight.js "^10.6.0"
|
highlight.js "^10.6.0"
|
||||||
js-beautify "^1.8.8"
|
js-beautify "^1.8.8"
|
||||||
|
@ -4333,10 +4333,10 @@ date-now@^0.1.4:
|
||||||
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
|
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
|
||||||
integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=
|
integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=
|
||||||
|
|
||||||
dateformat@^4.5.1:
|
dateformat@^5.0.1:
|
||||||
version "4.5.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.5.1.tgz#c20e7a9ca77d147906b6dc2261a8be0a5bd2173c"
|
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-5.0.1.tgz#60a27a2deb339f888ba4532f533e25ac73ca3d19"
|
||||||
integrity sha512-OD0TZ+B7yP7ZgpJf5K2DIbj3FZvFvxgFUuaqA/V5zTjAtAAXZ1E8bktHxmAGs4x5b7PflqA9LeQ84Og7wYtF7Q==
|
integrity sha512-DrcKxOW2am3mtqoJwBTK3OlWcF0QSk1p8diEWwpu3Mf//VdURD7XVaeOV738JvcaBiFfm9o2fisoMhiJH0aYxg==
|
||||||
|
|
||||||
de-indent@^1.0.2:
|
de-indent@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
|
@ -4675,7 +4675,7 @@ dompurify@2.3.0:
|
||||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.0.tgz#07bb39515e491588e5756b1d3e8375b5964814e2"
|
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.0.tgz#07bb39515e491588e5756b1d3e8375b5964814e2"
|
||||||
integrity sha512-VV5C6Kr53YVHGOBKO/F86OYX6/iLTw2yVSI721gKetxpHCK/V5TaLEf9ODjRgl1KLSWRMY6cUhAbv/c+IUnwQw==
|
integrity sha512-VV5C6Kr53YVHGOBKO/F86OYX6/iLTw2yVSI721gKetxpHCK/V5TaLEf9ODjRgl1KLSWRMY6cUhAbv/c+IUnwQw==
|
||||||
|
|
||||||
dompurify@^2.3.2, dompurify@^2.3.3:
|
dompurify@^2.3.3:
|
||||||
version "2.3.3"
|
version "2.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.3.tgz#c1af3eb88be47324432964d8abc75cf4b98d634c"
|
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.3.tgz#c1af3eb88be47324432964d8abc75cf4b98d634c"
|
||||||
integrity sha512-dqnqRkPMAjOZE0FogZ+ceJNM2dZ3V/yNOuFB7+39qpO93hHhfRpHw3heYQC7DPK9FqbQTfBKUJhiSfz4MvXYwg==
|
integrity sha512-dqnqRkPMAjOZE0FogZ+ceJNM2dZ3V/yNOuFB7+39qpO93hHhfRpHw3heYQC7DPK9FqbQTfBKUJhiSfz4MvXYwg==
|
||||||
|
|
Loading…
Reference in a new issue