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 4aae563d9ff3ed2487d2c8b0ba93f82092098ff1..f5cece910bbe4fa11ea316aae688cd9dc89ca9a0 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 @@ -16,6 +16,7 @@ import { GlSprintf, GlFormRadio, GlFormRadioGroup, + GlPopover, } from '@gitlab/ui'; import { __, s__, sprintf } from '~/locale'; import { DRAWER_Z_INDEX } from '~/lib/utils/constants'; @@ -29,10 +30,8 @@ import { ADD_VARIABLE_ACTION, DRAWER_EVENT_LABEL, EDIT_VARIABLE_ACTION, - ENVIRONMENT_SCOPE_LINK_TITLE, EVENT_ACTION, EXPANDED_VARIABLES_NOTE, - FLAG_LINK_TITLE, MASKED_VALUE_MIN_LENGTH, VARIABLE_ACTIONS, VISIBILITY_HIDDEN, @@ -57,12 +56,10 @@ export const i18n = { editVariable: s__('CiVariables|Edit variable'), saveVariable: __('Save changes'), environments: __('Environments'), - environmentScopeLinkTitle: ENVIRONMENT_SCOPE_LINK_TITLE, expandedField: s__('CiVariables|Expand variable reference'), expandedDescription: EXPANDED_VARIABLES_NOTE, flags: __('Flags'), visibility: __('Visibility'), - flagsLinkTitle: FLAG_LINK_TITLE, key: __('Key'), keyFeedback: s__("CiVariables|A variable key can only contain letters, numbers, and '_'."), keyHelpText: s__( @@ -103,6 +100,15 @@ export const i18n = { whitespaceCharsValidationText: s__( 'CiVariables|This value cannot be masked because it contains the following characters: whitespace characters.', ), + environmentsLabelHelpText: s__( + 'CiVariables|You can use a specific environment name like %{codeStart}production%{codeEnd}, or include a wildcard (%{codeStart}*%{codeEnd}) to match multiple environments, like %{codeStart}review*%{codeEnd}.', + ), + environmentsLabelLinkText: s__( + 'CiVariables|Learn how to %{linkStart}restrict CI/CD variables to specific environments%{linkEnd} for better security.', + ), + visibilityLabelHelpText: s__( + "CiVariables|Set the visibility level for the variable's value. The %{linkStart}Masked and hidden%{linkEnd} option is only available for new variables. You cannot update an existing variable to be hidden.", + ), type: __('Type'), value: __('Value'), }; @@ -127,13 +133,14 @@ export default { GlSprintf, GlFormRadio, GlFormRadioGroup, + GlPopover, HelpIcon, }, directives: { GlModalDirective, }, mixins: [trackingMixin], - inject: ['environmentScopeLink', 'isProtectedByDefault', 'maskableRawRegex', 'maskableRegex'], + inject: ['isProtectedByDefault', 'maskableRawRegex', 'maskableRegex'], props: { areEnvironmentsLoading: { type: Boolean, @@ -413,12 +420,19 @@ export default { }, }, awsTokenList, - flagLink: helpPagePath('ci/variables/index', { - anchor: 'define-a-cicd-variable-in-the-ui', - }), variablesPrecedenceLink: helpPagePath('ci/variables/index', { anchor: 'cicd-variable-precedence', }), + environmentsLabelHelpLink: helpPagePath('ci/environments/index', { + anchor: 'limit-the-environment-scope-of-a-cicd-variable', + }), + visibilityLabelHelpLink: helpPagePath('ci/variables/index', { + anchor: 'hide-a-cicd-variable', + }), + environmentsPopoverContainerId: 'environments-popover-container', + environmentsPopoverTargetId: 'environments-popover-target', + visibilityPopoverContainerId: 'visibility-popover-container', + visibilityPopoverTargetId: 'visibility-popover-target', i18n, variableOptions, deleteModal: { @@ -489,15 +503,28 @@ export default { {{ $options.i18n.environments }} - - - + + + + + +

+ + + +
+
{{ $options.i18n.visibility }} + + + + + + + +
{{ $options.i18n.visibleField }} - + {{ $options.i18n.maskedField }} - + @@ -619,12 +651,13 @@ export default {

-

@@ -682,8 +715,9 @@ export default { variant="danger" category="secondary" data-testid="ci-variable-delete-button" - >{{ $options.i18n.deleteVariable }} + {{ $options.i18n.deleteVariable }} + {{ $options.i18n.cancel }} diff --git a/app/assets/javascripts/ci/ci_variable_list/constants.js b/app/assets/javascripts/ci/ci_variable_list/constants.js index b89bb238e8d306b8f2b0d6760b425850e5aef074..21faa7e6115788f739c265107db9dad8fbf72760 100644 --- a/app/assets/javascripts/ci/ci_variable_list/constants.js +++ b/app/assets/javascripts/ci/ci_variable_list/constants.js @@ -68,11 +68,9 @@ export const CONTAINS_VARIABLE_REFERENCE_MESSAGE = __( export const DEFAULT_EXCEEDS_VARIABLE_LIMIT_TEXT = s__( 'CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables.', ); -export const ENVIRONMENT_SCOPE_LINK_TITLE = __('Learn more'); export const EXCEEDS_VARIABLE_LIMIT_TEXT = s__( '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.', ); -export const FLAG_LINK_TITLE = s__('CiVariable|Define a CI/CD variable in the UI'); export const MAXIMUM_VARIABLE_LIMIT_REACHED = s__( 'CiVariables|Maximum number of variables reached.', ); diff --git a/app/assets/javascripts/ci/ci_variable_list/index.js b/app/assets/javascripts/ci/ci_variable_list/index.js index 9342f57f2d87a21956605fbba36891cbdbbfd15f..ad3e81423c982489df8e1e9b109580320b91aad3 100644 --- a/app/assets/javascripts/ci/ci_variable_list/index.js +++ b/app/assets/javascripts/ci/ci_variable_list/index.js @@ -12,7 +12,6 @@ const mountCiVariableListApp = (containerEl) => { const { containsVariableReferenceLink, endpoint, - environmentScopeLink, groupId, groupPath, isGroup, @@ -55,7 +54,6 @@ const mountCiVariableListApp = (containerEl) => { provide: { containsVariableReferenceLink, endpoint, - environmentScopeLink, groupId, groupPath, isGroup: parsedIsGroup, diff --git a/app/views/ci/variables/_attributes.html.haml b/app/views/ci/variables/_attributes.html.haml index f83f84b985ae331f7ddc503620e4365526eeae69..582349e749d4cbb6473eb93a669360a3d53f72bc 100644 --- a/app/views/ci/variables/_attributes.html.haml +++ b/app/views/ci/variables/_attributes.html.haml @@ -1,25 +1,3 @@ %p = s_('CiVariables|Variables can be accidentally exposed in a job log, or maliciously sent to a third party server. The masked variable feature can help reduce the risk of accidentally exposing variable values, but is not a guaranteed method to prevent malicious users from accessing variables.') = link_to _('How can I make my variables more secure?'), help_page_path('ci/variables/index.md', anchor: 'cicd-variable-security'), target: '_blank', rel: 'noopener noreferrer' -%p - = s_('CiVariables|Variables can have several attributes.') - = link_to _('Learn more.'), help_page_path('ci/variables/index.md', anchor: 'define-a-cicd-variable-in-the-ui'), target: '_blank', rel: 'noopener noreferrer' -- if @group.present? || @project.present? - %ul - %li - = safe_format(s_('CiVariables|%{strong_start}Visibility:%{strong_end} Set the visibility level for the value. Can be visible, masked, or masked and hidden.'), tag_pair(tag.strong, :strong_start, :strong_end)) - %li - = safe_format(s_('CiVariables|%{strong_start}Flags%{strong_end}'), tag_pair(tag.strong, :strong_start, :strong_end)) - %ul - %li - = safe_format(s_('CiVariables|%{code_open}Protected:%{code_close} Only exposed to protected branches or protected tags.'), tag_pair(tag.code, :code_open, :code_close)) - %li - = safe_format(s_('CiVariables|%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable.'), tag_pair(tag.code, :code_open, :code_close)) -- else - %ul - %li - = safe_format(s_('CiVariables|%{code_open}Protected:%{code_close} Only exposed to protected branches or protected tags.'), tag_pair(tag.code, :code_open, :code_close)) - %li - = safe_format(s_('CiVariables|%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements.'), tag_pair(tag.code, :code_open, :code_close)) - %li - = safe_format(s_('CiVariables|%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable.'), tag_pair(tag.code, :code_open, :code_close)) diff --git a/app/views/ci/variables/_index.html.haml b/app/views/ci/variables/_index.html.haml index 8cb06fa7c3725d18de83bfefee8d43cd0943113b..7bc9f714f291b820d76b0f246bf2af73e48e1fce 100644 --- a/app/views/ci/variables/_index.html.haml +++ b/app/views/ci/variables/_index.html.haml @@ -15,8 +15,7 @@ maskable_regex: ci_variable_maskable_regex, protected_by_default: ci_variable_protected_by_default?.to_s, contains_variable_reference_link: help_page_path('ci/variables/index.md', anchor: 'prevent-cicd-variable-expansion'), - masked_environment_variables_link: help_page_path('ci/variables/index.md', anchor: 'mask-a-cicd-variable'), - environment_scope_link: help_page_path('ci/environments/index.md', anchor: 'limit-the-environment-scope-of-a-cicd-variable') } } + masked_environment_variables_link: help_page_path('ci/variables/index.md', anchor: 'mask-a-cicd-variable') } } - if !@group && @project.group = render 'ci/group_variables/header' diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 9cce41428badb564392ebb21e246b5665e5c8f32..a66a1224a626fc53c637ac189bb1d3b825669026 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -12196,21 +12196,6 @@ msgid_plural "CiVariables|%d values found" msgstr[0] "" msgstr[1] "" -msgid "CiVariables|%{code_open}Expanded:%{code_close} Variables with %{code_open}$%{code_close} will be treated as the start of a reference to another variable." -msgstr "" - -msgid "CiVariables|%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking requirements." -msgstr "" - -msgid "CiVariables|%{code_open}Protected:%{code_close} Only exposed to protected branches or protected tags." -msgstr "" - -msgid "CiVariables|%{strong_start}Flags%{strong_end}" -msgstr "" - -msgid "CiVariables|%{strong_start}Visibility:%{strong_end} Set the visibility level for the value. Can be visible, masked, or masked and hidden." -msgstr "" - msgid "CiVariables|A variable key can only contain letters, numbers, and '_'." msgstr "" @@ -12265,6 +12250,9 @@ msgstr "" msgid "CiVariables|Key" msgstr "" +msgid "CiVariables|Learn how to %{linkStart}restrict CI/CD variables to specific environments%{linkEnd} for better security." +msgstr "" + msgid "CiVariables|Masked" msgstr "" @@ -12310,6 +12298,9 @@ msgstr "" msgid "CiVariables|Search values" msgstr "" +msgid "CiVariables|Set the visibility level for the variable's value. The %{linkStart}Masked and hidden%{linkEnd} option is only available for new variables. You cannot update an existing variable to be hidden." +msgstr "" + msgid "CiVariables|Specify variable values to be used in this run. The variables specified in the configuration file and %{linkStart}CI/CD settings%{linkEnd} are used by default." msgstr "" @@ -12361,9 +12352,6 @@ msgstr "" msgid "CiVariables|Variables can be accidentally exposed in a job log, or maliciously sent to a third party server. The masked variable feature can help reduce the risk of accidentally exposing variable values, but is not a guaranteed method to prevent malicious users from accessing variables." msgstr "" -msgid "CiVariables|Variables can have several attributes." -msgstr "" - msgid "CiVariables|Variables specified here are %{boldStart}expanded%{boldEnd} and not %{boldStart}masked.%{boldEnd}" msgstr "" @@ -12379,6 +12367,9 @@ msgstr "" msgid "CiVariables|You can use CI/CD variables with the same name in different places, but the variables might overwrite each other. %{linkStart}What is the order of precedence for variables?%{linkEnd}" msgstr "" +msgid "CiVariables|You can use a specific environment name like %{codeStart}production%{codeEnd}, or include a wildcard (%{codeStart}*%{codeEnd}) to match multiple environments, like %{codeStart}review*%{codeEnd}." +msgstr "" + msgid "CiVariables|You have reached the maximum number of variables available. To add new variables, you must reduce the number of defined variables." msgstr "" @@ -12388,9 +12379,6 @@ msgstr "" msgid "CiVariable|Create wildcard" msgstr "" -msgid "CiVariable|Define a CI/CD variable in the UI" -msgstr "" - msgid "CiVariable|Enter a search query to find more environments, or use * to create a wildcard." msgstr "" 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 afe21edbc818b64fbcb1e82c74fdc8be5904a3ef..955158b1fe6b21f8a98f88907b0247845b124793 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 @@ -10,6 +10,7 @@ import { GlSprintf, GlFormRadio, GlFormRadioGroup, + GlPopover, } from '@gitlab/ui'; import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { helpPagePath } from '~/helpers/help_page_helper'; @@ -28,6 +29,7 @@ import { projectString, variableTypes, } from '~/ci/ci_variable_list/constants'; +import HelpIcon from '~/vue_shared/components/help_icon/help_icon.vue'; import { mockTracking } from 'helpers/tracking_helper'; import { mockVariablesWithScopes } from '../mocks'; @@ -93,7 +95,6 @@ describe('CI Variable Drawer', () => { const findDrawer = () => wrapper.findComponent(GlDrawer); const findEnvironmentScopeDropdown = () => wrapper.findComponent(CiEnvironmentsDropdown); const findExpandedCheckbox = () => wrapper.findByTestId('ci-variable-expanded-checkbox'); - const findFlagsDocsLink = () => wrapper.findByTestId('ci-variable-flags-docs-link'); const findKeyField = () => wrapper.findComponent(GlFormCombobox); const findVisibilityRadioButtons = () => wrapper.findAllComponents(GlFormRadio); const findVisibilityRadioGroup = () => wrapper.findComponent(GlFormRadioGroup); @@ -105,6 +106,14 @@ describe('CI Variable Drawer', () => { const findTypeDropdown = () => wrapper.findComponent(GlCollapsibleListbox); const findVariablesPrecedenceDocsLink = () => wrapper.findByTestId('ci-variable-precedence-docs-link'); + const findVisibilityLabelHelpContainer = () => + wrapper.findByTestId('visibility-popover-container'); + const findVisibilityLabelHelpPopover = () => + findVisibilityLabelHelpContainer().findComponent(GlPopover); + const findEnvironmentsLabelHelpContainer = () => + wrapper.findByTestId('environments-popover-container'); + const findEnvironmentsLabelHelpPopover = () => + findEnvironmentsLabelHelpContainer().findComponent(GlPopover); describe('template', () => { beforeEach(() => { @@ -117,15 +126,85 @@ describe('CI Variable Drawer', () => { ); }); - it('renders docs link for flags', () => { - expect(findFlagsDocsLink().attributes('href')).toBe( - helpPagePath('ci/variables/index', { anchor: 'define-a-cicd-variable-in-the-ui' }), - ); - }); - it('value field is resizable', () => { expect(findValueField().props('noResize')).toBe(false); }); + + describe('environments label', () => { + it('has a help icon', () => { + const helpIcon = findEnvironmentsLabelHelpContainer().findComponent(HelpIcon); + + expect(helpIcon.exists()).toBe(true); + }); + + it('has a popover', () => { + const popover = findEnvironmentsLabelHelpPopover(); + + expect(popover.exists()).toBe(true); + expect(popover.props()).toMatchObject({ + target: 'environments-popover-target', + container: 'environments-popover-container', + }); + }); + + describe('popover', () => { + it('renders the correct content', () => { + const popover = findEnvironmentsLabelHelpPopover(); + + expect(popover.text()).toContain( + 'You can use a specific environment name like production, or include a wildcard (*) to match multiple environments, like review*. Learn how to restrict CI/CD variables to specific environments for better security.', + ); + }); + + it('renders the documentation link', () => { + const popover = findEnvironmentsLabelHelpPopover(); + const link = popover.findComponent(GlLink); + const documentationLink = helpPagePath('ci/environments/index', { + anchor: 'limit-the-environment-scope-of-a-cicd-variable', + }); + + expect(link.attributes('href')).toBe(documentationLink); + }); + }); + }); + + describe('visibility label', () => { + it('has a help icon', () => { + const helpIcon = findVisibilityLabelHelpContainer().findComponent(HelpIcon); + + expect(helpIcon.exists()).toBe(true); + }); + + it('has a popover', () => { + const popover = findVisibilityLabelHelpPopover(); + + expect(popover.exists()).toBe(true); + expect(popover.props()).toMatchObject({ + target: 'visibility-popover-target', + container: 'visibility-popover-container', + }); + }); + + describe('popover', () => { + it('renders the correct content', () => { + const popover = findVisibilityLabelHelpPopover(); + + expect(popover.text()).toContain( + "Set the visibility level for the variable's value. The Masked and hidden option is only available for new variables. You cannot update an existing variable to be hidden.", + ); + }); + + it('renders the documentation link', () => { + const popover = findVisibilityLabelHelpPopover(); + const link = popover.findComponent(GlLink); + const documentationLink = helpPagePath('ci/variables/index', { + anchor: 'hide-a-cicd-variable', + }); + + expect(link.attributes('href')).toBe(documentationLink); + }); + }); + }); }); describe('validations', () => {