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 }}
-
-
-
+
+
+
+
+ {{ content }}
+
+
+
+
+
+ {{ content }}
+
+
+
+
{{ $options.i18n.visibility }}
+
+
+
+
+
+ {{ content }}
+
+
+
+
{{ $options.i18n.visibleField }}
- {{ $options.i18n.visibleDescription }}
+ {{ $options.i18n.visibleDescription }}
{{ $options.i18n.maskedField }}
- {{ $options.i18n.maskedDescription }}
+ {{ $options.i18n.maskedDescription }}
-
- {{ $options.i18n.flags }}
-
-
-
-
+ {{ $options.i18n.flags }}
@@ -619,12 +651,13 @@ export default {
-
+ {{ content }}
+ {{ content }}
+
@@ -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', () => {