mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 18:27:36 +00:00
Review fixes
This commit is contained in:
+2
-2
@@ -58,7 +58,7 @@ export const BitbucketSecretScanningFactory = () => {
|
|||||||
const authHeader = `Basic ${Buffer.from(`${email}:${apiToken}`).toString("base64")}`;
|
const authHeader = `Basic ${Buffer.from(`${email}:${apiToken}`).toString("base64")}`;
|
||||||
|
|
||||||
const { data } = await request.post<{ uuid: string }>(
|
const { data } = await request.post<{ uuid: string }>(
|
||||||
`${IntegrationUrls.BITBUCKET_API_URL}/2.0/workspaces/${payload.config.workspaceSlug}/hooks`,
|
`${IntegrationUrls.BITBUCKET_API_URL}/2.0/workspaces/${encodeURIComponent(payload.config.workspaceSlug)}/hooks`,
|
||||||
{
|
{
|
||||||
description: "Infisical webhook for push events",
|
description: "Infisical webhook for push events",
|
||||||
url: `${cfg.SITE_URL}/secret-scanning/webhooks/bitbucket`,
|
url: `${cfg.SITE_URL}/secret-scanning/webhooks/bitbucket`,
|
||||||
@@ -92,7 +92,7 @@ export const BitbucketSecretScanningFactory = () => {
|
|||||||
const newWebhookUrl = `${cfg.SITE_URL}/secret-scanning/webhooks/bitbucket?dataSourceId=${dataSourceId}`;
|
const newWebhookUrl = `${cfg.SITE_URL}/secret-scanning/webhooks/bitbucket?dataSourceId=${dataSourceId}`;
|
||||||
|
|
||||||
await request.put(
|
await request.put(
|
||||||
`${IntegrationUrls.BITBUCKET_API_URL}/2.0/workspaces/${payload.config.workspaceSlug}/hooks/${webhookId}`,
|
`${IntegrationUrls.BITBUCKET_API_URL}/2.0/workspaces/${encodeURIComponent(payload.config.workspaceSlug)}/hooks/${webhookId}`,
|
||||||
{
|
{
|
||||||
description: "Infisical webhook for push events",
|
description: "Infisical webhook for push events",
|
||||||
url: newWebhookUrl,
|
url: newWebhookUrl,
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ import { logger } from "@app/lib/logger";
|
|||||||
|
|
||||||
import { TBitbucketDataSource, TBitbucketPushEvent } from "./bitbucket-secret-scanning-types";
|
import { TBitbucketDataSource, TBitbucketPushEvent } from "./bitbucket-secret-scanning-types";
|
||||||
|
|
||||||
export const bitBucketSecretScanningService = (
|
export const bitbucketSecretScanningService = (
|
||||||
secretScanningV2DAL: TSecretScanningV2DALFactory,
|
secretScanningV2DAL: TSecretScanningV2DALFactory,
|
||||||
secretScanningV2Queue: Pick<TSecretScanningV2QueueServiceFactory, "queueResourceDiffScan">
|
secretScanningV2Queue: Pick<TSecretScanningV2QueueServiceFactory, "queueResourceDiffScan">
|
||||||
) => {
|
) => {
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ import { TAppConnection } from "@app/services/app-connection/app-connection-type
|
|||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
|
|
||||||
import { bitBucketSecretScanningService } from "./bitbucket/bitbucket-secret-scanning-service";
|
import { bitbucketSecretScanningService } from "./bitbucket/bitbucket-secret-scanning-service";
|
||||||
import { TSecretScanningV2DALFactory } from "./secret-scanning-v2-dal";
|
import { TSecretScanningV2DALFactory } from "./secret-scanning-v2-dal";
|
||||||
import { TSecretScanningV2QueueServiceFactory } from "./secret-scanning-v2-queue";
|
import { TSecretScanningV2QueueServiceFactory } from "./secret-scanning-v2-queue";
|
||||||
|
|
||||||
@@ -901,6 +901,6 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
findSecretScanningConfigByProjectId,
|
findSecretScanningConfigByProjectId,
|
||||||
upsertSecretScanningConfig,
|
upsertSecretScanningConfig,
|
||||||
github: githubSecretScanningService(secretScanningV2DAL, secretScanningV2Queue),
|
github: githubSecretScanningService(secretScanningV2DAL, secretScanningV2Queue),
|
||||||
bitbucket: bitBucketSecretScanningService(secretScanningV2DAL, secretScanningV2Queue)
|
bitbucket: bitbucketSecretScanningService(secretScanningV2DAL, secretScanningV2Queue)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import type { EmitterWebhookEventName } from "@octokit/webhooks/dist-types/types";
|
import type { EmitterWebhookEventName } from "@octokit/webhooks/dist-types/types";
|
||||||
import { PushEvent } from "@octokit/webhooks-types";
|
import { PushEvent } from "@octokit/webhooks-types";
|
||||||
|
import crypto from "crypto";
|
||||||
import { Probot } from "probot";
|
import { Probot } from "probot";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { generateBitbucketWebhookSecret } from "@app/ee/services/secret-scanning-v2/bitbucket/bitbucket-secret-scanning-factory";
|
||||||
import { TBitbucketPushEvent } from "@app/ee/services/secret-scanning-v2/bitbucket/bitbucket-secret-scanning-types";
|
import { TBitbucketPushEvent } from "@app/ee/services/secret-scanning-v2/bitbucket/bitbucket-secret-scanning-types";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import crypto from "crypto";
|
|
||||||
import { generateBitbucketWebhookSecret } from "@app/ee/services/secret-scanning-v2/bitbucket/bitbucket-secret-scanning-factory";
|
|
||||||
|
|
||||||
export const registerSecretScanningV2Webhooks = async (server: FastifyZodProvider) => {
|
export const registerSecretScanningV2Webhooks = async (server: FastifyZodProvider) => {
|
||||||
const probotApp = (app: Probot) => {
|
const probotApp = (app: Probot) => {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export const registerBitbucketConnectionRouter = async (server: FastifyZodProvid
|
|||||||
connectionId: z.string().uuid()
|
connectionId: z.string().uuid()
|
||||||
}),
|
}),
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
workspaceSlug: z.string()
|
workspaceSlug: z.string().min(1).max(255)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ import { ValidateAzureDevOpsConnectionCredentialsSchema } from "./azure-devops/a
|
|||||||
import { azureDevOpsConnectionService } from "./azure-devops/azure-devops-service";
|
import { azureDevOpsConnectionService } from "./azure-devops/azure-devops-service";
|
||||||
import { ValidateAzureKeyVaultConnectionCredentialsSchema } from "./azure-key-vault";
|
import { ValidateAzureKeyVaultConnectionCredentialsSchema } from "./azure-key-vault";
|
||||||
import { ValidateBitbucketConnectionCredentialsSchema } from "./bitbucket";
|
import { ValidateBitbucketConnectionCredentialsSchema } from "./bitbucket";
|
||||||
import { bitBucketConnectionService } from "./bitbucket/bitbucket-connection-service";
|
import { bitbucketConnectionService } from "./bitbucket/bitbucket-connection-service";
|
||||||
import { ValidateCamundaConnectionCredentialsSchema } from "./camunda";
|
import { ValidateCamundaConnectionCredentialsSchema } from "./camunda";
|
||||||
import { camundaConnectionService } from "./camunda/camunda-connection-service";
|
import { camundaConnectionService } from "./camunda/camunda-connection-service";
|
||||||
import { ValidateCloudflareConnectionCredentialsSchema } from "./cloudflare/cloudflare-connection-schema";
|
import { ValidateCloudflareConnectionCredentialsSchema } from "./cloudflare/cloudflare-connection-schema";
|
||||||
@@ -536,7 +536,7 @@ export const appConnectionServiceFactory = ({
|
|||||||
flyio: flyioConnectionService(connectAppConnectionById),
|
flyio: flyioConnectionService(connectAppConnectionById),
|
||||||
gitlab: gitlabConnectionService(connectAppConnectionById, appConnectionDAL, kmsService),
|
gitlab: gitlabConnectionService(connectAppConnectionById, appConnectionDAL, kmsService),
|
||||||
cloudflare: cloudflareConnectionService(connectAppConnectionById),
|
cloudflare: cloudflareConnectionService(connectAppConnectionById),
|
||||||
bitbucket: bitBucketConnectionService(connectAppConnectionById),
|
bitbucket: bitbucketConnectionService(connectAppConnectionById),
|
||||||
zabbix: zabbixConnectionService(connectAppConnectionById)
|
zabbix: zabbixConnectionService(connectAppConnectionById)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ export const listBitbucketRepositories = async (appConnection: TBitbucketConnect
|
|||||||
|
|
||||||
let allRepos: TBitbucketRepo[] = [];
|
let allRepos: TBitbucketRepo[] = [];
|
||||||
let nextUrl: string | undefined =
|
let nextUrl: string | undefined =
|
||||||
`${IntegrationUrls.BITBUCKET_API_URL}/2.0/repositories/${workspaceSlug}?pagelen=100`;
|
`${IntegrationUrls.BITBUCKET_API_URL}/2.0/repositories/${encodeURIComponent(workspaceSlug)}?pagelen=100`;
|
||||||
let iterationCount = 0;
|
let iterationCount = 0;
|
||||||
|
|
||||||
// Limit to 10 iterations, fetching at most 10 * 100 = 1000 repositories
|
// Limit to 10 iterations, fetching at most 10 * 100 = 1000 repositories
|
||||||
|
|||||||
@@ -11,8 +11,19 @@ import {
|
|||||||
import { BitbucketConnectionMethod } from "./bitbucket-connection-enums";
|
import { BitbucketConnectionMethod } from "./bitbucket-connection-enums";
|
||||||
|
|
||||||
export const BitbucketConnectionAccessTokenCredentialsSchema = z.object({
|
export const BitbucketConnectionAccessTokenCredentialsSchema = z.object({
|
||||||
apiToken: z.string().trim().min(1, "API Token required").describe(AppConnections.CREDENTIALS.BITBUCKET.apiToken),
|
apiToken: z
|
||||||
email: z.string().email().trim().min(1, "Email required").describe(AppConnections.CREDENTIALS.BITBUCKET.email)
|
.string()
|
||||||
|
.trim()
|
||||||
|
.min(1, "API Token required")
|
||||||
|
.max(255)
|
||||||
|
.describe(AppConnections.CREDENTIALS.BITBUCKET.apiToken),
|
||||||
|
email: z
|
||||||
|
.string()
|
||||||
|
.email()
|
||||||
|
.trim()
|
||||||
|
.min(1, "Email required")
|
||||||
|
.max(255)
|
||||||
|
.describe(AppConnections.CREDENTIALS.BITBUCKET.email)
|
||||||
});
|
});
|
||||||
|
|
||||||
const BaseBitbucketConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.Bitbucket) });
|
const BaseBitbucketConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.Bitbucket) });
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ type TGetAppConnectionFunc = (
|
|||||||
actor: OrgServiceActor
|
actor: OrgServiceActor
|
||||||
) => Promise<TBitbucketConnection>;
|
) => Promise<TBitbucketConnection>;
|
||||||
|
|
||||||
export const bitBucketConnectionService = (getAppConnection: TGetAppConnectionFunc) => {
|
export const bitbucketConnectionService = (getAppConnection: TGetAppConnectionFunc) => {
|
||||||
const listWorkspaces = async (connectionId: string, actor: OrgServiceActor) => {
|
const listWorkspaces = async (connectionId: string, actor: OrgServiceActor) => {
|
||||||
const appConnection = await getAppConnection(AppConnection.Bitbucket, connectionId, actor);
|
const appConnection = await getAppConnection(AppConnection.Bitbucket, connectionId, actor);
|
||||||
const workspaces = await listBitbucketWorkspaces(appConnection);
|
const workspaces = await listBitbucketWorkspaces(appConnection);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ description: "Learn how to configure secret scanning for Bitbucket."
|
|||||||
3. Configure which workspace and repositories you would like to scan. Then click **Next**.
|
3. Configure which workspace and repositories you would like to scan. Then click **Next**.
|
||||||

|

|
||||||
|
|
||||||
- **Bitbucket Radar Connection** - the connection that has access to the repositories you want to scan.
|
- **Bitbucket Connection** - the connection that has access to the repositories you want to scan.
|
||||||
- **Workspace** - the Bitbucket workspace to scan secrets in.
|
- **Workspace** - the Bitbucket workspace to scan secrets in.
|
||||||
- **Scan Repositories** - select which repositories you would like to scan.
|
- **Scan Repositories** - select which repositories you would like to scan.
|
||||||
- **All Repositories** - Infisical will scan all repositories associated with your connection.
|
- **All Repositories** - Infisical will scan all repositories associated with your connection.
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ Infisical supports the use of [Service Accounts](https://developer.1password.com
|
|||||||
"method": "api-token",
|
"method": "api-token",
|
||||||
"credentials": {
|
"credentials": {
|
||||||
"instanceUrl": "https://1pass.example.com",
|
"instanceUrl": "https://1pass.example.com",
|
||||||
"apiToken": "[PRIVATE TOKEN]"
|
"apiToken": "<YOUR-API-TOKEN>"
|
||||||
}
|
}
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ Infisical supports the use of [API Tokens](https://support.atlassian.com/bitbuck
|
|||||||
"method": "api-token",
|
"method": "api-token",
|
||||||
"credentials": {
|
"credentials": {
|
||||||
"email": "[email protected]",
|
"email": "[email protected]",
|
||||||
"apiToken": "[PRIVATE TOKEN]"
|
"apiToken": "<YOUR-API-TOKEN>"
|
||||||
}
|
}
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|||||||
+1
-1
@@ -120,7 +120,7 @@ export const OCIVaultSyncFields = () => {
|
|||||||
menuPlacement="top"
|
menuPlacement="top"
|
||||||
isLoading={isVaultsLoading && Boolean(connectionId)}
|
isLoading={isVaultsLoading && Boolean(connectionId)}
|
||||||
isDisabled={!connectionId || !selectedCompartment}
|
isDisabled={!connectionId || !selectedCompartment}
|
||||||
value={vaults?.find((v) => v.id === value) ?? null}
|
value={vaults?.find((v) => v.id === value) || null}
|
||||||
onChange={(option) => {
|
onChange={(option) => {
|
||||||
onChange((option as SingleValue<{ id: string }>)?.id ?? null);
|
onChange((option as SingleValue<{ id: string }>)?.id ?? null);
|
||||||
setValue("destinationConfig.keyOcid", "");
|
setValue("destinationConfig.keyOcid", "");
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ import {
|
|||||||
TBitbucketWorkspace
|
TBitbucketWorkspace
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
const bitBucketConnectionKeys = {
|
const bitbucketConnectionKeys = {
|
||||||
all: [...appConnectionKeys.all, "bitbucket"] as const,
|
all: [...appConnectionKeys.all, "bitbucket"] as const,
|
||||||
listRepos: (connectionId: string, workspaceSlug: string) =>
|
listRepos: (connectionId: string, workspaceSlug: string) =>
|
||||||
[...bitBucketConnectionKeys.all, "repos", connectionId, workspaceSlug] as const,
|
[...bitbucketConnectionKeys.all, "repos", connectionId, workspaceSlug] as const,
|
||||||
listWorkspaces: (connectionId: string) =>
|
listWorkspaces: (connectionId: string) =>
|
||||||
[...bitBucketConnectionKeys.all, "workspaces", connectionId] as const
|
[...bitbucketConnectionKeys.all, "workspaces", connectionId] as const
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useBitbucketConnectionListWorkspaces = (
|
export const useBitbucketConnectionListWorkspaces = (
|
||||||
@@ -25,13 +25,13 @@ export const useBitbucketConnectionListWorkspaces = (
|
|||||||
TBitbucketWorkspace[],
|
TBitbucketWorkspace[],
|
||||||
unknown,
|
unknown,
|
||||||
TBitbucketWorkspace[],
|
TBitbucketWorkspace[],
|
||||||
ReturnType<typeof bitBucketConnectionKeys.listWorkspaces>
|
ReturnType<typeof bitbucketConnectionKeys.listWorkspaces>
|
||||||
>,
|
>,
|
||||||
"queryKey" | "queryFn"
|
"queryKey" | "queryFn"
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: bitBucketConnectionKeys.listWorkspaces(connectionId),
|
queryKey: bitbucketConnectionKeys.listWorkspaces(connectionId),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const { data } = await apiRequest.get<TBitbucketConnectionListWorkspacesResponse>(
|
const { data } = await apiRequest.get<TBitbucketConnectionListWorkspacesResponse>(
|
||||||
`/api/v1/app-connections/bitbucket/${connectionId}/workspaces`
|
`/api/v1/app-connections/bitbucket/${connectionId}/workspaces`
|
||||||
@@ -52,18 +52,18 @@ export const useBitbucketConnectionListRepositories = (
|
|||||||
unknown,
|
unknown,
|
||||||
TBitbucketRepo[],
|
TBitbucketRepo[],
|
||||||
// The ReturnType here will be a supertype of the actual queryKey, which is acceptable
|
// The ReturnType here will be a supertype of the actual queryKey, which is acceptable
|
||||||
ReturnType<typeof bitBucketConnectionKeys.listRepos>
|
ReturnType<typeof bitbucketConnectionKeys.listRepos>
|
||||||
>,
|
>,
|
||||||
"queryKey" | "queryFn"
|
"queryKey" | "queryFn"
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
// Append workspaceSlug to the existing query key for unique caching
|
// Append workspaceSlug to the existing query key for unique caching
|
||||||
queryKey: bitBucketConnectionKeys.listRepos(connectionId, workspaceSlug),
|
queryKey: bitbucketConnectionKeys.listRepos(connectionId, workspaceSlug),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const { data } = await apiRequest.get<TBitbucketConnectionListRepositoriesResponse>(
|
const { data } = await apiRequest.get<TBitbucketConnectionListRepositoriesResponse>(
|
||||||
// Include workspaceSlug in the API endpoint path
|
// Include workspaceSlug in the API endpoint path
|
||||||
`/api/v1/app-connections/bitbucket/${connectionId}/repositories?workspaceSlug=${workspaceSlug}`
|
`/api/v1/app-connections/bitbucket/${connectionId}/repositories?workspaceSlug=${encodeURIComponent(workspaceSlug)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
return data.repositories;
|
return data.repositories;
|
||||||
|
|||||||
+9
-9
@@ -89,8 +89,8 @@ export const BitbucketConfigurePage = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const bitBucketWorkspace = watch("targetWorkspace");
|
const bitbucketWorkspace = watch("targetWorkspace");
|
||||||
const bitBucketRepo = watch("targetRepo");
|
const bitbucketRepo = watch("targetRepo");
|
||||||
|
|
||||||
const integrationAuthId = useSearch({
|
const integrationAuthId = useSearch({
|
||||||
from: ROUTE_PATHS.SecretManager.Integratons.BitbucketConfigurePage.id,
|
from: ROUTE_PATHS.SecretManager.Integratons.BitbucketConfigurePage.id,
|
||||||
@@ -103,17 +103,17 @@ export const BitbucketConfigurePage = () => {
|
|||||||
|
|
||||||
const { data: bitbucketRepos, isPending: isBitbucketReposLoading } = useGetIntegrationAuthApps({
|
const { data: bitbucketRepos, isPending: isBitbucketReposLoading } = useGetIntegrationAuthApps({
|
||||||
integrationAuthId: (integrationAuthId as string) ?? "",
|
integrationAuthId: (integrationAuthId as string) ?? "",
|
||||||
workspaceSlug: bitBucketWorkspace?.slug
|
workspaceSlug: bitbucketWorkspace?.slug
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: bitbucketEnvironments, isPending: isBitbucketEnvironmentsLoading } =
|
const { data: bitbucketEnvironments, isPending: isBitbucketEnvironmentsLoading } =
|
||||||
useGetIntegrationAuthBitbucketEnvironments(
|
useGetIntegrationAuthBitbucketEnvironments(
|
||||||
{
|
{
|
||||||
integrationAuthId: (integrationAuthId as string) ?? "",
|
integrationAuthId: (integrationAuthId as string) ?? "",
|
||||||
workspaceSlug: bitBucketWorkspace?.slug ?? "",
|
workspaceSlug: bitbucketWorkspace?.slug ?? "",
|
||||||
repoSlug: bitBucketRepo?.appId ?? ""
|
repoSlug: bitbucketRepo?.appId ?? ""
|
||||||
},
|
},
|
||||||
{ enabled: Boolean(bitBucketWorkspace?.slug && bitBucketRepo?.appId) }
|
{ enabled: Boolean(bitbucketWorkspace?.slug && bitbucketRepo?.appId) }
|
||||||
);
|
);
|
||||||
|
|
||||||
const onSubmit = async ({
|
const onSubmit = async ({
|
||||||
@@ -167,8 +167,8 @@ export const BitbucketConfigurePage = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
bitBucketWorkspace ||
|
bitbucketWorkspace ||
|
||||||
bitBucketRepo ||
|
bitbucketRepo ||
|
||||||
!bitbucketRepos ||
|
!bitbucketRepos ||
|
||||||
!bitbucketWorkspaces ||
|
!bitbucketWorkspaces ||
|
||||||
!currentWorkspace
|
!currentWorkspace
|
||||||
@@ -325,7 +325,7 @@ export const BitbucketConfigurePage = () => {
|
|||||||
? "Select an environment..."
|
? "Select an environment..."
|
||||||
: "No environments found..."
|
: "No environments found..."
|
||||||
}
|
}
|
||||||
isLoading={isBitbucketEnvironmentsLoading && Boolean(bitBucketRepo)}
|
isLoading={isBitbucketEnvironmentsLoading && Boolean(bitbucketRepo)}
|
||||||
isDisabled={!bitbucketEnvironments?.length || isBitbucketEnvironmentsLoading}
|
isDisabled={!bitbucketEnvironments?.length || isBitbucketEnvironmentsLoading}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|||||||
Reference in New Issue
Block a user