From 1f76765ed7d0ba4de4e10be1973ac4cec74ea1f9 Mon Sep 17 00:00:00 2001 From: Yunwen Zheng Date: Wed, 6 Aug 2025 13:04:24 -0400 Subject: [PATCH] TokenPermissionsInfo: Add token information for GitLab and Bitbucket (#109158) * TokenPermissionsInfo: add token information for gitlab and bitbucket --- .../provisioning/Config/ConfigForm.tsx | 4 +- .../Shared/TokenPermissionsInfo.tsx | 119 ++++++++++++++---- .../provisioning/Wizard/ConnectStep.tsx | 5 +- .../app/features/provisioning/Wizard/types.ts | 2 + public/app/features/provisioning/utils/git.ts | 6 + public/locales/en-US/grafana.json | 24 ++++ 6 files changed, 133 insertions(+), 27 deletions(-) diff --git a/public/app/features/provisioning/Config/ConfigForm.tsx b/public/app/features/provisioning/Config/ConfigForm.tsx index 177f788acec..e1b4d82bfff 100644 --- a/public/app/features/provisioning/Config/ConfigForm.tsx +++ b/public/app/features/provisioning/Config/ConfigForm.tsx @@ -22,6 +22,7 @@ import { getGitProviderFields, getLocalProviderFields } from '../Wizard/fields'; import { useCreateOrUpdateRepository } from '../hooks/useCreateOrUpdateRepository'; import { RepositoryFormData } from '../types'; import { dataToSpec } from '../utils/data'; +import { getHasTokenInstructions } from '../utils/git'; import { getRepositoryTypeConfig, isGitProvider } from '../utils/repositoryTypes'; import { ConfigFormGithubCollapse } from './ConfigFormGithubCollapse'; @@ -62,6 +63,7 @@ export function ConfigForm({ data }: ConfigFormProps) { // Get field configurations based on provider type const gitFields = isGitBased ? getGitProviderFields(type) : null; const localFields = type === 'local' ? getLocalProviderFields(type) : null; + const hasTokenInstructions = getHasTokenInstructions(type); useEffect(() => { if (request.isSuccess) { @@ -153,7 +155,7 @@ export function ConfigForm({ data }: ConfigFormProps) { /> )} - {type === 'github' && } + {hasTokenInstructions && } - {/* GitHub UI is English only, so these strings are not translated */} - {/* eslint-disable @grafana/i18n/no-untranslated-strings */} Go to - - GitHub Personal Access Tokens + + {tokenText} and click - "Fine-grained token". + "{createTokenButtonText}". Make sure to include these permissions: - {/* eslint-enable @grafana/i18n/no-untranslated-strings */}
    - {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */} -
  • - Content: Read and write -
  • - {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */} -
  • - Metadata: Read only -
  • - {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */} -
  • - Pull requests: Read and write -
  • - {/* eslint-disable-next-line @grafana/i18n/no-untranslated-strings */} -
  • - Webhooks: Read and write -
  • + {getPermissionsForProvider(type).map((permission) => ( + + ))}
); @@ -72,3 +59,87 @@ function getStyles(theme: GrafanaTheme2) { }), }; } + +type Permission = { + name: string; + access: string; +}; + +function getPermissionsForProvider(type: InstructionAvailability): Permission[] { + switch (type) { + case 'github': + // GitHub UI is English only, so these strings are not translated + return [ + { name: 'Contents', access: 'Read and write' }, + { name: 'Metadata', access: 'Read only' }, + { name: 'Pull requests', access: 'Read and write' }, + { name: 'Webhooks', access: 'Read and write' }, + ]; + case 'gitlab': + return [ + { + name: t('provisioning.gitlab.permissions.repository-label', 'Repository'), + access: t('provisioning.gitlab.permissions.repository-read-write', 'Read and write'), + }, + { + name: t('provisioning.gitlab.permissions.user-label', 'User'), + access: t('provisioning.gitlab.permissions.user-read', 'Read only'), + }, + { + name: t('provisioning.gitlab.permissions.api', 'API'), + access: t('provisioning.gitlab.permissions.api-read-write', 'Read and write'), + }, + ]; + case 'bitbucket': + return [ + { + name: t('provisioning.bitbucket.permissions.repository-label', 'Repositories'), + access: t('provisioning.bitbucket.permissions.repository-read-write-admin', 'Read, and write'), + }, + { + name: t('provisioning.bitbucket.permissions.pull-requests-label', 'Pull requests'), + access: t('provisioning.bitbucket.permissions.pull-requests-read-write', 'Read and write'), + }, + { + name: t('provisioning.bitbucket.permissions.webhooks-label', 'Webhooks'), + access: t('provisioning.bitbucket.permissions.webhooks-read-write', 'Read and write'), + }, + ]; + default: + return []; + } +} + +function AccessLevelField({ label, access }: { label: string; access: string }) { + const styles = useStyles2(getStyles); + return ( +
  • + {label}: {access} +
  • + ); +} + +function connectStepInstruction() { + return { + bitbucket: { + // Bitbucket App password will be replaced by API tokens on Sep 9 2025 + createTokenLink: 'https://bitbucket.org/account/settings/app-passwords/', + tokenText: t('provisioning.token-permissions-info.bitbucket.token-text', 'Bitbucket Personal Access Token'), + createTokenButtonText: t( + 'provisioning.token-permissions-info.bitbucket.create-token-button', + 'Create App passwords' + ), + }, + gitlab: { + createTokenLink: 'https://gitlab.com/-/user_settings/personal_access_tokens', + tokenText: t('provisioning.token-permissions-info.gitlab.token-text', 'GitLab Personal Access Token'), + createTokenButtonText: t('provisioning.token-permissions-info.gitlab.create-token-button', 'Add new token'), + }, + // GitHub UI is English only, so these strings are not translated + github: { + createTokenLink: 'https://github.com/settings/personal-access-tokens/new', + tokenText: 'GitHub Personal Access Token', + createTokenButtonText: 'Fine-grained token', + }, + }; +} diff --git a/public/app/features/provisioning/Wizard/ConnectStep.tsx b/public/app/features/provisioning/Wizard/ConnectStep.tsx index a9186f2d05f..49e15f56012 100644 --- a/public/app/features/provisioning/Wizard/ConnectStep.tsx +++ b/public/app/features/provisioning/Wizard/ConnectStep.tsx @@ -4,6 +4,7 @@ import { Controller, useFormContext } from 'react-hook-form'; import { Field, Input, SecretInput, Stack } from '@grafana/ui'; import { TokenPermissionsInfo } from '../Shared/TokenPermissionsInfo'; +import { getHasTokenInstructions } from '../utils/git'; import { isGitProvider } from '../utils/repositoryTypes'; import { getGitProviderFields, getLocalProviderFields } from './fields'; @@ -26,11 +27,11 @@ export function ConnectStep() { // Get field configurations based on provider type const gitFields = isGitBased ? getGitProviderFields(type) : null; const localFields = !isGitBased ? getLocalProviderFields(type) : null; + const hasTokenInstructions = getHasTokenInstructions(type); return ( - {/*TODO: Add same permission info for other providers*/} - {type === 'github' && } + {hasTokenInstructions && } {gitFields && ( <> diff --git a/public/app/features/provisioning/Wizard/types.ts b/public/app/features/provisioning/Wizard/types.ts index c5a0cc52fbf..b98dc293558 100644 --- a/public/app/features/provisioning/Wizard/types.ts +++ b/public/app/features/provisioning/Wizard/types.ts @@ -41,3 +41,5 @@ export type StepStatusInfo = | { status: 'success'; success?: string | StatusInfo } | { status: 'error'; error: string | StatusInfo } | { status: 'warning'; warning: string | StatusInfo }; + +export type InstructionAvailability = Extract; diff --git a/public/app/features/provisioning/utils/git.ts b/public/app/features/provisioning/utils/git.ts index cfc30bbae0d..a5d14b3cb5a 100644 --- a/public/app/features/provisioning/utils/git.ts +++ b/public/app/features/provisioning/utils/git.ts @@ -1,5 +1,7 @@ import { RepositorySpec } from 'app/api/clients/provisioning/v0alpha1'; +import { InstructionAvailability, RepoType } from '../Wizard/types'; + /** * Validates a Git branch name according to the following rules: * 1. The branch name cannot start with `/`, end with `/`, `.`, or whitespace. @@ -64,3 +66,7 @@ export const getRepoHrefForProvider = (spec?: RepositorySpec) => { return undefined; } }; + +export function getHasTokenInstructions(type: RepoType): type is InstructionAvailability { + return type === 'github' || type === 'gitlab' || type === 'bitbucket'; +} diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index b794b8c6e1c..b7ea709d3dd 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -11098,6 +11098,14 @@ "branch-required": "Branch is required", "path-description": "Optional subdirectory path within the repository", "path-label": "Path", + "permissions": { + "pull-requests-label": "Pull requests", + "pull-requests-read-write": "Read and write", + "repository-label": "Repositories", + "repository-read-write-admin": "Read, and write", + "webhooks-label": "Webhooks", + "webhooks-read-write": "Read and write" + }, "pr-workflow-description": "Allows users to choose whether to open a pull request when saving changes. If the repository does not allow direct changes to the main branch, a pull request may still be required.", "pr-workflow-label": "Enable pull request option when saving", "token-description": "Bitbucket App Password with repository permissions", @@ -11311,6 +11319,14 @@ "branch-required": "Branch is required", "path-description": "Optional subdirectory path within the repository", "path-label": "Path", + "permissions": { + "api": "API", + "api-read-write": "Read and write", + "repository-label": "Repository", + "repository-read-write": "Read and write", + "user-label": "User", + "user-read": "Read only" + }, "pr-workflow-description": "Allows users to choose whether to open a merge request when saving changes. If the repository does not allow direct changes to the main branch, a merge request may still be required.", "pr-workflow-label": "Enable merge request option when saving", "token-description": "GitLab Project Access Token with repository permissions", @@ -11517,6 +11533,14 @@ }, "token-permissions-info": { "and-click": "and click", + "bitbucket": { + "create-token-button": "Create App passwords", + "token-text": "Bitbucket Personal Access Token" + }, + "gitlab": { + "create-token-button": "Add new token", + "token-text": "GitLab Personal Access Token" + }, "go-to": "Go to", "make-sure": "Make sure to include these permissions" },