Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-11-17 06:13:50 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-11-17 06:13:50 +0300
commiteac5de031737387f9e00be37ae429a426fe7d19d (patch)
treedee06fb1b4d18c362de32b41f30e77fd577154f7
parente7ddd83a4484ff9fdf355d8eb726db9bfd3dd521 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/ci/ci_variable_list/components/ci_variable_drawer.vue92
-rw-r--r--app/assets/javascripts/ci/ci_variable_list/constants.js4
-rw-r--r--app/views/projects/_new_project_fields.html.haml2
-rw-r--r--config/feature_flags/development/use_500_page_size_for_contribution_analytics.yml (renamed from config/feature_flags/development/import_fallback_to_db_empty_cache.yml)10
-rw-r--r--doc/development/database/not_null_constraints.md117
-rw-r--r--doc/development/i18n/externalization.md5
-rw-r--r--doc/development/i18n/index.md4
-rw-r--r--doc/user/application_security/index.md4
-rw-r--r--doc/user/application_security/policies/scan-execution-policies.md7
-rw-r--r--lib/gitlab/github_import/issuable_finder.rb2
-rw-r--r--lib/gitlab/github_import/label_finder.rb2
-rw-r--r--lib/gitlab/github_import/milestone_finder.rb2
-rw-r--r--locale/gitlab.pot11
-rw-r--r--package.json2
-rw-r--r--spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js140
-rw-r--r--spec/lib/gitlab/github_import/issuable_finder_spec.rb28
-rw-r--r--spec/lib/gitlab/github_import/label_finder_spec.rb28
-rw-r--r--spec/lib/gitlab/github_import/milestone_finder_spec.rb30
-rw-r--r--spec/support/shared_examples/features/variable_list_drawer_shared_examples.rb5
-rw-r--r--yarn.lock8
20 files changed, 337 insertions, 166 deletions
diff --git a/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_drawer.vue b/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_drawer.vue
index ccfe773b01f..33ae747df22 100644
--- a/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_drawer.vue
+++ b/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_drawer.vue
@@ -29,8 +29,10 @@ import {
EVENT_ACTION,
EXPANDED_VARIABLES_NOTE,
FLAG_LINK_TITLE,
+ MASKED_VALUE_MIN_LENGTH,
VARIABLE_ACTIONS,
variableOptions,
+ WHITESPACE_REG_EX,
} from '../constants';
import CiEnvironmentsDropdown from './ci_environments_dropdown.vue';
import { awsTokenList } from './ci_variable_autocomplete_tokens';
@@ -54,21 +56,30 @@ export const i18n = {
maskedDescription: s__(
'CiVariables|Variable will be masked in job logs. Requires values to meet regular expression requirements.',
),
+ maskedValueMinLengthValidationText: s__(
+ 'CiVariables|The value must have at least %{charsAmount} characters.',
+ ),
modalDeleteMessage: s__('CiVariables|Do you want to delete the variable %{key}?'),
protectedField: s__('CiVariables|Protect variable'),
protectedDescription: s__(
'CiVariables|Export variable to pipelines running on protected branches and tags only.',
),
+ unsupportedCharsValidationText: s__(
+ 'CiVariables|This value cannot be masked because it contains the following characters: %{unsupportedChars}.',
+ ),
+ unsupportedAndWhitespaceCharsValidationText: s__(
+ 'CiVariables|This value cannot be masked because it contains the following characters: %{unsupportedChars} and whitespace characters.',
+ ),
valueFeedback: {
rawHelpText: s__('CiVariables|Variable value will be evaluated as raw string.'),
- maskedReqsNotMet: s__(
- 'CiVariables|This variable value does not meet the masking requirements.',
- ),
},
variableReferenceTitle: s__('CiVariables|Value might contain a variable reference'),
variableReferenceDescription: s__(
'CiVariables|Unselect "Expand variable reference" if you want to use the variable value as a raw string.',
),
+ whitespaceCharsValidationText: s__(
+ 'CiVariables|This value cannot be masked because it contains the following characters: whitespace characters.',
+ ),
type: __('Type'),
value: __('Value'),
};
@@ -169,11 +180,76 @@ export default {
isEditing() {
return this.mode === EDIT_VARIABLE_ACTION;
},
+ isMaskedValueContainsWhitespaceChars() {
+ return this.isValueMaskable && WHITESPACE_REG_EX.test(this.variable.value);
+ },
maskedRegexToUse() {
return this.variable.raw ? this.maskableRawRegex : this.maskableRegex;
},
- maskedReqsNotMetText() {
- return !this.isMaskedReqsMet ? this.$options.i18n.valueFeedback.maskedReqsNotMet : '';
+ maskedSupportedCharsRegEx() {
+ const supportedChars = this.maskedRegexToUse.replace('^', '').replace(/{(\d,)}\$/, '');
+ return new RegExp(supportedChars, 'g');
+ },
+ maskedValueMinLengthValidationText() {
+ return sprintf(this.$options.i18n.maskedValueMinLengthValidationText, {
+ charsAmount: MASKED_VALUE_MIN_LENGTH,
+ });
+ },
+ unsupportedCharsList() {
+ if (this.isMaskedReqsMet) {
+ return [];
+ }
+
+ return [
+ ...new Set(
+ this.variable.value
+ .replace(WHITESPACE_REG_EX, '')
+ .replace(this.maskedSupportedCharsRegEx, '')
+ .split(''),
+ ),
+ ];
+ },
+ unsupportedChars() {
+ return this.unsupportedCharsList.join(', ');
+ },
+ unsupportedCharsValidationText() {
+ return sprintf(
+ this.$options.i18n.unsupportedCharsValidationText,
+ {
+ unsupportedChars: this.unsupportedChars,
+ },
+ false,
+ );
+ },
+ unsupportedAndWhitespaceCharsValidationText() {
+ return sprintf(
+ this.$options.i18n.unsupportedAndWhitespaceCharsValidationText,
+ {
+ unsupportedChars: this.unsupportedChars,
+ },
+ false,
+ );
+ },
+ maskedValidationIssuesText() {
+ if (this.isMaskedReqsMet) {
+ return '';
+ }
+
+ let validationIssuesText = '';
+
+ if (this.unsupportedCharsList.length && !this.isMaskedValueContainsWhitespaceChars) {
+ validationIssuesText = this.unsupportedCharsValidationText;
+ } else if (this.unsupportedCharsList.length && this.isMaskedValueContainsWhitespaceChars) {
+ validationIssuesText = this.unsupportedAndWhitespaceCharsValidationText;
+ } else if (!this.unsupportedCharsList.length && this.isMaskedValueContainsWhitespaceChars) {
+ validationIssuesText = this.$options.i18n.whitespaceCharsValidationText;
+ }
+
+ if (this.variable.value.length < MASKED_VALUE_MIN_LENGTH) {
+ validationIssuesText += ` ${this.maskedValueMinLengthValidationText}`;
+ }
+
+ return validationIssuesText.trim();
},
modalActionText() {
return this.isEditing ? this.$options.i18n.editVariable : this.$options.i18n.addVariable;
@@ -218,9 +294,7 @@ export default {
let property;
if (this.isValueMaskable) {
- const supportedChars = this.maskedRegexToUse.replace('^', '').replace(/{(\d,)}\$/, '');
- const regex = new RegExp(supportedChars, 'g');
- property = this.variable.value.replace(regex, '');
+ property = this.variable.value.replace(this.maskedSupportedCharsRegEx, '');
} else if (this.hasVariableReference) {
property = '$';
}
@@ -382,7 +456,7 @@ export default {
label-for="ci-variable-value"
class="gl-border-none gl-mb-n2"
data-testid="ci-variable-value-label"
- :invalid-feedback="maskedReqsNotMetText"
+ :invalid-feedback="maskedValidationIssuesText"
:state="isValueValid"
>
<gl-form-textarea
diff --git a/app/assets/javascripts/ci/ci_variable_list/constants.js b/app/assets/javascripts/ci/ci_variable_list/constants.js
index d85827b8220..4ec7333f465 100644
--- a/app/assets/javascripts/ci/ci_variable_list/constants.js
+++ b/app/assets/javascripts/ci/ci_variable_list/constants.js
@@ -2,6 +2,10 @@ import { __, s__, sprintf } from '~/locale';
export const ENVIRONMENT_QUERY_LIMIT = 30;
+export const MASKED_VALUE_MIN_LENGTH = 8;
+
+export const WHITESPACE_REG_EX = /\s/;
+
export const SORT_DIRECTIONS = {
ASC: 'KEY_ASC',
DESC: 'KEY_DESC',
diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml
index ca1fef6eb32..ee1516facbc 100644
--- a/app/views/projects/_new_project_fields.html.haml
+++ b/app/views/projects/_new_project_fields.html.haml
@@ -25,7 +25,7 @@
input_name: 'project[namespace_id]',
root_url: root_url,
track_label: track_label,
- user_namespace_id: current_user.namespace.id } }
+ user_namespace_id: current_user.namespace_id } }
- else
.input-group-prepend.static-namespace.flex-shrink-0.has-tooltip{ title: user_url(current_user.username) + '/' }
.input-group-text.border-0
diff --git a/config/feature_flags/development/import_fallback_to_db_empty_cache.yml b/config/feature_flags/development/use_500_page_size_for_contribution_analytics.yml
index d97adc841fc..a52dacbe229 100644
--- a/config/feature_flags/development/import_fallback_to_db_empty_cache.yml
+++ b/config/feature_flags/development/use_500_page_size_for_contribution_analytics.yml
@@ -1,8 +1,8 @@
---
-name: import_fallback_to_db_empty_cache
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/133914
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/428700
-milestone: '16.6'
+name: use_500_page_size_for_contribution_analytics
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136724
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/431595
+milestone: '16.7'
type: development
-group: group::import and integrate
+group: group::optimize
default_enabled: false
diff --git a/doc/development/database/not_null_constraints.md b/doc/development/database/not_null_constraints.md
index b6e8786b9f3..7ffc1fba1a0 100644
--- a/doc/development/database/not_null_constraints.md
+++ b/doc/development/database/not_null_constraints.md
@@ -204,11 +204,118 @@ If you have to clean up a nullable column for a [high-traffic table](../migratio
it needs an additional [batched background migration cleaning up](batched_background_migrations.md#cleaning-up-a-batched-background-migration)
in the release after adding the data migration.
-In that rare case you need 3 releases end-to-end:
-
-1. Release `N.M` - Add the `NOT NULL` constraint and the background-migration to fix the existing records.
-1. Release `N.M+1` - Cleanup the background migration.
-1. Release `N.M+2` - Validate the `NOT NULL` constraint.
+In this case the number of releases depends on the amount of time needed to migrate existing records. The cleanup is
+scheduled after the background migration has completed, which could be several releases after the constraint was added.
+
+1. Release `N.M`:
+ - Add the `NOT NULL` constraint without validating it:
+
+ ```ruby
+ # db/post_migrate/
+ class AddMergeRequestDiffsProjectIdNotNullConstraint < Gitlab::Database::Migration[2.2]
+ disable_ddl_transaction!
+ milestone '16.7'
+
+ def up
+ add_not_null_constraint :merge_request_diffs, :project_id, validate: false
+ end
+
+ def down
+ remove_not_null_constraint :merge_request_diffs, :project_id
+ end
+ end
+ ```
+
+ - Add the background-migration to fix the existing records:
+
+ ```ruby
+ # db/post_migrate/
+ class QueueBackfillMergeRequestDiffsProjectId < Gitlab::Database::Migration[2.2]
+ milestone '16.7'
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ MIGRATION = 'BackfillMergeRequestDiffsProjectId'
+ DELAY_INTERVAL = 2.minutes
+
+ def up
+ queue_batched_background_migration(
+ MIGRATION,
+ :merge_request_diffs,
+ :id,
+ job_interval: DELAY_INTERVAL,
+ queued_migration_version: '20231114043522'
+ )
+ end
+
+ def down
+ delete_batched_background_migration(MIGRATION, :merge_request_diffs, :id, [])
+ end
+ end
+ ```
+
+1. Release `N.M+X`, where `X` is the number of releases the migration was running:
+ - Cleanup the background migration:
+
+ ```ruby
+ # db/post_migrate/
+ class FinalizeMergeRequestDiffsProjectIdBackfill < Gitlab::Database::Migration[2.2]
+ disable_ddl_transaction!
+ milestone '16.10'
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ MIGRATION = 'BackfillMergeRequestDiffsProjectId'
+
+ def up
+ ensure_batched_background_migration_is_finished(
+ job_class_name: MIGRATION,
+ table_name: :merge_request_diffs,
+ column_name: :id,
+ job_arguments: [],
+ finalize: true
+ )
+ end
+
+ def down
+ # no-op
+ end
+ end
+ ```
+
+ - **Optional.** For very large tables, schedule asynchronous validation of the `NOT NULL` constraint:
+
+ ```ruby
+ # db/post_migrate/
+ class PrepareMergeRequestDiffsProjectIdNotNullValidation < Gitlab::Database::Migration[2.2]
+ milestone '16.10'
+
+ CONSTRAINT_NAME = 'check_11c5f029ad'
+
+ def up
+ prepare_async_check_constraint_validation :merge_request_diffs, name: CONSTRAINT_NAME
+ end
+
+ def down
+ unprepare_async_check_constraint_validation :merge_request_diffs, name: CONSTRAINT_NAME
+ end
+ end
+ ```
+
+1. Validate the `NOT NULL` constraint (if the constraint was validated asynchronously, wait for this validation to finish):
+
+ ```ruby
+ # db/post_migrate/
+ class ValidateMergeRequestDiffsProjectIdNullConstraint < Gitlab::Database::Migration[2.2]
+ milestone '16.10'
+
+ def up
+ validate_not_null_constraint :merge_request_diffs, :project_id
+ end
+
+ def down
+ # no-op
+ end
+ end
+ ```
For these cases, consult the database team early in the update cycle. The `NOT NULL`
constraint may not be required or other options could exist that do not affect really large
diff --git a/doc/development/i18n/externalization.md b/doc/development/i18n/externalization.md
index 0e67ee0eea8..6b2beb17880 100644
--- a/doc/development/i18n/externalization.md
+++ b/doc/development/i18n/externalization.md
@@ -258,6 +258,11 @@ expect(wrapper.text()).toBe('There was an error: Please refresh and hope for the
For more details you can see how we [keep translations dynamic](#keep-translations-dynamic).
+## Making changes to translated strings
+
+If you change the source strings in GitLab, you must [update the `pot` file](#updating-the-po-files-with-the-new-content) before pushing your changes.
+If the `pot` file is out of date, pre-push checks and a pipeline job for `gettext` fail.
+
## Working with special content
### Interpolation
diff --git a/doc/development/i18n/index.md b/doc/development/i18n/index.md
index 0fb4412a98a..9e39d5554ab 100644
--- a/doc/development/i18n/index.md
+++ b/doc/development/i18n/index.md
@@ -29,6 +29,10 @@ strings.
See [Externalization for GitLab](externalization.md).
+### Editing externalized strings
+
+If you edit externalized strings in GitLab, you must [update the `pot` file](externalization.md#updating-the-po-files-with-the-new-content) before pushing your changes.
+
## Translate strings
The translation process is managed at [https://crowdin.com/project/gitlab-ee](https://crowdin.com/project/gitlab-ee)
diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md
index 25fa1f5cbaf..cf8d9637d73 100644
--- a/doc/user/application_security/index.md
+++ b/doc/user/application_security/index.md
@@ -270,7 +270,9 @@ In the Free tier, the reports above aren't parsed by GitLab. As a result, the wi
A merge request contains a security widget which displays a summary of the _new_ results. New results are determined by comparing the findings of the merge request against the findings of the most recent completed pipeline (`success`, `failed`, `canceled` or `skipped`) for the commit when the feature branch was created from the target branch.
-GitLab checks the last 10 pipelines for the commit when the feature was created from the target branch to find one with security reports to use in comparison logic. If security scans have not run for the last 10 completed pipelines in the target branch when the feature branch was created, there is no base for comparison. The vulnerabilities from the merge request findings are listed as new in the merge request security widget. We recommend you run a scan of the `default` (target) branch before enabling feature branch scans for your developers.
+GitLab checks the last 10 pipelines for the commit when the feature branch was created from the target branch to find one with security reports to use in comparison logic. If security scans have not run for the last 10 completed pipelines in the target branch when the feature branch was created, there is no base for comparison. The vulnerabilities from the merge request findings are listed as _new_ in the merge request security widget. We recommend you run a scan of the `default` (target) branch before enabling feature branch scans for your developers.
+
+The MR security widget considers all supported pipeline sources (based on the [`CI_PIPELINE_SOURCE` variable](../../ci/variables/predefined_variables.md)) when comparing results from both the source and target branches when determining if a merge request requires approval. Pipeline sources `webide` and `parent_pipeline` are not supported.
The merge request security widget displays only a subset of the vulnerabilities in the generated JSON artifact because it contains both new and existing findings.
diff --git a/doc/user/application_security/policies/scan-execution-policies.md b/doc/user/application_security/policies/scan-execution-policies.md
index f6ef8a2c49e..f03b0633096 100644
--- a/doc/user/application_security/policies/scan-execution-policies.md
+++ b/doc/user/application_security/policies/scan-execution-policies.md
@@ -224,10 +224,9 @@ Note the following:
policy project's repository must have access to the scanner and site profiles. Otherwise, the scan
is not scheduled successfully.
- For a secret detection scan, only rules with the default ruleset are supported. [Custom rulesets](../secret_detection/index.md#custom-rulesets)
- are not supported.
-- A secret detection scan runs in `default` mode when executed as part of a pipeline, and in
- [`historic`](../secret_detection/index.md#full-history-secret-detection)
- mode when executed as part of a scheduled scan.
+ are not supported. Alternatively, you may configure a [remote configuration file](../secret_detection/index.md#specify-a-remote-configuration-file) and set the `SECRET_DETECTION_RULESET_GIT_REFERENCE` variable.
+- By default, for `scheduled` scan execution policies, secret detection scans configured without any CI variables defined run first in `historic` mode (`SECRET_DETECTION_HISTORIC_SCAN` = `true`). All subsequent scheduled scans run in default mode with `SECRET_DETECTION_LOG_OPTIONS` set to the commit range between last run and current SHA. CI variables provided in the scan execution policy can override this behavior. Learn more about [historic mode](../secret_detection/index.md#full-history-secret-detection).
+- For `triggered` scan execution policies, secret detection works just like regular scan [configured manually in the `.gitlab-ci.yml`](../secret_detection/index.md#edit-the-gitlab-ciyml-file-manually).
- A container scanning scan that is configured for the `pipeline` rule type ignores the agent defined in the `agents` object. The `agents` object is only considered for `schedule` rule types.
An agent with a name provided in the `agents` object must be created and configured for the project.
- Variables defined in a Scan Execution Policy follow the standard [CI/CD variable precedence](../../../ci/variables/index.md#cicd-variable-precedence).
diff --git a/lib/gitlab/github_import/issuable_finder.rb b/lib/gitlab/github_import/issuable_finder.rb
index 0780ba6119f..5ce50e5b4e7 100644
--- a/lib/gitlab/github_import/issuable_finder.rb
+++ b/lib/gitlab/github_import/issuable_finder.rb
@@ -26,8 +26,6 @@ module Gitlab
def database_id
val = Gitlab::Cache::Import::Caching.read_integer(cache_key, timeout: timeout)
- return val if Feature.disabled?(:import_fallback_to_db_empty_cache, project)
-
return if val == CACHE_OBJECT_NOT_FOUND
return val if val.present?
diff --git a/lib/gitlab/github_import/label_finder.rb b/lib/gitlab/github_import/label_finder.rb
index d0bbd2bc7cf..87d3195eb93 100644
--- a/lib/gitlab/github_import/label_finder.rb
+++ b/lib/gitlab/github_import/label_finder.rb
@@ -19,8 +19,6 @@ module Gitlab
cache_key = cache_key_for(name)
val = Gitlab::Cache::Import::Caching.read_integer(cache_key)
- return val if Feature.disabled?(:import_fallback_to_db_empty_cache, project)
-
return if val == CACHE_OBJECT_NOT_FOUND
return val if val.present?
diff --git a/lib/gitlab/github_import/milestone_finder.rb b/lib/gitlab/github_import/milestone_finder.rb
index dcb679fda6d..fd60fa86e82 100644
--- a/lib/gitlab/github_import/milestone_finder.rb
+++ b/lib/gitlab/github_import/milestone_finder.rb
@@ -24,8 +24,6 @@ module Gitlab
val = Gitlab::Cache::Import::Caching.read_integer(cache_key)
- return val if Feature.disabled?(:import_fallback_to_db_empty_cache, project)
-
return if val == CACHE_OBJECT_NOT_FOUND
return val if val.present?
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 48159e6a13b..e39cf624d93 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -10561,13 +10561,22 @@ msgstr ""
msgid "CiVariables|State"
msgstr ""
+msgid "CiVariables|The value must have at least %{charsAmount} characters."
+msgstr ""
+
msgid "CiVariables|There was an error fetching the inherited CI variables."
msgstr ""
msgid "CiVariables|This %{entity} has %{currentVariableCount} defined CI/CD variables. The maximum number of variables per %{entity} is %{maxVariableLimit}. To add new variables, you must reduce the number of defined variables."
msgstr ""
-msgid "CiVariables|This variable value does not meet the masking requirements."
+msgid "CiVariables|This value cannot be masked because it contains the following characters: %{unsupportedChars} and whitespace characters."
+msgstr ""
+
+msgid "CiVariables|This value cannot be masked because it contains the following characters: %{unsupportedChars}."
+msgstr ""
+
+msgid "CiVariables|This value cannot be masked because it contains the following characters: whitespace characters."
msgstr ""
msgid "CiVariables|Type"
diff --git a/package.json b/package.json
index e479d528aca..ec0af3047d7 100644
--- a/package.json
+++ b/package.json
@@ -61,7 +61,7 @@
"@gitlab/svgs": "3.71.0",
"@gitlab/ui": "^68.5.0",
"@gitlab/visual-review-tools": "1.7.3",
- "@gitlab/web-ide": "0.0.1-dev-20231004090414",
+ "@gitlab/web-ide": "^0.0.1-dev-20231116214726",
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
"@rails/actioncable": "7.0.8",
"@rails/ujs": "7.0.8",
diff --git a/spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js
index 610aae3946f..113e946908a 100644
--- a/spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js
+++ b/spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js
@@ -20,6 +20,8 @@ describe('CI Variable Drawer', () => {
let wrapper;
let trackingSpy;
+ const itif = (condition) => (condition ? it : it.skip);
+
const mockProjectVariable = mockVariablesWithScopes(projectString)[0];
const mockProjectVariableFileType = mockVariablesWithScopes(projectString)[1];
const mockEnvScope = 'staging';
@@ -284,52 +286,106 @@ describe('CI Variable Drawer', () => {
expect(findConfirmBtn().attributes('disabled')).toBeUndefined();
});
- describe.each`
- value | canSubmit | trackingErrorProperty
- ${'secretValue'} | ${true} | ${null}
- ${'~v@lid:symbols.'} | ${true} | ${null}
- ${'short'} | ${false} | ${null}
- ${'multiline\nvalue'} | ${false} | ${'\n'}
- ${'dollar$ign'} | ${false} | ${'$'}
- ${'unsupported|char'} | ${false} | ${'|'}
- `('masking requirements', ({ value, canSubmit, trackingErrorProperty }) => {
- beforeEach(async () => {
- createComponent();
-
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- await findKeyField().vm.$emit('input', 'NEW_VARIABLE');
- await findValueField().vm.$emit('input', value);
- await findMaskedCheckbox().vm.$emit('input', true);
- });
+ const invalidValues = {
+ short: 'short',
+ multiLine: 'multiline\nvalue',
+ unsupportedChar: 'unsupported|char',
+ twoUnsupportedChars: 'unsupported|chars!',
+ threeUnsupportedChars: '%unsupported|chars!',
+ shortAndMultiLine: 'sho\nrt',
+ shortAndUnsupportedChar: 'short!',
+ shortAndMultiLineAndUnsupportedChar: 'short\n!',
+ multiLineAndUnsupportedChar: 'multiline\nvalue!',
+ };
+ const maskedValidationIssuesText = {
+ short: 'The value must have at least 8 characters.',
+ multiLine:
+ 'This value cannot be masked because it contains the following characters: whitespace characters.',
+ unsupportedChar:
+ 'This value cannot be masked because it contains the following characters: |.',
+ unsupportedDollarChar:
+ 'This value cannot be masked because it contains the following characters: $.',
+ twoUnsupportedChars:
+ 'This value cannot be masked because it contains the following characters: |, !.',
+ threeUnsupportedChars:
+ 'This value cannot be masked because it contains the following characters: %, |, !.',
+ shortAndMultiLine:
+ 'This value cannot be masked because it contains the following characters: whitespace characters. The value must have at least 8 characters.',
+ shortAndUnsupportedChar:
+ 'This value cannot be masked because it contains the following characters: !. The value must have at least 8 characters.',
+ shortAndMultiLineAndUnsupportedChar:
+ 'This value cannot be masked because it contains the following characters: ! and whitespace characters. The value must have at least 8 characters.',
+ multiLineAndUnsupportedChar:
+ 'This value cannot be masked because it contains the following characters: ! and whitespace characters.',
+ };
- it(`${
- canSubmit ? 'can submit' : 'shows validation errors and disables submit button'
- } when value is '${value}'`, () => {
- if (canSubmit) {
+ describe.each`
+ value | canSubmit | trackingErrorProperty | validationIssueKey
+ ${'secretValue'} | ${true} | ${null} | ${''}
+ ${'~v@lid:symbols.'} | ${true} | ${null} | ${''}
+ ${invalidValues.short} | ${false} | ${null} | ${'short'}
+ ${invalidValues.multiLine} | ${false} | ${'\n'} | ${'multiLine'}
+ ${'dollar$ign'} | ${false} | ${'$'} | ${'unsupportedDollarChar'}
+ ${invalidValues.unsupportedChar} | ${false} | ${'|'} | ${'unsupportedChar'}
+ ${invalidValues.twoUnsupportedChars} | ${false} | ${'|!'} | ${'twoUnsupportedChars'}
+ ${invalidValues.threeUnsupportedChars} | ${false} | ${'%|!'} | ${'threeUnsupportedChars'}
+ ${invalidValues.shortAndMultiLine} | ${false} | ${'\n'} | ${'shortAndMultiLine'}
+ ${invalidValues.shortAndUnsupportedChar} | ${false} | ${'!'} | ${'shortAndUnsupportedChar'}
+ ${invalidValues.shortAndMultiLineAndUnsupportedChar} | ${false} | ${'\n!'} | ${'shortAndMultiLineAndUnsupportedChar'}
+ ${invalidValues.multiLineAndUnsupportedChar} | ${false} | ${'\n!'} | ${'multiLineAndUnsupportedChar'}
+ `(
+ 'masking requirements',
+ ({ value, canSubmit, trackingErrorProperty, validationIssueKey }) => {
+ beforeEach(() => {
+ createComponent();
+
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ findKeyField().vm.$emit('input', 'NEW_VARIABLE');
+ findValueField().vm.$emit('input', value);
+ findMaskedCheckbox().vm.$emit('input', true);
+ });
+
+ itif(canSubmit)(`can submit when value is ${value}`, () => {
+ /* eslint-disable jest/no-standalone-expect */
expect(findValueLabel().attributes('invalid-feedback')).toBe('');
expect(findConfirmBtn().attributes('disabled')).toBeUndefined();
- } else {
- expect(findValueLabel().attributes('invalid-feedback')).toBe(
- 'This variable value does not meet the masking requirements.',
- );
- expect(findConfirmBtn().attributes('disabled')).toBeDefined();
- }
- });
+ /* eslint-enable jest/no-standalone-expect */
+ });
+
+ itif(!canSubmit)(
+ `shows validation errors and disables submit button when value is ${value}`,
+ () => {
+ const validationIssueText = maskedValidationIssuesText[validationIssueKey] || '';
+
+ /* eslint-disable jest/no-standalone-expect */
+ expect(findValueLabel().attributes('invalid-feedback')).toBe(validationIssueText);
+ expect(findConfirmBtn().attributes('disabled')).toBeDefined();
+ /* eslint-enable jest/no-standalone-expect */
+ },
+ );
+
+ itif(trackingErrorProperty)(
+ `sends the correct variable validation tracking event when value is ${value}`,
+ () => {
+ /* eslint-disable jest/no-standalone-expect */
+ expect(trackingSpy).toHaveBeenCalledTimes(1);
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTION, {
+ label: DRAWER_EVENT_LABEL,
+ property: trackingErrorProperty,
+ });
+ /* eslint-enable jest/no-standalone-expect */
+ },
+ );
- it(`${
- trackingErrorProperty ? 'sends the correct' : 'does not send the'
- } variable validation tracking event when value is '${value}'`, () => {
- const trackingEventSent = trackingErrorProperty ? 1 : 0;
- expect(trackingSpy).toHaveBeenCalledTimes(trackingEventSent);
-
- if (trackingErrorProperty) {
- expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTION, {
- label: DRAWER_EVENT_LABEL,
- property: trackingErrorProperty,
- });
- }
- });
- });
+ itif(!trackingErrorProperty)(
+ `does not send the the correct variable validation tracking event when value is ${value}`,
+ () => {
+ // eslint-disable-next-line jest/no-standalone-expect
+ expect(trackingSpy).toHaveBeenCalledTimes(0);
+ },
+ );
+ },
+ );
it('only sends the tracking event once', async () => {
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
diff --git a/spec/lib/gitlab/github_import/issuable_finder_spec.rb b/spec/lib/gitlab/github_import/issuable_finder_spec.rb
index 977fef95d64..3fe07923a50 100644
--- a/spec/lib/gitlab/github_import/issuable_finder_spec.rb
+++ b/spec/lib/gitlab/github_import/issuable_finder_spec.rb
@@ -48,34 +48,6 @@ RSpec.describe Gitlab::GithubImport::IssuableFinder, :clean_gitlab_redis_cache,
expect { finder.database_id }.to raise_error(TypeError)
end
- context 'with FF import_fallback_to_db_empty_cache disabled' do
- before do
- stub_feature_flags(import_fallback_to_db_empty_cache: false)
- end
-
- it 'returns nil if object does not exist' do
- missing_issue = double(:issue, issuable_type: 'MergeRequest', issuable_id: 999)
-
- expect(described_class.new(project, missing_issue).database_id).to be_nil
- end
-
- it 'does not fetch object id from database if not in cache' do
- expect(finder.database_id).to eq(nil)
- end
-
- it 'fetches object id from cache if present' do
- finder.cache_database_id(10)
-
- expect(finder.database_id).to eq(10)
- end
-
- it 'returns -1 if cache is -1' do
- finder.cache_database_id(-1)
-
- expect(finder.database_id).to eq(-1)
- end
- end
-
context 'when group is present' do
context 'when settings single_endpoint_notes_import is enabled' do
let(:single_endpoint_optional_stage) { true }
diff --git a/spec/lib/gitlab/github_import/label_finder_spec.rb b/spec/lib/gitlab/github_import/label_finder_spec.rb
index e46595974d1..4c01e2b65da 100644
--- a/spec/lib/gitlab/github_import/label_finder_spec.rb
+++ b/spec/lib/gitlab/github_import/label_finder_spec.rb
@@ -49,34 +49,6 @@ RSpec.describe Gitlab::GithubImport::LabelFinder, :clean_gitlab_redis_cache, fea
expect(finder.id_for(feature.name)).to eq(feature.id)
end
end
-
- context 'with FF import_fallback_to_db_empty_cache disabled' do
- before do
- stub_feature_flags(import_fallback_to_db_empty_cache: false)
- end
-
- it 'returns nil for a non existing label name' do
- expect(finder.id_for('kittens')).to be_nil
- end
-
- it 'does not fetch object id from database if not in cache' do
- expect(finder.id_for(feature.name)).to be_nil
- end
-
- it 'fetches object id from cache if present' do
- finder.build_cache
-
- expect(finder.id_for(feature.name)).to eq(feature.id)
- end
-
- it 'returns -1 if cache is -1' do
- key = finder.cache_key_for(bug.name)
-
- Gitlab::Cache::Import::Caching.write(key, -1)
-
- expect(finder.id_for(bug.name)).to eq(-1)
- end
- end
end
describe '#build_cache' do
diff --git a/spec/lib/gitlab/github_import/milestone_finder_spec.rb b/spec/lib/gitlab/github_import/milestone_finder_spec.rb
index 62886981de1..91f1c3b8cb9 100644
--- a/spec/lib/gitlab/github_import/milestone_finder_spec.rb
+++ b/spec/lib/gitlab/github_import/milestone_finder_spec.rb
@@ -57,36 +57,6 @@ RSpec.describe Gitlab::GithubImport::MilestoneFinder, :clean_gitlab_redis_cache,
expect(finder.id_for(issuable)).to eq(milestone.id)
end
end
-
- context 'with FF import_fallback_to_db_empty_cache disabled' do
- before do
- stub_feature_flags(import_fallback_to_db_empty_cache: false)
- end
-
- it 'returns nil if object does not exist' do
- missing_issuable = double(:issuable, milestone_number: 999)
-
- expect(finder.id_for(missing_issuable)).to be_nil
- end
-
- it 'does not fetch object id from database if not in cache' do
- expect(finder.id_for(issuable)).to be_nil
- end
-
- it 'fetches object id from cache if present' do
- finder.build_cache
-
- expect(finder.id_for(issuable)).to eq(milestone.id)
- end
-
- it 'returns -1 if cache is -1' do
- key = finder.cache_key_for(milestone.iid)
-
- Gitlab::Cache::Import::Caching.write(key, -1)
-
- expect(finder.id_for(issuable)).to eq(-1)
- end
- end
end
describe '#build_cache' do
diff --git a/spec/support/shared_examples/features/variable_list_drawer_shared_examples.rb b/spec/support/shared_examples/features/variable_list_drawer_shared_examples.rb
index b438a23aafd..04e73cfeee7 100644
--- a/spec/support/shared_examples/features/variable_list_drawer_shared_examples.rb
+++ b/spec/support/shared_examples/features/variable_list_drawer_shared_examples.rb
@@ -140,7 +140,10 @@ RSpec.shared_examples 'variable list drawer' do
toggle_masked
fill_variable('EMPTY_MASK_KEY', '???')
- expect(page).to have_content('This variable value does not meet the masking requirements.')
+ # rubocop:disable Layout/LineLength -- error message is too long
+ expect(page).to have_content('This value cannot be masked because it contains the following characters: ?. The value must have at least 8 characters.')
+ # rubocop:enable Layout/LineLength
+
page.within('[data-testid="ci-variable-drawer"]') do
expect(find_button('Add variable', disabled: true)).to be_present
end
diff --git a/yarn.lock b/yarn.lock
index ba3dd5db54b..298d39fc322 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1292,10 +1292,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/visual-review-tools/-/visual-review-tools-1.7.3.tgz#9ea641146436da388ffbad25d7f2abe0df52c235"
integrity sha512-NMV++7Ew1FSBDN1xiZaauU9tfeSfgDHcOLpn+8bGpP+O5orUPm2Eu66R5eC5gkjBPaXosNAxNWtriee+aFk4+g==
-"@gitlab/web-ide@0.0.1-dev-20231004090414":
- version "0.0.1-dev-20231004090414"
- resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20231004090414.tgz#925c9e942917acbbb17c4ea77b0841649ae186ad"
- integrity sha512-NfkRzvz5Z3uDceQC5QN8W5u1ETlHbbEXjrnKxiYM4TD1d+bOV567GYglRKjyOhQMTYRGpqbisLSAPgvShAQB6g==
+"@gitlab/web-ide@^0.0.1-dev-20231116214726":
+ version "0.0.1-dev-20231116214726"
+ resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20231116214726.tgz#3b80e3de4dce8d07e6acd913d287de0c772b4bb3"
+ integrity sha512-sc/LVSKqcSyKQ5rQlgMfvAMxiJvOkhAI5ZJqA8pHqnKK/sAx7C66lBtYfkWGvj+Dw31ElkZgV/7tS2wQXLnwjA==
"@graphql-eslint/eslint-plugin@3.20.1":
version "3.20.1"