diff options
-rw-r--r-- | .eslintrc.yml | 5 | ||||
-rw-r--r-- | app/assets/javascripts/ci/ci_variable_list/components/ci_variable_settings.vue | 1 | ||||
-rw-r--r-- | app/assets/javascripts/ci/ci_variable_list/components/ci_variable_table.vue | 397 | ||||
-rw-r--r-- | app/assets/javascripts/repository/index.js | 8 | ||||
-rw-r--r-- | app/assets/stylesheets/framework/new_card.scss | 3 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/settings.scss | 1 | ||||
-rw-r--r-- | app/mailers/emails/merge_requests.rb | 2 | ||||
-rw-r--r-- | app/views/admin/application_settings/ci_cd.html.haml | 2 | ||||
-rw-r--r-- | app/views/ci/variables/_index.html.haml | 6 | ||||
-rw-r--r-- | app/views/projects/_files.html.haml | 2 | ||||
-rw-r--r-- | app/views/projects/tree/_tree_header.html.haml | 4 | ||||
-rw-r--r-- | data/deprecations/16-3-deprecate-twitter-saas.yml | 10 | ||||
-rw-r--r-- | data/deprecations/16-3-deprecate-twitter-sm.yml | 10 | ||||
-rw-r--r-- | doc/development/ai_features.md | 47 | ||||
-rw-r--r-- | doc/integration/twitter.md | 9 | ||||
-rw-r--r-- | doc/update/deprecations.md | 28 | ||||
-rw-r--r-- | locale/gitlab.pot | 12 | ||||
-rw-r--r-- | spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js | 25 |
18 files changed, 374 insertions, 198 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml index c9ae61e75a5..527e94d4101 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -148,11 +148,6 @@ rules: methods: 'sanitize' overrides: - files: - - 'ee/**/*' - # See https://gitlab.com/gitlab-org/gitlab/-/issues/360551 - rules: - vue/multi-word-component-names: off - - files: - '{,ee/,jh/}spec/frontend*/**/*' rules: '@gitlab/require-i18n-strings': off diff --git a/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_settings.vue b/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_settings.vue index 4faec24e19d..f4e1da9b34f 100644 --- a/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_settings.vue +++ b/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_settings.vue @@ -117,6 +117,7 @@ export default { @handle-prev-page="$emit('handle-prev-page')" @handle-next-page="$emit('handle-next-page')" @set-selected-variable="setSelectedVariable" + @delete-variable="deleteVariable" @sort-changed="(val) => $emit('sort-changed', val)" /> <ci-variable-modal diff --git a/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_table.vue b/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_table.vue index ec7a921664f..a14cd1e387a 100644 --- a/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_table.vue +++ b/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_table.vue @@ -3,11 +3,14 @@ import { GlAlert, GlBadge, GlButton, + GlCard, + GlIcon, GlLoadingIcon, GlModalDirective, GlKeysetPagination, GlLink, GlTable, + GlModal, GlTooltipDirective, } from '@gitlab/ui'; import { __, s__, sprintf } from '~/locale'; @@ -45,9 +48,8 @@ export default { }, { key: 'actions', - label: '', - tdClass: 'text-right', - thClass: 'gl-w-5p', + label: __('Actions'), + thClass: 'gl-text-right', }, ], inheritedVarsFields: [ @@ -73,10 +75,13 @@ export default { GlAlert, GlBadge, GlButton, + GlCard, GlKeysetPagination, GlLink, + GlIcon, GlLoadingIcon, GlTable, + GlModal, }, directives: { GlModalDirective, @@ -84,6 +89,14 @@ export default { }, mixins: [glFeatureFlagsMixin()], inject: ['isInheritedGroupVars'], + i18n: { + title: s__('CiVariables|CI/CD Variables'), + addButton: s__('CiVariables|Add variable'), + editButton: __('Edit'), + deleteButton: __('Delete'), + modalDeleteTitle: s__('CiVariables|Delete variable'), + modalDeleteMessage: s__('CiVariables|Do you want to delete the variable %{key}?'), + }, props: { entity: { type: String, @@ -107,6 +120,20 @@ export default { required: true, }, }, + deleteModal: { + actionPrimary: { + text: __('Delete'), + attributes: { + variant: 'danger', + }, + }, + actionSecondary: { + text: __('Cancel'), + attributes: { + variant: 'default', + }, + }, + }, data() { return { areValuesHidden: true, @@ -165,6 +192,9 @@ export default { setSelectedVariable(index = -1) { this.$emit('set-selected-variable', this.variables[index] ?? null); }, + deleteSelectedVariable(index = -1) { + this.$emit('delete-variable', this.variables[index] ?? null); + }, getAttributes(item) { const attributes = []; if (item.variableType === variableTypes.fileType) { @@ -181,188 +211,219 @@ export default { } return attributes; }, + removeVariableMessage(key) { + return sprintf(this.$options.i18n.modalDeleteMessage, { + key, + }); + }, }, maximumVariableLimitReached: MAXIMUM_VARIABLE_LIMIT_REACHED, }; </script> <template> - <div class="ci-variable-table" :data-testid="tableDataTestId"> - <gl-loading-icon v-if="isLoading" /> - <gl-alert - v-if="showAlert" - :dismissible="false" - :title="$options.maximumVariableLimitReached" - variant="info" - > - {{ exceedsVariableLimitText }} - </gl-alert> - <div - v-if="showPagination && !isInheritedGroupVars" - class="ci-variable-actions gl-display-flex gl-justify-content-end gl-my-3" + <div> + <gl-card + class="gl-new-card ci-variable-table" + header-class="gl-new-card-header" + body-class="gl-new-card-body gl-px-0" + :data-testid="tableDataTestId" > - <gl-button v-if="!isTableEmpty" @click="toggleHiddenState">{{ valuesButtonText }}</gl-button> - <gl-button - v-gl-modal-directive="$options.modalId" - class="gl-mx-3" - data-qa-selector="add_ci_variable_button" - variant="confirm" - category="primary" - :aria-label="__('Add')" - :disabled="exceedsVariableLimit" - @click="setSelectedVariable()" - >{{ __('Add variable') }}</gl-button - > - </div> - <gl-table - v-if="!isLoading" - :fields="fields" - :items="variablesWithAttributes" - tbody-tr-class="js-ci-variable-row" - sort-by="key" - sort-direction="asc" - stacked="lg" - fixed - show-empty - sort-icon-left - no-sort-reset - no-local-sorting - @sort-changed="(val) => $emit('sort-changed', val)" - > - <template #table-colgroup="scope"> - <col v-for="field in scope.fields" :key="field.key" :style="field.customStyle" /> - </template> - <template #cell(key)="{ item }"> - <div - class="gl-display-flex gl-align-items-flex-start gl-justify-content-end gl-lg-justify-content-start gl-mr-n3" - > - <span - :id="`ci-variable-key-${item.id}`" - class="gl-display-inline-block gl-max-w-full gl-word-break-word" - >{{ item.key }}</span - > + <template #header> + <div class="gl-new-card-title-wrapper"> + <h5 class="gl-new-card-title">{{ $options.i18n.title }}</h5> + <span class="gl-new-card-count"> + <gl-icon name="code" class="gl-mr-2" /> + {{ variables.length }} + </span> + </div> + <div v-if="!isInheritedGroupVars" class="gl-new-card-actions gl-font-size-0"> <gl-button - v-gl-tooltip + v-if="!isTableEmpty" category="tertiary" - icon="copy-to-clipboard" - class="gl-my-n3 gl-ml-2" - :title="__('Copy key')" - :data-clipboard-text="item.key" - :aria-label="__('Copy to clipboard')" - /> - </div> - </template> - <template v-if="!isInheritedGroupVars" #cell(value)="{ item }"> - <div - class="gl-display-flex gl-align-items-flex-start gl-justify-content-end gl-lg-justify-content-start gl-mr-n3" - > - <span v-if="areValuesHidden" data-testid="hiddenValue">*****</span> - <span - v-else - :id="`ci-variable-value-${item.id}`" - class="gl-display-inline-block gl-max-w-full gl-text-truncate" - data-testid="revealedValue" - >{{ item.value }}</span + size="small" + class="gl-mr-3" + @click="toggleHiddenState" + >{{ valuesButtonText }}</gl-button > <gl-button - v-gl-tooltip - category="tertiary" - icon="copy-to-clipboard" - class="gl-my-n3 gl-ml-2" - :title="__('Copy value')" - :data-clipboard-text="item.value" - :aria-label="__('Copy to clipboard')" - /> + v-gl-modal-directive="$options.modalId" + size="small" + :disabled="exceedsVariableLimit" + data-qa-selector="add_ci_variable_button" + data-testid="add-ci-variable-button" + @click="setSelectedVariable()" + >{{ $options.i18n.addButton }}</gl-button + > </div> </template> - <template #cell(attributes)="{ item }"> - <span data-testid="ci-variable-table-row-attributes"> - <gl-badge - v-for="attribute in item.attributes" - :key="`${item.key}-${attribute}`" - class="gl-mr-2" - variant="info" - size="sm" + + <gl-loading-icon v-if="isLoading" class="gl-p-4" /> + <gl-alert + v-if="showAlert" + :dismissible="false" + :title="$options.maximumVariableLimitReached" + variant="info" + > + {{ exceedsVariableLimitText }} + </gl-alert> + <gl-table + v-if="!isLoading" + :fields="fields" + :items="variablesWithAttributes" + tbody-tr-class="js-ci-variable-row" + sort-by="key" + sort-direction="asc" + stacked="md" + fixed + show-empty + sort-icon-left + no-sort-reset + no-local-sorting + @sort-changed="(val) => $emit('sort-changed', val)" + > + <template #table-colgroup="scope"> + <col v-for="field in scope.fields" :key="field.key" :style="field.customStyle" /> + </template> + <template #cell(key)="{ item }"> + <div + class="gl-display-flex gl-align-items-flex-start gl-justify-content-end gl-lg-justify-content-start gl-mr-n3" > - {{ attribute }} - </gl-badge> - </span> - </template> - <template #cell(environmentScope)="{ item }"> - <div - class="gl-display-flex gl-align-items-flex-start gl-justify-content-end gl-lg-justify-content-start gl-mr-n3" - > - <span - :id="`ci-variable-env-${item.id}`" - class="gl-display-inline-block gl-max-w-full gl-word-break-word" - >{{ convertEnvironmentScopeValue(item.environmentScope) }}</span + <span + :id="`ci-variable-key-${item.id}`" + class="gl-display-inline-block gl-max-w-full gl-word-break-word" + >{{ item.key }}</span + > + <gl-button + v-gl-tooltip + category="tertiary" + icon="copy-to-clipboard" + class="gl-my-n3 gl-ml-2" + :title="__('Copy key')" + :data-clipboard-text="item.key" + :aria-label="__('Copy to clipboard')" + /> + </div> + </template> + <template v-if="!isInheritedGroupVars" #cell(value)="{ item }"> + <div + class="gl-display-flex gl-align-items-flex-start gl-justify-content-end gl-lg-justify-content-start gl-mr-n3" > - <gl-button - v-gl-tooltip - category="tertiary" - icon="copy-to-clipboard" - class="gl-my-n3 gl-ml-2" - :title="__('Copy environment')" - :data-clipboard-text="convertEnvironmentScopeValue(item.environmentScope)" - :aria-label="__('Copy to clipboard')" - /> - </div> - </template> - <template v-if="isInheritedGroupVars" #cell(group)="{ item }"> - <div - class="gl-display-flex gl-align-items-flex-start gl-justify-content-end gl-lg-justify-content-start gl-mr-n3" - > - <gl-link - :id="`ci-variable-group-${item.id}`" - data-testid="ci-variable-table-row-cicd-path" - class="gl-display-inline-block gl-max-w-full gl-word-break-word" - :href="item.groupCiCdSettingsPath" + <span v-if="areValuesHidden" data-testid="hiddenValue">*****</span> + <span + v-else + :id="`ci-variable-value-${item.id}`" + class="gl-display-inline-block gl-max-w-full gl-text-truncate" + data-testid="revealedValue" + >{{ item.value }}</span + > + <gl-button + v-gl-tooltip + category="tertiary" + icon="copy-to-clipboard" + class="gl-my-n3 gl-ml-2" + :title="__('Copy value')" + :data-clipboard-text="item.value" + :aria-label="__('Copy to clipboard')" + /> + </div> + </template> + <template #cell(attributes)="{ item }"> + <span data-testid="ci-variable-table-row-attributes"> + <gl-badge + v-for="attribute in item.attributes" + :key="`${item.key}-${attribute}`" + class="gl-mr-2" + variant="info" + size="sm" + > + {{ attribute }} + </gl-badge> + </span> + </template> + <template #cell(environmentScope)="{ item }"> + <div + class="gl-display-flex gl-align-items-flex-start gl-justify-content-end gl-lg-justify-content-start gl-mr-n3" > - {{ item.groupName }} - </gl-link> - </div> - </template> - <template v-if="!isInheritedGroupVars" #cell(actions)="{ item }"> - <gl-button - v-gl-modal-directive="$options.modalId" - icon="pencil" - :aria-label="__('Edit')" - data-qa-selector="edit_ci_variable_button" - @click="setSelectedVariable(item.index)" - /> - </template> - <template #empty> - <p class="gl-text-center gl-py-6 gl-text-black-normal gl-mb-0"> - {{ __('There are no variables yet.') }} - </p> - </template> - </gl-table> - <gl-alert - v-if="showAlert" - :dismissible="false" - :title="$options.maximumVariableLimitReached" - variant="info" - > - {{ exceedsVariableLimitText }} - </gl-alert> + <span + :id="`ci-variable-env-${item.id}`" + class="gl-display-inline-block gl-max-w-full gl-word-break-word" + >{{ convertEnvironmentScopeValue(item.environmentScope) }}</span + > + <gl-button + v-gl-tooltip + category="tertiary" + icon="copy-to-clipboard" + class="gl-my-n3 gl-ml-2" + :title="__('Copy environment')" + :data-clipboard-text="convertEnvironmentScopeValue(item.environmentScope)" + :aria-label="__('Copy to clipboard')" + /> + </div> + </template> + <template v-if="isInheritedGroupVars" #cell(group)="{ item }"> + <div + class="gl-display-flex gl-align-items-flex-start gl-justify-content-end gl-lg-justify-content-start gl-mr-n3" + > + <gl-link + :id="`ci-variable-group-${item.id}`" + data-testid="ci-variable-table-row-cicd-path" + class="gl-display-inline-block gl-max-w-full gl-word-break-word" + :href="item.groupCiCdSettingsPath" + > + {{ item.groupName }} + </gl-link> + </div> + </template> + <template v-if="!isInheritedGroupVars" #cell(actions)="{ item }"> + <div class="gl-display-flex gl-justify-content-end gl-mt-n2 gl-mb-n2"> + <gl-button + v-gl-modal-directive="$options.modalId" + icon="pencil" + size="small" + class="gl-mr-3" + :aria-label="$options.i18n.editButton" + data-qa-selector="edit_ci_variable_button" + @click="setSelectedVariable(item.index)" + /> + <gl-button + v-gl-modal-directive="`delete-variable-${item.index}`" + variant="danger" + category="secondary" + icon="remove" + size="small" + :aria-label="$options.i18n.deleteButton" + data-qa-selector="delete_ci_variable_button" + /> + <gl-modal + ref="modal" + :modal-id="`delete-variable-${item.index}`" + :title="$options.i18n.modalDeleteTitle" + :action-primary="$options.deleteModal.actionPrimary" + :action-secondary="$options.deleteModal.actionSecondary" + @primary="deleteSelectedVariable(item.index)" + > + {{ removeVariableMessage(item.key) }} + </gl-modal> + </div> + </template> + <template #empty> + <p class="gl-text-secondary gl-text-center gl-py-1 gl-mb-0"> + {{ __('There are no variables yet.') }} + </p> + </template> + </gl-table> + <gl-alert + v-if="showAlert" + :dismissible="false" + :title="$options.maximumVariableLimitReached" + variant="info" + > + {{ exceedsVariableLimitText }} + </gl-alert> + </gl-card> <div v-if="!isInheritedGroupVars"> - <div v-if="!showPagination" class="ci-variable-actions gl-display-flex gl-mt-5"> - <gl-button - v-gl-modal-directive="$options.modalId" - class="gl-mr-3" - data-qa-selector="add_ci_variable_button" - variant="confirm" - category="primary" - :aria-label="__('Add')" - :disabled="exceedsVariableLimit" - @click="setSelectedVariable()" - >{{ __('Add variable') }}</gl-button - > - <gl-button v-if="!isTableEmpty" @click="toggleHiddenState">{{ - valuesButtonText - }}</gl-button> - </div> - <div v-else class="gl-display-flex gl-justify-content-center gl-mt-6"> + <div v-if="showPagination" class="gl-display-flex gl-justify-content-center gl-mt-5"> <gl-keyset-pagination v-bind="pageInfo" :prev-text="__('Previous')" diff --git a/app/assets/javascripts/repository/index.js b/app/assets/javascripts/repository/index.js index 5c53fc04640..87274e9c8ea 100644 --- a/app/assets/javascripts/repository/index.js +++ b/app/assets/javascripts/repository/index.js @@ -230,20 +230,12 @@ export default function setupVueRepositoryList() { const treeHistoryLinkEl = document.getElementById('js-tree-history-link'); const { historyLink } = treeHistoryLinkEl.dataset; - let { isProjectOverview } = treeHistoryLinkEl.dataset; - - const isProjectOverviewAfterEach = router.afterEach(() => { - isProjectOverview = false; - isProjectOverviewAfterEach(); - }); // eslint-disable-next-line no-new new Vue({ el: treeHistoryLinkEl, router, render(h) { - if (parseBoolean(isProjectOverview) && !this.$route.params.path) return null; - return h( GlButton, { diff --git a/app/assets/stylesheets/framework/new_card.scss b/app/assets/stylesheets/framework/new_card.scss index a1d7761bd88..3fcaab671f3 100644 --- a/app/assets/stylesheets/framework/new_card.scss +++ b/app/assets/stylesheets/framework/new_card.scss @@ -97,7 +97,8 @@ // Table adjustments @mixin new-card-table-adjustments { tbody > tr { - &:first-of-type > td[data-label] { + &:first-of-type > td[data-label], + &:first-of-type > td:first-of-type:last-of-type { @include gl-border-t-0; } diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss index ad792a6ee50..c6f2b730d9b 100644 --- a/app/assets/stylesheets/pages/settings.scss +++ b/app/assets/stylesheets/pages/settings.scss @@ -50,7 +50,6 @@ } } -.ci-variable-table, .deploy-freeze-table, .ci-secure-files-table { table { diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb index 6678bb563ed..67a445c14ab 100644 --- a/app/mailers/emails/merge_requests.rb +++ b/app/mailers/emails/merge_requests.rb @@ -186,5 +186,3 @@ module Emails end end end - -Emails::MergeRequests.prepend_mod_with('Emails::MergeRequests') diff --git a/app/views/admin/application_settings/ci_cd.html.haml b/app/views/admin/application_settings/ci_cd.html.haml index a9a16f72ebe..9b36a983f26 100644 --- a/app/views/admin/application_settings/ci_cd.html.haml +++ b/app/views/admin/application_settings/ci_cd.html.haml @@ -8,7 +8,7 @@ = render 'admin/application_settings/ci/header', expanded: expanded_by_default? .settings-content - if ci_variable_protected_by_default? - %p.settings-message.text-center + %p.settings-message.text-center.gl-mb-0 - link_start = '<a href="%{url}">'.html_safe % { url: help_page_path('ci/variables/index', anchor: 'protect-a-cicd-variable') } = s_('Environment variables on this GitLab instance are configured to be %{link_start}protected%{link_end} by default.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe } #js-instance-variables{ data: { endpoint: admin_ci_variables_path, maskable_regex: ci_variable_maskable_regex, protected_by_default: ci_variable_protected_by_default?.to_s} } diff --git a/app/views/ci/variables/_index.html.haml b/app/views/ci/variables/_index.html.haml index 5eed4e92386..7e5e8d8f961 100644 --- a/app/views/ci/variables/_index.html.haml +++ b/app/views/ci/variables/_index.html.haml @@ -32,7 +32,5 @@ environment_scope_link: help_page_path('ci/environments/index', anchor: 'limit-the-environment-scope-of-a-cicd-variable') } } - if !@group && @project.group - .settings-header.border-top.gl-mt-6 - = render 'ci/group_variables/header' - .settings-content.pr-0 - = render 'ci/group_variables/index' + = render 'ci/group_variables/header' + = render 'ci/group_variables/index' diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml index ba6cf689bdc..cb341ede9de 100644 --- a/app/views/projects/_files.html.haml +++ b/app/views/projects/_files.html.haml @@ -15,7 +15,7 @@ #js-code-owners{ data: { branch: @ref, can_view_branch_rules: can_view_branch_rules?, branch_rules_path: branch_rules_path } } .nav-block.gl-display-flex.gl-xs-flex-direction-column.gl-align-items-stretch - = render 'projects/tree/tree_header', tree: @tree, is_project_overview: is_project_overview + = render 'projects/tree/tree_header', tree: @tree - if project.forked? #js-fork-info{ data: vue_fork_divergence_data(project, ref) } diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index c834a0bc818..a4ed19c2fc9 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -1,5 +1,3 @@ -- is_project_overview = local_assigns.fetch(:is_project_overview, false) - .tree-ref-container.gl-display-flex.gl-flex-wrap.gl-gap-2.mb-2.mb-md-0 .tree-ref-holder.gl-max-w-26{ data: { qa_selector: 'ref_dropdown_container' } } #js-tree-ref-switcher{ data: { project_id: @project.id, ref_type: @ref_type.to_s, project_root_path: project_path(@project) } } @@ -10,7 +8,7 @@ .tree-controls .d-block.d-sm-flex.flex-wrap.align-items-start.gl-children-ml-sm-3.gl-first-child-ml-sm-0< = render_if_exists 'projects/tree/lock_link' - #js-tree-history-link{ data: { history_link: project_commits_path(@project, @ref), is_project_overview: is_project_overview.to_s } } + #js-tree-history-link{ data: { history_link: project_commits_path(@project, @ref) } } = render 'projects/find_file_link' = render 'shared/web_ide_button', blob: nil diff --git a/data/deprecations/16-3-deprecate-twitter-saas.yml b/data/deprecations/16-3-deprecate-twitter-saas.yml new file mode 100644 index 00000000000..3c0150ec65c --- /dev/null +++ b/data/deprecations/16-3-deprecate-twitter-saas.yml @@ -0,0 +1,10 @@ +- title: "Twitter OmniAuth login option is removed from GitLab.com" + removal_milestone: "16.3" + announcement_milestone: "16.3" + breaking_change: true + reporter: jessieay + stage: Manage + issue_url: https://gitlab.com/gitlab-com/Product/-/issues/11417 + body: | # (required) Do not modify this line, instead modify the lines below. + Twitter OAuth 1.0a OmniAuth is being deprecated and removed on GitLab.com in GitLab 16.3 due to low use, lack of gem support, and the lack of a functional sign-in option for this feature. If you sign into GitLab.com with Twitter, you can sign in with a password or another [supported OmniAuth provider](https://gitlab.com/users/sign_in). + documentation_url: https://docs.gitlab.com/ee/integration/twitter.html diff --git a/data/deprecations/16-3-deprecate-twitter-sm.yml b/data/deprecations/16-3-deprecate-twitter-sm.yml new file mode 100644 index 00000000000..562fce1ff1c --- /dev/null +++ b/data/deprecations/16-3-deprecate-twitter-sm.yml @@ -0,0 +1,10 @@ +- title: "Twitter OmniAuth login option is deprecated from self-managed GitLab" + removal_milestone: "17.0" + announcement_milestone: "16.3" + breaking_change: true + reporter: jessieay + stage: Manage + issue_url: https://gitlab.com/gitlab-com/Product/-/issues/11417 + body: | # (required) Do not modify this line, instead modify the lines below. + Twitter OAuth 1.0a OmniAuth is deprecated and will be removed for self-managed GitLab instances in GitLab 17.0 due to low use and lack of gem support. Use [another supported OmniAuth provider](https://docs.gitlab.com/ee/integration/omniauth.html#supported-providers) instead. + documentation_url: https://docs.gitlab.com/ee/integration/twitter.html diff --git a/doc/development/ai_features.md b/doc/development/ai_features.md index 79d03ff6c72..aa9051438c8 100644 --- a/doc/development/ai_features.md +++ b/doc/development/ai_features.md @@ -138,6 +138,53 @@ To populate the embedding database for GitLab chat: 1. Open a rails console 1. Run [this script](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/10588#note_1373586079) to populate the embedding database +### Contributing to GitLab Duo Chat + +The Chat feature uses a [zero-shot agent](https://gitlab.com/gitlab-org/gitlab/blob/master/ee/lib/gitlab/llm/chain/agents/zero_shot/executor.rb) that includes a system prompt explaining how the large language model should interpret the question and provide an +answer. The system prompt defines available tools that can be used to gather +information to answer the user's question. + +The zero-shot agent receives the user's question and decides which tools to use to gather information to answer it. +It then makes a request to the large language model, which decides if it can answer directly or if it needs to use one +of the defined tools. + +The tools each have their own prompt that provides instructions to the large language model on how to use that tool to +gather information. The tools are designed to be self-sufficient and avoid multiple requests back and forth to +the large language model. + +After the tools have gathered the required information, it is returned to the zero-shot agent, which asks the large language +model if enough information has been gathered to provide the final answer to the user's question. + +#### Adding a new tool + +To add a new tool: + +1. Create files for the tool in the `ee/lib/gitlab/llm/chain/tools/` folder. Use existing tools like `issue_identifier` or +`resource_reader` as a template. + +1. Write a class for the tool that includes: + + - Name and description of what the tool does + - Example questions that would use this tool + - Instructions for the large language model on how to use the tool to gather information - so the main prompts that + this tool is using. + +1. Test and iterate on the prompt using RSpec tests that make real requests to the large language model. + - Prompts require trial and error, the non-deterministic nature of working with LLM can be surprising. + - Anthropic provides good [guide](https://docs.anthropic.com/claude/docs/introduction-to-prompt-design) on working on prompts. + +1. Implement code in the tool to parse the response from the large language model and return it to the zero-shot agent. + +1. Add the new tool name to the `tools` array in `ee/lib/gitlab/llm/completions/chat.rb` so the zero-shot agent knows about it. + +1. Add tests by adding questions to the test-suite for which the new tool should respond to. Iterate on the prompts as needed. + +The key things to keep in mind are properly instructing the large language model through prompts and tool descriptions, +keeping tools self-sufficient, and returning responses to the zero-shot agent. With some trial and error on prompts, +adding new tools can expand the capabilities of the chat feature. + +There are available short [videos](https://www.youtube.com/playlist?list=PL05JrBw4t0KoOK-bm_bwfHaOv-1cveh8i) covering this topic. + ### Debugging To gather more insights about the full request, use the `Gitlab::Llm::Logger` file to debug logs. diff --git a/doc/integration/twitter.md b/doc/integration/twitter.md index 3edb0f25938..4e1762d0268 100644 --- a/doc/integration/twitter.md +++ b/doc/integration/twitter.md @@ -4,7 +4,14 @@ group: Authentication and Authorization info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments --- -# Twitter OAuth 1.0a OmniAuth Provider **(FREE SELF)** +# Twitter OAuth 1.0a OmniAuth Provider (deprecated) **(FREE SELF)** + +<!--- start_remove The following content will be removed on remove_date: '2024-05-17' --> + +WARNING: +This feature was [deprecated](https://gitlab.com/gitlab-com/Product/-/issues/11417) in GitLab 16.3 and is planned for removal in 17.0. Use [another supported OmniAuth provider](omniauth.md#supported-providers) instead. This change is a breaking change. + +<!--- end_remove --> NOTE: Twitter OAuth 2.0 support is [not supported](https://gitlab.com/gitlab-org/gitlab/-/issues/366213). diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md index 89d7f04b549..47bece45a45 100644 --- a/doc/update/deprecations.md +++ b/doc/update/deprecations.md @@ -774,6 +774,20 @@ In some cases, like when a downstream pipeline had the `passed with warnings` st <div class="deprecation breaking-change" data-milestone="17.0"> +### Twitter OmniAuth login option is deprecated from self-managed GitLab + +<div class="deprecation-notes"> +- Announced in GitLab <span class="milestone">16.3</span> +- Removal in GitLab <span class="milestone">17.0</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) +- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-com/Product/-/issues/11417). +</div> + +Twitter OAuth 1.0a OmniAuth is deprecated and will be removed for self-managed GitLab instances in GitLab 17.0 due to low use and lack of gem support. Use [another supported OmniAuth provider](https://docs.gitlab.com/ee/integration/omniauth.html#supported-providers) instead. + +</div> + +<div class="deprecation breaking-change" data-milestone="17.0"> + ### Unified approval rules are deprecated <div class="deprecation-notes"> @@ -1032,6 +1046,20 @@ The GitLab [License Compliance](https://docs.gitlab.com/ee/user/compliance/licen | LS template is included but DS template is not | License data from LS job is used | License data from LS job is used | No license data | </div> + +<div class="deprecation breaking-change" data-milestone="16.3"> + +### Twitter OmniAuth login option is removed from GitLab.com + +<div class="deprecation-notes"> +- Announced in GitLab <span class="milestone">16.3</span> +- Removal in GitLab <span class="milestone">16.3</span> ([breaking change](https://docs.gitlab.com/ee/update/terminology.html#breaking-change)) +- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/gitlab-com/Product/-/issues/11417). +</div> + +Twitter OAuth 1.0a OmniAuth is being deprecated and removed on GitLab.com in GitLab 16.3 due to low use, lack of gem support, and the lack of a functional sign-in option for this feature. If you sign into GitLab.com with Twitter, you can sign in with a password or another [supported OmniAuth provider](https://gitlab.com/users/sign_in). + +</div> </div> <div class="milestone-wrapper" data-milestone="16.1"> diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 758f6701083..d87aaee2558 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -10083,15 +10083,27 @@ msgstr "" msgid "CiVariables|Add Variable" msgstr "" +msgid "CiVariables|Add variable" +msgstr "" + msgid "CiVariables|Attributes" msgstr "" +msgid "CiVariables|CI/CD Variables" +msgstr "" + msgid "CiVariables|Cancel" msgstr "" msgid "CiVariables|Cannot use Masked Variable with current value" msgstr "" +msgid "CiVariables|Delete variable" +msgstr "" + +msgid "CiVariables|Do you want to delete the variable %{key}?" +msgstr "" + msgid "CiVariables|Environments" msgstr "" diff --git a/spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js index f3f1c5bd2c5..39c03a41660 100644 --- a/spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js +++ b/spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js @@ -1,4 +1,4 @@ -import { GlAlert, GlBadge, GlKeysetPagination } from '@gitlab/ui'; +import { GlAlert, GlBadge, GlKeysetPagination, GlCard, GlIcon } from '@gitlab/ui'; import { sprintf } from '~/locale'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import CiVariableTable from '~/ci/ci_variable_list/components/ci_variable_table.vue'; @@ -36,7 +36,7 @@ describe('Ci variable table', () => { }; const findRevealButton = () => wrapper.findByText('Reveal values'); - const findAddButton = () => wrapper.findByLabelText('Add'); + const findAddButton = () => wrapper.findByTestId('add-ci-variable-button'); const findEditButton = () => wrapper.findByLabelText('Edit'); const findEmptyVariablesPlaceholder = () => wrapper.findByText('There are no variables yet.'); const findHiddenValues = () => wrapper.findAllByTestId('hiddenValue'); @@ -50,11 +50,30 @@ describe('Ci variable table', () => { const findGroupCiCdSettingsLink = (rowIndex) => wrapper.findAllByTestId('ci-variable-table-row-cicd-path').at(rowIndex).attributes('href'); const findKeysetPagination = () => wrapper.findComponent(GlKeysetPagination); + const findCard = () => wrapper.findComponent(GlCard); const generateExceedsVariableLimitText = (entity, currentVariableCount, maxVariableLimit) => { return sprintf(EXCEEDS_VARIABLE_LIMIT_TEXT, { entity, currentVariableCount, maxVariableLimit }); }; + describe('card', () => { + it('displays the correct title', () => { + createComponent(); + expect(findCard().text()).toContain('CI/CD Variables'); + }); + + it('displays the correct icon', () => { + createComponent(); + expect(findCard().findComponent(GlIcon).props('name')).toBe('code'); + }); + + it('displays the number of added CI/CD Variables', () => { + const variables = [1, 2, 3]; + createComponent({ props: { variables } }); + expect(findCard().text()).toContain(String(variables.length)); + }); + }); + describe.each` isVariablePagesEnabled | text ${true} | ${'enabled'} @@ -88,7 +107,7 @@ describe('Ci variable table', () => { ${1} | ${'Value'} ${2} | ${'Attributes'} ${3} | ${'Environments'} - ${4} | ${''} + ${4} | ${'Actions'} `('renders the $text column', ({ index, text }) => { expect(findTableColumnText(index)).toEqual(text); }); |