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"
},