mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 22:27:22 +00:00
feat(secret-sync): add Gitlab PR comments suggestions
This commit is contained in:
@@ -2232,7 +2232,8 @@ export const AppConnections = {
|
|||||||
GITLAB: {
|
GITLAB: {
|
||||||
instanceUrl: "The GitLab instance URL to connect with.",
|
instanceUrl: "The GitLab instance URL to connect with.",
|
||||||
accessToken: "The Access Token used to access GitLab.",
|
accessToken: "The Access Token used to access GitLab.",
|
||||||
code: "The OAuth code to use to connect with GitLab."
|
code: "The OAuth code to use to connect with GitLab.",
|
||||||
|
accessTokenType: "The type of token used to connect with GitLab."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,3 +2,8 @@ export enum GitLabConnectionMethod {
|
|||||||
OAuth = "oauth",
|
OAuth = "oauth",
|
||||||
AccessToken = "access-token"
|
AccessToken = "access-token"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum GitLabAccessTokenType {
|
||||||
|
Project = "project",
|
||||||
|
Personal = "personal"
|
||||||
|
}
|
||||||
|
|||||||
@@ -79,10 +79,13 @@ export const refreshGitLabToken = async (
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const expiresAt = new Date(Date.now() + data.expires_in * 1000 - 60000);
|
const expiresAt = new Date(Date.now() + data.expires_in * 1000 - 600000);
|
||||||
|
|
||||||
const encryptedCredentials = await encryptAppConnectionCredentials({
|
const encryptedCredentials = await encryptAppConnectionCredentials({
|
||||||
credentials: {
|
credentials: {
|
||||||
|
instanceUrl,
|
||||||
|
tokenType: data.token_type,
|
||||||
|
createdAt: new Date(data.created_at * 1000).toISOString(),
|
||||||
refreshToken: data.refresh_token,
|
refreshToken: data.refresh_token,
|
||||||
accessToken: data.access_token,
|
accessToken: data.access_token,
|
||||||
expiresAt
|
expiresAt
|
||||||
@@ -174,7 +177,7 @@ export const validateGitLabConnectionCredentials = async (config: TGitLabConnect
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const url = await getGitLabInstanceUrl(inputCredentials.instanceUrl);
|
const url = await getGitLabInstanceUrl(inputCredentials.instanceUrl);
|
||||||
response = await request.get<TGitLabProject[]>(`${url}/api/v4/groups`, {
|
response = await request.get<TGitLabProject[]>(`${url}/api/v4/user`, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
Accept: "application/json"
|
Accept: "application/json"
|
||||||
@@ -200,6 +203,7 @@ export const validateGitLabConnectionCredentials = async (config: TGitLabConnect
|
|||||||
if (method === GitLabConnectionMethod.OAuth && oauthData) {
|
if (method === GitLabConnectionMethod.OAuth && oauthData) {
|
||||||
return {
|
return {
|
||||||
accessToken,
|
accessToken,
|
||||||
|
instanceUrl: inputCredentials.instanceUrl,
|
||||||
refreshToken: oauthData.refresh_token,
|
refreshToken: oauthData.refresh_token,
|
||||||
expiresAt: new Date(Date.now() + oauthData.expires_in * 1000 - 60000),
|
expiresAt: new Date(Date.now() + oauthData.expires_in * 1000 - 60000),
|
||||||
tokenType: oauthData.token_type,
|
tokenType: oauthData.token_type,
|
||||||
|
|||||||
@@ -8,9 +8,8 @@ import {
|
|||||||
GenericUpdateAppConnectionFieldsSchema
|
GenericUpdateAppConnectionFieldsSchema
|
||||||
} from "@app/services/app-connection/app-connection-schemas";
|
} from "@app/services/app-connection/app-connection-schemas";
|
||||||
|
|
||||||
import { GitLabConnectionMethod } from "./gitlab-connection-enums";
|
import { GitLabAccessTokenType, GitLabConnectionMethod } from "./gitlab-connection-enums";
|
||||||
|
|
||||||
// Fixed: Use consistent accessToken naming throughout
|
|
||||||
export const GitLabConnectionAccessTokenCredentialsSchema = z.object({
|
export const GitLabConnectionAccessTokenCredentialsSchema = z.object({
|
||||||
accessToken: z
|
accessToken: z
|
||||||
.string()
|
.string()
|
||||||
@@ -22,7 +21,8 @@ export const GitLabConnectionAccessTokenCredentialsSchema = z.object({
|
|||||||
.trim()
|
.trim()
|
||||||
.url("Invalid Instance URL")
|
.url("Invalid Instance URL")
|
||||||
.optional()
|
.optional()
|
||||||
.describe(AppConnections.CREDENTIALS.GITLAB.instanceUrl)
|
.describe(AppConnections.CREDENTIALS.GITLAB.instanceUrl),
|
||||||
|
accessTokenType: z.nativeEnum(GitLabAccessTokenType).describe(AppConnections.CREDENTIALS.GITLAB.accessTokenType)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const GitLabConnectionOAuthCredentialsSchema = z.object({
|
export const GitLabConnectionOAuthCredentialsSchema = z.object({
|
||||||
@@ -35,7 +35,6 @@ export const GitLabConnectionOAuthCredentialsSchema = z.object({
|
|||||||
.describe(AppConnections.CREDENTIALS.GITLAB.instanceUrl)
|
.describe(AppConnections.CREDENTIALS.GITLAB.instanceUrl)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fixed: Updated schema to match GitLab's actual OAuth response structure
|
|
||||||
export const GitLabConnectionOAuthOutputCredentialsSchema = z.object({
|
export const GitLabConnectionOAuthOutputCredentialsSchema = z.object({
|
||||||
accessToken: z.string().trim(),
|
accessToken: z.string().trim(),
|
||||||
refreshToken: z.string().trim(),
|
refreshToken: z.string().trim(),
|
||||||
@@ -50,7 +49,6 @@ export const GitLabConnectionOAuthOutputCredentialsSchema = z.object({
|
|||||||
.describe(AppConnections.CREDENTIALS.GITLAB.instanceUrl)
|
.describe(AppConnections.CREDENTIALS.GITLAB.instanceUrl)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Schema for refresh token input during initial setup
|
|
||||||
export const GitLabConnectionRefreshTokenCredentialsSchema = z.object({
|
export const GitLabConnectionRefreshTokenCredentialsSchema = z.object({
|
||||||
refreshToken: z.string().trim().min(1, "Refresh token required"),
|
refreshToken: z.string().trim().min(1, "Refresh token required"),
|
||||||
instanceUrl: z
|
instanceUrl: z
|
||||||
@@ -83,8 +81,9 @@ export const SanitizedGitLabConnectionSchema = z.discriminatedUnion("method", [
|
|||||||
BaseGitLabConnectionSchema.extend({
|
BaseGitLabConnectionSchema.extend({
|
||||||
method: z.literal(GitLabConnectionMethod.AccessToken),
|
method: z.literal(GitLabConnectionMethod.AccessToken),
|
||||||
credentials: GitLabConnectionAccessTokenCredentialsSchema.pick({
|
credentials: GitLabConnectionAccessTokenCredentialsSchema.pick({
|
||||||
instanceUrl: true
|
instanceUrl: true,
|
||||||
}) // Don't expose sensitive data
|
accessTokenType: true
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
BaseGitLabConnectionSchema.extend({
|
BaseGitLabConnectionSchema.extend({
|
||||||
method: z.literal(GitLabConnectionMethod.OAuth),
|
method: z.literal(GitLabConnectionMethod.OAuth),
|
||||||
|
|||||||
@@ -271,67 +271,84 @@ export const GitLabSyncFns = {
|
|||||||
const currentVariableMap = new Map(currentVariables.map((v) => [v.key, v]));
|
const currentVariableMap = new Map(currentVariables.map((v) => [v.key, v]));
|
||||||
|
|
||||||
for (const [key, { value }] of Object.entries(secretMap)) {
|
for (const [key, { value }] of Object.entries(secretMap)) {
|
||||||
const existingVariable = currentVariableMap.get(key);
|
try {
|
||||||
|
const existingVariable = currentVariableMap.get(key);
|
||||||
|
|
||||||
if (existingVariable) {
|
if (existingVariable) {
|
||||||
if (existingVariable.value !== value) {
|
if (existingVariable.value !== value) {
|
||||||
await updateGitLabVariable({
|
await updateGitLabVariable({
|
||||||
|
accessToken,
|
||||||
|
connection,
|
||||||
|
projectId,
|
||||||
|
key,
|
||||||
|
variable: {
|
||||||
|
value,
|
||||||
|
variable_type: existingVariable.variable_type,
|
||||||
|
environment_scope: targetEnvironment || existingVariable.environment_scope,
|
||||||
|
protected: destinationConfig.shouldProtectSecrets ?? existingVariable.protected,
|
||||||
|
...(!existingVariable.masked && destinationConfig.shouldMaskSecrets && { masked: value?.length > 8 }),
|
||||||
|
...(!existingVariable.hidden &&
|
||||||
|
destinationConfig.shouldHideSecrets && { masked_and_hidden: value?.length > 8 }),
|
||||||
|
description: existingVariable.description ?? undefined
|
||||||
|
},
|
||||||
|
targetEnvironment
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await createGitLabVariable({
|
||||||
accessToken,
|
accessToken,
|
||||||
connection,
|
connection,
|
||||||
projectId,
|
projectId,
|
||||||
key,
|
|
||||||
variable: {
|
variable: {
|
||||||
|
key,
|
||||||
value,
|
value,
|
||||||
variable_type: existingVariable.variable_type,
|
variable_type: "env_var",
|
||||||
environment_scope: targetEnvironment || existingVariable.environment_scope,
|
environment_scope: targetEnvironment || "*",
|
||||||
protected: destinationConfig.shouldProtectSecrets ?? existingVariable.protected,
|
protected: destinationConfig.shouldProtectSecrets || false,
|
||||||
...(!existingVariable.masked && destinationConfig.shouldMaskSecrets && { masked: value?.length > 8 }),
|
masked: value?.length > 8 ? destinationConfig.shouldMaskSecrets || false : false,
|
||||||
...(!existingVariable.hidden &&
|
masked_and_hidden: value?.length > 8 ? destinationConfig.shouldHideSecrets || false : false
|
||||||
destinationConfig.shouldHideSecrets && { masked_and_hidden: value?.length > 8 }),
|
}
|
||||||
description: existingVariable.description ?? undefined
|
|
||||||
},
|
|
||||||
targetEnvironment
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} catch (error) {
|
||||||
await createGitLabVariable({
|
throw new SecretSyncError({
|
||||||
accessToken,
|
error,
|
||||||
connection,
|
secretKey: key
|
||||||
projectId,
|
|
||||||
variable: {
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
variable_type: "env_var",
|
|
||||||
environment_scope: targetEnvironment || "*",
|
|
||||||
protected: destinationConfig.shouldProtectSecrets || false,
|
|
||||||
masked: value?.length > 8 ? destinationConfig.shouldMaskSecrets || false : false,
|
|
||||||
masked_and_hidden: value?.length > 8 ? destinationConfig.shouldHideSecrets || false : false
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!secretSync.syncOptions.disableSecretDeletion) {
|
if (!secretSync.syncOptions.disableSecretDeletion) {
|
||||||
for (const variable of currentVariables) {
|
for (const variable of currentVariables) {
|
||||||
const shouldDelete =
|
try {
|
||||||
matchesSchema(variable.key, environment?.slug || "", secretSync.syncOptions.keySchema) &&
|
const shouldDelete =
|
||||||
!(variable.key in secretMap);
|
matchesSchema(variable.key, environment?.slug || "", secretSync.syncOptions.keySchema) &&
|
||||||
|
!(variable.key in secretMap);
|
||||||
|
|
||||||
if (shouldDelete) {
|
if (shouldDelete) {
|
||||||
await deleteGitLabVariable({
|
await deleteGitLabVariable({
|
||||||
accessToken,
|
accessToken,
|
||||||
connection,
|
connection,
|
||||||
projectId,
|
projectId,
|
||||||
key: variable.key,
|
key: variable.key,
|
||||||
targetEnvironment
|
targetEnvironment
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new SecretSyncError({
|
||||||
|
error,
|
||||||
|
secretKey: variable.key
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error instanceof SecretSyncError) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
throw new SecretSyncError({
|
throw new SecretSyncError({
|
||||||
error,
|
message: "Failed to sync secrets",
|
||||||
secretKey: "batch_sync"
|
error
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -347,8 +364,8 @@ export const GitLabSyncFns = {
|
|||||||
|
|
||||||
const accessToken = await getValidAccessToken(connection, appConnectionDAL, kmsService);
|
const accessToken = await getValidAccessToken(connection, appConnectionDAL, kmsService);
|
||||||
|
|
||||||
try {
|
for (const key of Object.keys(secretMap)) {
|
||||||
for (const key of Object.keys(secretMap)) {
|
try {
|
||||||
await deleteGitLabVariable({
|
await deleteGitLabVariable({
|
||||||
accessToken,
|
accessToken,
|
||||||
connection,
|
connection,
|
||||||
@@ -356,12 +373,12 @@ export const GitLabSyncFns = {
|
|||||||
key,
|
key,
|
||||||
targetEnvironment
|
targetEnvironment
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new SecretSyncError({
|
||||||
|
error,
|
||||||
|
secretKey: key
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
throw new SecretSyncError({
|
|
||||||
error,
|
|
||||||
secretKey: "batch_remove"
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ export type TGitLabSyncWithCredentials = TGitLabSync & {
|
|||||||
connection: TGitLabConnection;
|
connection: TGitLabConnection;
|
||||||
};
|
};
|
||||||
|
|
||||||
// GitLab CI/CD Variable structure based on API documentation
|
|
||||||
export type TGitLabVariable = {
|
export type TGitLabVariable = {
|
||||||
key: string;
|
key: string;
|
||||||
value: string;
|
value: string;
|
||||||
@@ -25,7 +24,6 @@ export type TGitLabVariable = {
|
|||||||
description: string | null;
|
description: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Type for creating a new variable
|
|
||||||
export type TGitLabVariableCreate = {
|
export type TGitLabVariableCreate = {
|
||||||
key: string;
|
key: string;
|
||||||
value: string;
|
value: string;
|
||||||
@@ -37,7 +35,6 @@ export type TGitLabVariableCreate = {
|
|||||||
description?: string;
|
description?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Type for updating an existing variable
|
|
||||||
export type TGitLabVariableUpdate = {
|
export type TGitLabVariableUpdate = {
|
||||||
value: string;
|
value: string;
|
||||||
variable_type?: "env_var" | "file";
|
variable_type?: "env_var" | "file";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "GitLab App Connection"
|
title: "GitLab Connection"
|
||||||
description: "Learn how to configure a GitLab App Connection for Infisical using OAuth or Access Token methods."
|
description: "Learn how to configure a GitLab Connection for Infisical using OAuth or Access Token methods."
|
||||||
---
|
---
|
||||||
|
|
||||||
Infisical supports two methods for connecting to GitLab: **OAuth** and **Access Token**. Choose the method that best fits your setup and security requirements.
|
Infisical supports two methods for connecting to GitLab: **OAuth** and **Access Token**. Choose the method that best fits your setup and security requirements.
|
||||||
@@ -10,7 +10,7 @@ Infisical supports two methods for connecting to GitLab: **OAuth** and **Access
|
|||||||
The OAuth method provides secure authentication through GitLab's OAuth flow.
|
The OAuth method provides secure authentication through GitLab's OAuth flow.
|
||||||
|
|
||||||
<Accordion title="Self-Hosted Instance Setup">
|
<Accordion title="Self-Hosted Instance Setup">
|
||||||
Using the GitLab App Connection with OAuth on a self-hosted instance of Infisical requires configuring an OAuth application in GitLab and registering your instance with it.
|
Using the GitLab Connection with OAuth on a self-hosted instance of Infisical requires configuring an OAuth application in GitLab and registering your instance with it.
|
||||||
|
|
||||||
**Prerequisites:**
|
**Prerequisites:**
|
||||||
- A GitLab account with existing projects
|
- A GitLab account with existing projects
|
||||||
@@ -47,7 +47,7 @@ Infisical supports two methods for connecting to GitLab: **OAuth** and **Access
|
|||||||
- `CLIENT_ID_GITLAB`: The **Application ID** of your GitLab OAuth application.
|
- `CLIENT_ID_GITLAB`: The **Application ID** of your GitLab OAuth application.
|
||||||
- `CLIENT_SECRET_GITLAB`: The **Secret** of your GitLab OAuth application.
|
- `CLIENT_SECRET_GITLAB`: The **Secret** of your GitLab OAuth application.
|
||||||
|
|
||||||
Once added, restart your Infisical instance and use the GitLab App Connection.
|
Once added, restart your Infisical instance and use the GitLab Connection.
|
||||||
</Step>
|
</Step>
|
||||||
</Steps>
|
</Steps>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
@@ -60,7 +60,7 @@ Infisical supports two methods for connecting to GitLab: **OAuth** and **Access
|
|||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Add Connection">
|
<Step title="Add Connection">
|
||||||
Select the **GitLab App Connection** option from the connection options modal.
|
Select the **GitLab Connection** option from the connection options modal.
|
||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Choose OAuth Method">
|
<Step title="Choose OAuth Method">
|
||||||
@@ -73,7 +73,7 @@ Infisical supports two methods for connecting to GitLab: **OAuth** and **Access
|
|||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Connection Created">
|
<Step title="Connection Created">
|
||||||
Your **GitLab App Connection** is now available for use.
|
Your **GitLab Connection** is now available for use.
|
||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
</Steps>
|
</Steps>
|
||||||
@@ -96,13 +96,17 @@ Infisical supports two methods for connecting to GitLab: **OAuth** and **Access
|
|||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Configure Token">
|
<Step title="Configure Token">
|
||||||
Fill in the token details:
|
<Tabs>
|
||||||
- **Token name**: A descriptive name for the token (e.g., "connection-token")
|
<Tab title="Secret Rotation">
|
||||||
- **Expiration date**: Set an appropriate expiration date
|
For Secret Rotations, your token will require the ability to access the API:
|
||||||
- **Select scopes**: Choose the **api** scope for full API access
|
Fill in the token details:
|
||||||
|
- **Token name**: A descriptive name for the token (e.g., "connection-token")
|
||||||

|
- **Expiration date**: Set an appropriate expiration date
|
||||||
|
- **Select scopes**: Choose the **api** scope for full API access
|
||||||
|
|
||||||
|

|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Copy Token">
|
<Step title="Copy Token">
|
||||||
Copy the generated token immediately as it won't be shown again.
|
Copy the generated token immediately as it won't be shown again.
|
||||||
@@ -126,19 +130,31 @@ Infisical supports two methods for connecting to GitLab: **OAuth** and **Access
|
|||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Configure Token">
|
<Step title="Configure Token">
|
||||||
Fill in the token details:
|
<Tabs>
|
||||||
- **Token name**: A descriptive name for the token
|
<Tab title="Secret Rotation">
|
||||||
- **Expiration date**: Set an appropriate expiration date
|
For Secret Rotations, your token will require the ability to access the API and be at least an **Owner**:
|
||||||
- **Select role**: Choose **Owner** or higher role
|
Fill in the token details:
|
||||||
- **Select scopes**: Choose the **api** scope for API access
|
- **Token name**: A descriptive name for the token
|
||||||
|
- **Expiration date**: Set an appropriate expiration date
|
||||||
|
- **Select role**: Choose **Owner** or higher role
|
||||||
|
- **Select scopes**: Choose the **api** scope for API access
|
||||||
|
|
||||||

|

|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
<Info>
|
||||||
|
Access Token connections require manual token rotation when your GitLab access token expires or is regenerated. Monitor your connection status and update the token as needed.
|
||||||
|
</Info>
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Copy Token">
|
<Step title="Copy Token">
|
||||||
Copy the generated token immediately as it won't be shown again.
|
Copy the generated token immediately as it won't be shown again.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
<Warning>
|
||||||
|
Keep your access token secure and do not share it. Anyone with access to this token can access your GitLab account and projects.
|
||||||
|
</Warning>
|
||||||
</Step>
|
</Step>
|
||||||
</Steps>
|
</Steps>
|
||||||
</Tab>
|
</Tab>
|
||||||
@@ -152,7 +168,7 @@ Infisical supports two methods for connecting to GitLab: **OAuth** and **Access
|
|||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Add Connection">
|
<Step title="Add Connection">
|
||||||
Select the **GitLab App Connection** option from the connection options modal.
|
Select the **GitLab Connection** option from the connection options modal.
|
||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Configure Access Token">
|
<Step title="Configure Access Token">
|
||||||
@@ -163,14 +179,10 @@ Infisical supports two methods for connecting to GitLab: **OAuth** and **Access
|
|||||||
Click **Connect** to establish the connection.
|
Click **Connect** to establish the connection.
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Connection Created">
|
<Step title="Connection Created">
|
||||||
Your **GitLab App Connection** is now available for use.
|
Your **GitLab Connection** is now available for use.
|
||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
<Info>
|
|
||||||
Access Token connections require manual token rotation when your GitLab access token expires or is regenerated. Monitor your connection status and update the token as needed.
|
|
||||||
</Info>
|
|
||||||
|
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "Heroku App Connection"
|
title: "Heroku Connection"
|
||||||
description: "Learn how to configure a Heroku App Connection for Infisical using OAuth or Auth Token methods."
|
description: "Learn how to configure a Heroku Connection for Infisical using OAuth or Auth Token methods."
|
||||||
---
|
---
|
||||||
|
|
||||||
Infisical supports two methods for connecting to Heroku: **OAuth** and **Auth Token**. Choose the method that best fits your setup and security requirements.
|
Infisical supports two methods for connecting to Heroku: **OAuth** and **Auth Token**. Choose the method that best fits your setup and security requirements.
|
||||||
@@ -10,7 +10,7 @@ Infisical supports two methods for connecting to Heroku: **OAuth** and **Auth To
|
|||||||
The OAuth method provides secure authentication through Heroku's OAuth flow.
|
The OAuth method provides secure authentication through Heroku's OAuth flow.
|
||||||
|
|
||||||
<Accordion title="Self-Hosted Instance Setup">
|
<Accordion title="Self-Hosted Instance Setup">
|
||||||
Using the Heroku App Connection with OAuth on a self-hosted instance of Infisical requires configuring an API client in Heroku and registering your instance with it.
|
Using the Heroku Connection with OAuth on a self-hosted instance of Infisical requires configuring an API client in Heroku and registering your instance with it.
|
||||||
|
|
||||||
**Prerequisites:**
|
**Prerequisites:**
|
||||||
- A Heroku account with existing applications
|
- A Heroku account with existing applications
|
||||||
@@ -42,7 +42,7 @@ Infisical supports two methods for connecting to Heroku: **OAuth** and **Auth To
|
|||||||
- `CLIENT_ID_HEROKU`: The **Client ID** of your Heroku API client.
|
- `CLIENT_ID_HEROKU`: The **Client ID** of your Heroku API client.
|
||||||
- `CLIENT_SECRET_HEROKU`: The **Client Secret** of your Heroku API client.
|
- `CLIENT_SECRET_HEROKU`: The **Client Secret** of your Heroku API client.
|
||||||
|
|
||||||
Once added, restart your Infisical instance and use the Heroku App Connection.
|
Once added, restart your Infisical instance and use the Heroku Connection.
|
||||||
</Step>
|
</Step>
|
||||||
</Steps>
|
</Steps>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
@@ -55,7 +55,7 @@ Infisical supports two methods for connecting to Heroku: **OAuth** and **Auth To
|
|||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Add Connection">
|
<Step title="Add Connection">
|
||||||
Select the **Heroku App Connection** option from the connection options modal.
|
Select the **Heroku Connection** option from the connection options modal.
|
||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Choose OAuth Method">
|
<Step title="Choose OAuth Method">
|
||||||
@@ -68,7 +68,7 @@ Infisical supports two methods for connecting to Heroku: **OAuth** and **Auth To
|
|||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Connection Created">
|
<Step title="Connection Created">
|
||||||
Your **Heroku App Connection** is now available for use.
|
Your **Heroku Connection** is now available for use.
|
||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
</Steps>
|
</Steps>
|
||||||
@@ -97,7 +97,7 @@ Infisical supports two methods for connecting to Heroku: **OAuth** and **Auth To
|
|||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Add Connection">
|
<Step title="Add Connection">
|
||||||
Select the **Heroku App Connection** option from the connection options modal.
|
Select the **Heroku Connection** option from the connection options modal.
|
||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Configure Auth Token">
|
<Step title="Configure Auth Token">
|
||||||
@@ -108,7 +108,7 @@ Infisical supports two methods for connecting to Heroku: **OAuth** and **Auth To
|
|||||||
Click **Connect** to establish the connection.
|
Click **Connect** to establish the connection.
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Connection Created">
|
<Step title="Connection Created">
|
||||||
Your **Heroku App Connection** is now available for use.
|
Your **Heroku Connection** is now available for use.
|
||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|||||||
@@ -39,17 +39,17 @@ description: "Learn how to configure a GitLab Sync for Infisical."
|
|||||||
<Accordion title="Individual">
|
<Accordion title="Individual">
|
||||||
- **GitLab Project**: The project to deploy secrets to.
|
- **GitLab Project**: The project to deploy secrets to.
|
||||||
- **GitLab Environment Scope**: The environment scope to deploy secrets to (optional, defaults to "*" for all environments).
|
- **GitLab Environment Scope**: The environment scope to deploy secrets to (optional, defaults to "*" for all environments).
|
||||||
- **Mark Infisical secrets in GitLab as 'Protected' secrets**: If enabled, synced secrets will be marked as protected in GitLab.
|
- **Mark secrets as Protected**: If enabled, synced secrets will be marked as protected in GitLab.
|
||||||
- **Mark Infisical secrets in GitLab as 'Masked' secrets**: If enabled, synced secrets will be masked in GitLab CI/CD logs.
|
- **Mark secrets as Masked**: If enabled, synced secrets will be masked in GitLab CI/CD logs.
|
||||||
- **Mark Infisical secrets in GitLab as 'Hidden' secrets**: If enabled, synced secrets will be hidden from the GitLab UI.
|
- **Mark secrets as Hidden**: If enabled, synced secrets will be hidden from the GitLab UI.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="Group">
|
<Accordion title="Group">
|
||||||
- **GitLab Group**: The group containing the project.
|
- **GitLab Group**: The group containing the project.
|
||||||
- **GitLab Project**: The project to deploy secrets to.
|
- **GitLab Project**: The project to deploy secrets to.
|
||||||
- **GitLab Environment Scope**: The environment scope to deploy secrets to (optional, defaults to "*" for all environments).
|
- **GitLab Environment Scope**: The environment scope to deploy secrets to (optional, defaults to "*" for all environments).
|
||||||
- **Mark Infisical secrets in GitLab as 'Protected' secrets**: If enabled, synced secrets will be marked as protected in GitLab.
|
- **Mark secrets as Protected**: If enabled, synced secrets will be marked as protected in GitLab.
|
||||||
- **Mark Infisical secrets in GitLab as 'Masked' secrets**: If enabled, synced secrets will be masked in GitLab CI/CD logs.
|
- **Mark secrets as Masked**: If enabled, synced secrets will be masked in GitLab CI/CD logs.
|
||||||
- **Mark Infisical secrets in GitLab as 'Hidden' secrets**: If enabled, synced secrets will be hidden from the GitLab UI.
|
- **Mark secrets as Hidden**: If enabled, synced secrets will be hidden from the GitLab UI.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</AccordionGroup>
|
</AccordionGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ description: "Learn how to configure a Heroku Sync for Infisical."
|
|||||||
**Prerequisites:**
|
**Prerequisites:**
|
||||||
|
|
||||||
- Set up and add secrets to [Infisical Cloud](https://app.infisical.com)
|
- Set up and add secrets to [Infisical Cloud](https://app.infisical.com)
|
||||||
- Create a [Heroku App Connection](/integrations/app-connections/heroku)
|
- Create a [Heroku Connection](/integrations/app-connections/heroku)
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab title="Infisical UI">
|
<Tab title="Infisical UI">
|
||||||
@@ -29,7 +29,7 @@ description: "Learn how to configure a Heroku Sync for Infisical."
|
|||||||
4. Configure the **Destination** to where secrets should be deployed, then click **Next**.
|
4. Configure the **Destination** to where secrets should be deployed, then click **Next**.
|
||||||

|

|
||||||
|
|
||||||
- **Heroku App Connection**: The Heroku App Connection to authenticate with.
|
- **Heroku Connection**: The Heroku Connection to authenticate with.
|
||||||
- **Heroku App**: The Heroku application to sync secrets to.
|
- **Heroku App**: The Heroku application to sync secrets to.
|
||||||
|
|
||||||
5. Configure the **Sync Options** to specify how secrets should be synced, then click **Next**.
|
5. Configure the **Sync Options** to specify how secrets should be synced, then click **Next**.
|
||||||
|
|||||||
@@ -590,6 +590,17 @@ You can configure third-party app connections for re-use across Infisical Projec
|
|||||||
|
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="GitLab OAuth Connection">
|
||||||
|
<ParamField query="CLIENT_ID_GITLAB" type="string" default="none" optional>
|
||||||
|
The Application ID of your GitLab OAuth application.
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="CLIENT_SECRET_GITLAB" type="string" default="none" optional>
|
||||||
|
The Secret of your GitLab OAuth application.
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
## Native Secret Integrations
|
## Native Secret Integrations
|
||||||
|
|
||||||
To help you sync secrets from Infisical to services such as Github and Gitlab, Infisical provides native integrations out of the box.
|
To help you sync secrets from Infisical to services such as Github and Gitlab, Infisical provides native integrations out of the box.
|
||||||
|
|||||||
+24
-26
@@ -1,6 +1,6 @@
|
|||||||
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
||||||
import { SingleValue } from "react-select";
|
import { SingleValue } from "react-select";
|
||||||
import { faCircleInfo } from "@fortawesome/free-solid-svg-icons";
|
import { faCircleInfo, faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import { SecretSyncConnectionField } from "@app/components/secret-syncs/forms/SecretSyncConnectionField";
|
import { SecretSyncConnectionField } from "@app/components/secret-syncs/forms/SecretSyncConnectionField";
|
||||||
@@ -40,26 +40,24 @@ const SecretProtectionOption = ({
|
|||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-start justify-between rounded-lg border border-mineshaft-600 bg-mineshaft-800/50 p-4 transition-all duration-200 hover:border-mineshaft-500">
|
<Switch
|
||||||
<div className="flex flex-1 items-start space-x-3">
|
className="bg-mineshaft-400/80 shadow-inner data-[state=checked]:bg-green/80"
|
||||||
<div className="min-w-0 flex-1">
|
id={id}
|
||||||
<div className="mb-1 flex items-center gap-2">
|
thumbClassName="bg-mineshaft-800"
|
||||||
<h4 className="text-sm font-medium text-bunker-100">{title}</h4>
|
onCheckedChange={onChange}
|
||||||
{tooltip && (
|
isChecked={isEnabled}
|
||||||
<Tooltip className="max-w-sm" content={tooltip}>
|
isDisabled={isDisabled}
|
||||||
<FontAwesomeIcon
|
containerClassName="w-full"
|
||||||
icon={faCircleInfo}
|
>
|
||||||
className="cursor-help text-xs text-mineshaft-400 hover:text-mineshaft-300"
|
<p>
|
||||||
/>
|
{title}{" "}
|
||||||
</Tooltip>
|
{tooltip && (
|
||||||
)}
|
<Tooltip className="max-w-md" content={tooltip}>
|
||||||
</div>
|
<FontAwesomeIcon icon={faQuestionCircle} size="sm" className="ml-1" />
|
||||||
</div>
|
</Tooltip>
|
||||||
</div>
|
)}
|
||||||
<div className="ml-4 flex-shrink-0">
|
</p>
|
||||||
<Switch id={id} onCheckedChange={onChange} isChecked={isEnabled} isDisabled={isDisabled} />
|
</Switch>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -86,7 +84,7 @@ export const GitLabSyncFields = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-[calc(100vh-20rem)] overflow-auto">
|
<div className="h-[calc(100vh-28rem)] overflow-auto">
|
||||||
<SecretSyncConnectionField
|
<SecretSyncConnectionField
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
setValue("destinationConfig.projectId", "");
|
setValue("destinationConfig.projectId", "");
|
||||||
@@ -175,7 +173,7 @@ export const GitLabSyncFields = () => {
|
|||||||
helperText={
|
helperText={
|
||||||
<Tooltip
|
<Tooltip
|
||||||
className="max-w-md"
|
className="max-w-md"
|
||||||
content="Ensure the project exists in the connection's GitLab instance URL."
|
content="Ensure the project exists in the connection's GitLab instance URL and the connection has access to it."
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<span>Don't see the project you're looking for?</span>{" "}
|
<span>Don't see the project you're looking for?</span>{" "}
|
||||||
@@ -229,7 +227,7 @@ export const GitLabSyncFields = () => {
|
|||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<SecretProtectionOption
|
<SecretProtectionOption
|
||||||
id="should-protect-secrets"
|
id="should-protect-secrets"
|
||||||
title="Mark Infisical secrets in GitLab as 'Protected' secrets"
|
title="Mark secrets as Protected"
|
||||||
isEnabled={value || false}
|
isEnabled={value || false}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
@@ -242,7 +240,7 @@ export const GitLabSyncFields = () => {
|
|||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<SecretProtectionOption
|
<SecretProtectionOption
|
||||||
id="should-mask-secrets"
|
id="should-mask-secrets"
|
||||||
title="Mark Infisical secrets in GitLab as 'Masked' secrets"
|
title="Mark secrets as Masked"
|
||||||
tooltip="GitLab has limitations for masked variables: secrets must be at least 8 characters long and not match existing CI/CD variable names. Secrets not meeting these criteria won't be masked."
|
tooltip="GitLab has limitations for masked variables: secrets must be at least 8 characters long and not match existing CI/CD variable names. Secrets not meeting these criteria won't be masked."
|
||||||
isEnabled={value || false}
|
isEnabled={value || false}
|
||||||
onChange={(checked) => {
|
onChange={(checked) => {
|
||||||
@@ -262,7 +260,7 @@ export const GitLabSyncFields = () => {
|
|||||||
<div className="max-h-32 opacity-100 transition-all duration-300">
|
<div className="max-h-32 opacity-100 transition-all duration-300">
|
||||||
<SecretProtectionOption
|
<SecretProtectionOption
|
||||||
id="should-hide-secrets"
|
id="should-hide-secrets"
|
||||||
title="Mark Infisical secrets in GitLab as 'Hidden' secrets"
|
title="Mark secrets as Hidden"
|
||||||
tooltip="Secrets can only be marked as hidden if they are also masked."
|
tooltip="Secrets can only be marked as hidden if they are also masked."
|
||||||
isEnabled={value || false}
|
isEnabled={value || false}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
|||||||
+2
-2
@@ -6,7 +6,7 @@ import { SecretSync } from "@app/hooks/api/secretSyncs";
|
|||||||
|
|
||||||
export const GitLabSyncReviewFields = () => {
|
export const GitLabSyncReviewFields = () => {
|
||||||
const { watch } = useFormContext<TSecretSyncForm & { destination: SecretSync.GitLab }>();
|
const { watch } = useFormContext<TSecretSyncForm & { destination: SecretSync.GitLab }>();
|
||||||
const projectId = watch("destinationConfig.projectId");
|
const projectName = watch("destinationConfig.projectName");
|
||||||
const targetEnvironment = watch("destinationConfig.targetEnvironment");
|
const targetEnvironment = watch("destinationConfig.targetEnvironment");
|
||||||
const groupId = watch("destinationConfig.groupId");
|
const groupId = watch("destinationConfig.groupId");
|
||||||
const scope = watch("destinationConfig.scope");
|
const scope = watch("destinationConfig.scope");
|
||||||
@@ -17,7 +17,7 @@ export const GitLabSyncReviewFields = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<GenericFieldLabel label="Scope">{scope}</GenericFieldLabel>
|
<GenericFieldLabel label="Scope">{scope}</GenericFieldLabel>
|
||||||
<GenericFieldLabel label="Project ID">{projectId}</GenericFieldLabel>
|
<GenericFieldLabel label="Project Name">{projectName}</GenericFieldLabel>
|
||||||
{groupId && <GenericFieldLabel label="Group ID">{groupId}</GenericFieldLabel>}
|
{groupId && <GenericFieldLabel label="Group ID">{groupId}</GenericFieldLabel>}
|
||||||
{targetEnvironment && (
|
{targetEnvironment && (
|
||||||
<GenericFieldLabel label="Environment">{targetEnvironment}</GenericFieldLabel>
|
<GenericFieldLabel label="Environment">{targetEnvironment}</GenericFieldLabel>
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export const SECRET_SYNC_MAP: Record<SecretSync, { name: string; image: string }
|
|||||||
image: "Flyio.svg"
|
image: "Flyio.svg"
|
||||||
},
|
},
|
||||||
[SecretSync.GitLab]: {
|
[SecretSync.GitLab]: {
|
||||||
name: "Gitlab",
|
name: "GitLab",
|
||||||
image: "GitLab.png"
|
image: "GitLab.png"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,3 +7,8 @@ export type TGitLabGroup = {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum GitLabAccessTokenType {
|
||||||
|
Personal = "personal",
|
||||||
|
Project = "project"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||||
import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
|
import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
|
||||||
|
|
||||||
|
import { GitLabAccessTokenType } from "../gitlab";
|
||||||
|
|
||||||
export enum GitlabConnectionMethod {
|
export enum GitlabConnectionMethod {
|
||||||
AccessToken = "access-token",
|
AccessToken = "access-token",
|
||||||
OAuth = "oauth"
|
OAuth = "oauth"
|
||||||
@@ -12,6 +14,7 @@ export type TGitlabConnection = TRootAppConnection & { app: AppConnection.Gitlab
|
|||||||
credentials: {
|
credentials: {
|
||||||
instanceUrl?: string;
|
instanceUrl?: string;
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
|
accessTokenType: GitLabAccessTokenType;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
|
|||||||
+62
-24
@@ -20,6 +20,7 @@ import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/
|
|||||||
import { isInfisicalCloud } from "@app/helpers/platform";
|
import { isInfisicalCloud } from "@app/helpers/platform";
|
||||||
import { useGetAppConnectionOption } from "@app/hooks/api/appConnections";
|
import { useGetAppConnectionOption } from "@app/hooks/api/appConnections";
|
||||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||||
|
import { GitLabAccessTokenType } from "@app/hooks/api/appConnections/gitlab";
|
||||||
import {
|
import {
|
||||||
GitlabConnectionMethod,
|
GitlabConnectionMethod,
|
||||||
TGitlabConnection
|
TGitlabConnection
|
||||||
@@ -41,6 +42,7 @@ const formSchema = z.discriminatedUnion("method", [
|
|||||||
method: z.literal(GitlabConnectionMethod.AccessToken),
|
method: z.literal(GitlabConnectionMethod.AccessToken),
|
||||||
credentials: z.object({
|
credentials: z.object({
|
||||||
accessToken: z.string().min(1, "Access token is required"),
|
accessToken: z.string().min(1, "Access token is required"),
|
||||||
|
accessTokenType: z.nativeEnum(GitLabAccessTokenType),
|
||||||
instanceUrl: z
|
instanceUrl: z
|
||||||
.string()
|
.string()
|
||||||
.trim()
|
.trim()
|
||||||
@@ -90,6 +92,7 @@ export const GitLabConnectionForm = ({ appConnection, onSubmit: formSubmit }: Pr
|
|||||||
method: GitlabConnectionMethod.AccessToken,
|
method: GitlabConnectionMethod.AccessToken,
|
||||||
credentials: {
|
credentials: {
|
||||||
accessToken: "",
|
accessToken: "",
|
||||||
|
accessTokenType: GitLabAccessTokenType.Personal,
|
||||||
instanceUrl: ""
|
instanceUrl: ""
|
||||||
}
|
}
|
||||||
} as FormData))
|
} as FormData))
|
||||||
@@ -151,7 +154,7 @@ export const GitLabConnectionForm = ({ appConnection, onSubmit: formSubmit }: Pr
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error("Unhandled Gitlab Connection method");
|
throw new Error("Unhandled GitLab Connection method");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error handling form submission:", error);
|
console.error("Error handling form submission:", error);
|
||||||
@@ -169,7 +172,7 @@ export const GitLabConnectionForm = ({ appConnection, onSubmit: formSubmit }: Pr
|
|||||||
isMissingConfig = false;
|
isMissingConfig = false;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unhandled Gitlab Connection method: ${selectedMethod}`);
|
throw new Error(`Unhandled GitLab Connection method: ${selectedMethod}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const methodDetails = getAppConnectionMethodDetails(selectedMethod);
|
const methodDetails = getAppConnectionMethodDetails(selectedMethod);
|
||||||
@@ -211,7 +214,7 @@ export const GitLabConnectionForm = ({ appConnection, onSubmit: formSubmit }: Pr
|
|||||||
? `Environment variables have not been configured. ${
|
? `Environment variables have not been configured. ${
|
||||||
isInfisicalCloud()
|
isInfisicalCloud()
|
||||||
? "Please contact Infisical."
|
? "Please contact Infisical."
|
||||||
: `See Docs to configure Gitlab ${methodDetails.name} Connections.`
|
: `See Docs to configure GitLab ${methodDetails.name} Connections.`
|
||||||
}`
|
}`
|
||||||
: error?.message
|
: error?.message
|
||||||
}
|
}
|
||||||
@@ -245,24 +248,59 @@ export const GitLabConnectionForm = ({ appConnection, onSubmit: formSubmit }: Pr
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{selectedMethod === GitlabConnectionMethod.AccessToken && (
|
{selectedMethod === GitlabConnectionMethod.AccessToken && (
|
||||||
<Controller
|
<>
|
||||||
name="credentials.accessToken"
|
<Controller
|
||||||
control={control}
|
name="credentials.accessTokenType"
|
||||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
control={control}
|
||||||
<FormControl
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
label="Access Token"
|
<FormControl
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
isError={Boolean(error?.message)}
|
isError={Boolean(error?.message)}
|
||||||
tooltipText="Your Gitlab Access Token"
|
label="Access Token Type"
|
||||||
>
|
>
|
||||||
<SecretInput
|
<Select
|
||||||
containerClassName="text-gray-400 group-focus-within:!border-primary-400/50 border border-mineshaft-500 bg-mineshaft-900 px-2.5 py-1.5"
|
isDisabled={isUpdate}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => onChange(e.target.value)}
|
onValueChange={(val) => {
|
||||||
/>
|
onChange(val);
|
||||||
</FormControl>
|
if (val === GitlabConnectionMethod.OAuth) {
|
||||||
)}
|
setValue("credentials.code", "custom");
|
||||||
/>
|
}
|
||||||
|
}}
|
||||||
|
className="w-full border border-mineshaft-500"
|
||||||
|
position="popper"
|
||||||
|
dropdownContainerClassName="max-w-none"
|
||||||
|
>
|
||||||
|
{Object.values(GitLabAccessTokenType).map((method) => {
|
||||||
|
return (
|
||||||
|
<SelectItem value={method} key={method}>
|
||||||
|
{method.charAt(0).toUpperCase() + method.slice(1)} Access Token
|
||||||
|
</SelectItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
name="credentials.accessToken"
|
||||||
|
control={control}
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token"
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error?.message)}
|
||||||
|
tooltipText="Your GitLab Access Token"
|
||||||
|
>
|
||||||
|
<SecretInput
|
||||||
|
containerClassName="text-gray-400 group-focus-within:!border-primary-400/50 border border-mineshaft-500 bg-mineshaft-900 px-2.5 py-1.5"
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="mt-8 flex items-center">
|
<div className="mt-8 flex items-center">
|
||||||
@@ -280,10 +318,10 @@ export const GitLabConnectionForm = ({ appConnection, onSubmit: formSubmit }: Pr
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{isRedirecting && selectedMethod === GitlabConnectionMethod.OAuth
|
{isRedirecting && selectedMethod === GitlabConnectionMethod.OAuth
|
||||||
? "Redirecting to Gitlab..."
|
? "Redirecting to GitLab..."
|
||||||
: isUpdate
|
: isUpdate
|
||||||
? "Reconnect to Gitlab"
|
? "Reconnect to GitLab"
|
||||||
: "Connect to Gitlab"}
|
: "Connect to GitLab"}
|
||||||
</Button>
|
</Button>
|
||||||
<ModalClose asChild>
|
<ModalClose asChild>
|
||||||
<Button colorSchema="secondary" variant="plain">
|
<Button colorSchema="secondary" variant="plain">
|
||||||
|
|||||||
Reference in New Issue
Block a user