feat(secret-sync): add Gitlab PR comments suggestions

This commit is contained in:
carlosmonastyrski
2025-06-24 10:05:46 -03:00
parent c305ddd463
commit 43e0d400f9
17 changed files with 243 additions and 153 deletions

View File

@@ -2232,7 +2232,8 @@ export const AppConnections = {
GITLAB: {
instanceUrl: "The GitLab instance URL to connect with.",
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."
}
}
};

View File

@@ -2,3 +2,8 @@ export enum GitLabConnectionMethod {
OAuth = "oauth",
AccessToken = "access-token"
}
export enum GitLabAccessTokenType {
Project = "project",
Personal = "personal"
}

View File

@@ -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({
credentials: {
instanceUrl,
tokenType: data.token_type,
createdAt: new Date(data.created_at * 1000).toISOString(),
refreshToken: data.refresh_token,
accessToken: data.access_token,
expiresAt
@@ -174,7 +177,7 @@ export const validateGitLabConnectionCredentials = async (config: TGitLabConnect
try {
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: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json"
@@ -200,6 +203,7 @@ export const validateGitLabConnectionCredentials = async (config: TGitLabConnect
if (method === GitLabConnectionMethod.OAuth && oauthData) {
return {
accessToken,
instanceUrl: inputCredentials.instanceUrl,
refreshToken: oauthData.refresh_token,
expiresAt: new Date(Date.now() + oauthData.expires_in * 1000 - 60000),
tokenType: oauthData.token_type,

View File

@@ -8,9 +8,8 @@ import {
GenericUpdateAppConnectionFieldsSchema
} 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({
accessToken: z
.string()
@@ -22,7 +21,8 @@ export const GitLabConnectionAccessTokenCredentialsSchema = z.object({
.trim()
.url("Invalid Instance URL")
.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({
@@ -35,7 +35,6 @@ export const GitLabConnectionOAuthCredentialsSchema = z.object({
.describe(AppConnections.CREDENTIALS.GITLAB.instanceUrl)
});
// Fixed: Updated schema to match GitLab's actual OAuth response structure
export const GitLabConnectionOAuthOutputCredentialsSchema = z.object({
accessToken: z.string().trim(),
refreshToken: z.string().trim(),
@@ -50,7 +49,6 @@ export const GitLabConnectionOAuthOutputCredentialsSchema = z.object({
.describe(AppConnections.CREDENTIALS.GITLAB.instanceUrl)
});
// Schema for refresh token input during initial setup
export const GitLabConnectionRefreshTokenCredentialsSchema = z.object({
refreshToken: z.string().trim().min(1, "Refresh token required"),
instanceUrl: z
@@ -83,8 +81,9 @@ export const SanitizedGitLabConnectionSchema = z.discriminatedUnion("method", [
BaseGitLabConnectionSchema.extend({
method: z.literal(GitLabConnectionMethod.AccessToken),
credentials: GitLabConnectionAccessTokenCredentialsSchema.pick({
instanceUrl: true
}) // Don't expose sensitive data
instanceUrl: true,
accessTokenType: true
})
}),
BaseGitLabConnectionSchema.extend({
method: z.literal(GitLabConnectionMethod.OAuth),

View File

@@ -271,67 +271,84 @@ export const GitLabSyncFns = {
const currentVariableMap = new Map(currentVariables.map((v) => [v.key, v]));
for (const [key, { value }] of Object.entries(secretMap)) {
const existingVariable = currentVariableMap.get(key);
try {
const existingVariable = currentVariableMap.get(key);
if (existingVariable) {
if (existingVariable.value !== value) {
await updateGitLabVariable({
if (existingVariable) {
if (existingVariable.value !== value) {
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,
connection,
projectId,
key,
variable: {
key,
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
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
}
});
}
} else {
await createGitLabVariable({
accessToken,
connection,
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
}
} catch (error) {
throw new SecretSyncError({
error,
secretKey: key
});
}
}
if (!secretSync.syncOptions.disableSecretDeletion) {
for (const variable of currentVariables) {
const shouldDelete =
matchesSchema(variable.key, environment?.slug || "", secretSync.syncOptions.keySchema) &&
!(variable.key in secretMap);
try {
const shouldDelete =
matchesSchema(variable.key, environment?.slug || "", secretSync.syncOptions.keySchema) &&
!(variable.key in secretMap);
if (shouldDelete) {
await deleteGitLabVariable({
accessToken,
connection,
projectId,
key: variable.key,
targetEnvironment
if (shouldDelete) {
await deleteGitLabVariable({
accessToken,
connection,
projectId,
key: variable.key,
targetEnvironment
});
}
} catch (error) {
throw new SecretSyncError({
error,
secretKey: variable.key
});
}
}
}
} catch (error) {
if (error instanceof SecretSyncError) {
throw error;
}
throw new SecretSyncError({
error,
secretKey: "batch_sync"
message: "Failed to sync secrets",
error
});
}
},
@@ -347,8 +364,8 @@ export const GitLabSyncFns = {
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({
accessToken,
connection,
@@ -356,12 +373,12 @@ export const GitLabSyncFns = {
key,
targetEnvironment
});
} catch (error) {
throw new SecretSyncError({
error,
secretKey: key
});
}
} catch (error) {
throw new SecretSyncError({
error,
secretKey: "batch_remove"
});
}
},

View File

@@ -12,7 +12,6 @@ export type TGitLabSyncWithCredentials = TGitLabSync & {
connection: TGitLabConnection;
};
// GitLab CI/CD Variable structure based on API documentation
export type TGitLabVariable = {
key: string;
value: string;
@@ -25,7 +24,6 @@ export type TGitLabVariable = {
description: string | null;
};
// Type for creating a new variable
export type TGitLabVariableCreate = {
key: string;
value: string;
@@ -37,7 +35,6 @@ export type TGitLabVariableCreate = {
description?: string;
};
// Type for updating an existing variable
export type TGitLabVariableUpdate = {
value: string;
variable_type?: "env_var" | "file";

View File

@@ -1,6 +1,6 @@
---
title: "GitLab App Connection"
description: "Learn how to configure a GitLab App Connection for Infisical using OAuth or Access Token methods."
title: "GitLab Connection"
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.
@@ -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.
<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:**
- 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_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>
</Steps>
</Accordion>
@@ -60,7 +60,7 @@ Infisical supports two methods for connecting to GitLab: **OAuth** and **Access
![App Connections Tab](/images/app-connections/general/add-connection.png)
</Step>
<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.
![Select GitLab Connection](/images/app-connections/gitlab/select-gitlab-connection.png)
</Step>
<Step title="Choose OAuth Method">
@@ -73,7 +73,7 @@ Infisical supports two methods for connecting to GitLab: **OAuth** and **Access
![GitLab Authorization](/images/app-connections/gitlab/gitlab-authorization-page.png)
</Step>
<Step title="Connection Created">
Your **GitLab App Connection** is now available for use.
Your **GitLab Connection** is now available for use.
![GitLab OAuth Connection](/images/app-connections/gitlab/gitlab-oauth-connection.png)
</Step>
</Steps>
@@ -96,13 +96,17 @@ Infisical supports two methods for connecting to GitLab: **OAuth** and **Access
![GitLab Personal Access Tokens](/images/app-connections/gitlab/gitlab-add-access-token.png)
</Step>
<Step title="Configure Token">
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
![GitLab Personal Token Form](/images/app-connections/gitlab/gitlab-personal-access-token-form.png)
<Tabs>
<Tab title="Secret Rotation">
For Secret Rotations, your token will require the ability to access the API:
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
![GitLab Personal Token Form](/images/app-connections/gitlab/gitlab-personal-access-token-form.png)
</Tab>
</Tabs>
</Step>
<Step title="Copy Token">
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
![GitLab Project Access Tokens](/images/app-connections/gitlab/gitlab-project-access-token-list.png)
</Step>
<Step title="Configure Token">
Fill in the token details:
- **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
<Tabs>
<Tab title="Secret Rotation">
For Secret Rotations, your token will require the ability to access the API and be at least an **Owner**:
Fill in the token details:
- **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
![GitLab Create Project Token](/images/app-connections/gitlab/gitlab-project-access-token-form.png)
![GitLab Create Project Token](/images/app-connections/gitlab/gitlab-project-access-token-form.png)
</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 title="Copy Token">
Copy the generated token immediately as it won't be shown again.
![GitLab Project Token Form](/images/app-connections/gitlab/gitlab-project-access-token-created.png)
<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>
</Steps>
</Tab>
@@ -152,7 +168,7 @@ Infisical supports two methods for connecting to GitLab: **OAuth** and **Access
![App Connections Tab](/images/app-connections/general/add-connection.png)
</Step>
<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.
![Select GitLab Connection](/images/app-connections/gitlab/select-gitlab-connection.png)
</Step>
<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.
</Step>
<Step title="Connection Created">
Your **GitLab App Connection** is now available for use.
Your **GitLab Connection** is now available for use.
![GitLab Access Token Connection](/images/app-connections/gitlab/gitlab-access-token-connection.png)
</Step>
</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>
</Tabs>

View File

@@ -1,6 +1,6 @@
---
title: "Heroku App Connection"
description: "Learn how to configure a Heroku App Connection for Infisical using OAuth or Auth Token methods."
title: "Heroku Connection"
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.
@@ -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.
<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:**
- 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_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>
</Steps>
</Accordion>
@@ -55,7 +55,7 @@ Infisical supports two methods for connecting to Heroku: **OAuth** and **Auth To
![App Connections Tab](/images/app-connections/general/add-connection.png)
</Step>
<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.
![Select Heroku Connection](/images/app-connections/heroku/heroku-select-connection.png)
</Step>
<Step title="Choose OAuth Method">
@@ -68,7 +68,7 @@ Infisical supports two methods for connecting to Heroku: **OAuth** and **Auth To
![Heroku Authorization](/images/integrations/heroku/integrations-heroku-auth.png)
</Step>
<Step title="Connection Created">
Your **Heroku App Connection** is now available for use.
Your **Heroku Connection** is now available for use.
![Heroku OAuth Connection](/images/app-connections/heroku/heroku-connection.png)
</Step>
</Steps>
@@ -97,7 +97,7 @@ Infisical supports two methods for connecting to Heroku: **OAuth** and **Auth To
![App Connections Tab](/images/app-connections/general/add-connection.png)
</Step>
<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.
![Select Heroku Connection](/images/app-connections/heroku/heroku-select-connection.png)
</Step>
<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.
</Step>
<Step title="Connection Created">
Your **Heroku App Connection** is now available for use.
Your **Heroku Connection** is now available for use.
![Heroku Auth Token Connection](/images/app-connections/heroku/heroku-connection.png)
</Step>
</Steps>

View File

@@ -39,17 +39,17 @@ description: "Learn how to configure a GitLab Sync for Infisical."
<Accordion title="Individual">
- **GitLab Project**: The project to deploy secrets to.
- **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 Infisical secrets in GitLab as 'Masked' secrets**: 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 Protected**: If enabled, synced secrets will be marked as protected in GitLab.
- **Mark secrets as Masked**: If enabled, synced secrets will be masked in GitLab CI/CD logs.
- **Mark secrets as Hidden**: If enabled, synced secrets will be hidden from the GitLab UI.
</Accordion>
<Accordion title="Group">
- **GitLab Group**: The group containing the project.
- **GitLab Project**: The project to deploy secrets to.
- **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 Infisical secrets in GitLab as 'Masked' secrets**: 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 Protected**: If enabled, synced secrets will be marked as protected in GitLab.
- **Mark secrets as Masked**: If enabled, synced secrets will be masked in GitLab CI/CD logs.
- **Mark secrets as Hidden**: If enabled, synced secrets will be hidden from the GitLab UI.
</Accordion>
</AccordionGroup>

View File

@@ -6,7 +6,7 @@ description: "Learn how to configure a Heroku Sync for Infisical."
**Prerequisites:**
- 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>
<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**.
![Configure Destination](/images/secret-syncs/heroku/heroku-destination.png)
- **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.
5. Configure the **Sync Options** to specify how secrets should be synced, then click **Next**.

View File

@@ -590,6 +590,17 @@ You can configure third-party app connections for re-use across Infisical Projec
</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
To help you sync secrets from Infisical to services such as Github and Gitlab, Infisical provides native integrations out of the box.

View File

@@ -1,6 +1,6 @@
import { Controller, useFormContext, useWatch } from "react-hook-form";
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 { SecretSyncConnectionField } from "@app/components/secret-syncs/forms/SecretSyncConnectionField";
@@ -40,26 +40,24 @@ const SecretProtectionOption = ({
tooltip?: string;
}) => {
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">
<div className="flex flex-1 items-start space-x-3">
<div className="min-w-0 flex-1">
<div className="mb-1 flex items-center gap-2">
<h4 className="text-sm font-medium text-bunker-100">{title}</h4>
{tooltip && (
<Tooltip className="max-w-sm" content={tooltip}>
<FontAwesomeIcon
icon={faCircleInfo}
className="cursor-help text-xs text-mineshaft-400 hover:text-mineshaft-300"
/>
</Tooltip>
)}
</div>
</div>
</div>
<div className="ml-4 flex-shrink-0">
<Switch id={id} onCheckedChange={onChange} isChecked={isEnabled} isDisabled={isDisabled} />
</div>
</div>
<Switch
className="bg-mineshaft-400/80 shadow-inner data-[state=checked]:bg-green/80"
id={id}
thumbClassName="bg-mineshaft-800"
onCheckedChange={onChange}
isChecked={isEnabled}
isDisabled={isDisabled}
containerClassName="w-full"
>
<p>
{title}{" "}
{tooltip && (
<Tooltip className="max-w-md" content={tooltip}>
<FontAwesomeIcon icon={faQuestionCircle} size="sm" className="ml-1" />
</Tooltip>
)}
</p>
</Switch>
);
};
@@ -86,7 +84,7 @@ export const GitLabSyncFields = () => {
);
return (
<div className="h-[calc(100vh-20rem)] overflow-auto">
<div className="h-[calc(100vh-28rem)] overflow-auto">
<SecretSyncConnectionField
onChange={() => {
setValue("destinationConfig.projectId", "");
@@ -175,7 +173,7 @@ export const GitLabSyncFields = () => {
helperText={
<Tooltip
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>
<span>Don&#39;t see the project you&#39;re looking for?</span>{" "}
@@ -229,7 +227,7 @@ export const GitLabSyncFields = () => {
render={({ field: { onChange, value } }) => (
<SecretProtectionOption
id="should-protect-secrets"
title="Mark Infisical secrets in GitLab as 'Protected' secrets"
title="Mark secrets as Protected"
isEnabled={value || false}
onChange={onChange}
/>
@@ -242,7 +240,7 @@ export const GitLabSyncFields = () => {
render={({ field: { onChange, value } }) => (
<SecretProtectionOption
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."
isEnabled={value || false}
onChange={(checked) => {
@@ -262,7 +260,7 @@ export const GitLabSyncFields = () => {
<div className="max-h-32 opacity-100 transition-all duration-300">
<SecretProtectionOption
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."
isEnabled={value || false}
onChange={onChange}

View File

@@ -6,7 +6,7 @@ import { SecretSync } from "@app/hooks/api/secretSyncs";
export const GitLabSyncReviewFields = () => {
const { watch } = useFormContext<TSecretSyncForm & { destination: SecretSync.GitLab }>();
const projectId = watch("destinationConfig.projectId");
const projectName = watch("destinationConfig.projectName");
const targetEnvironment = watch("destinationConfig.targetEnvironment");
const groupId = watch("destinationConfig.groupId");
const scope = watch("destinationConfig.scope");
@@ -17,7 +17,7 @@ export const GitLabSyncReviewFields = () => {
return (
<>
<GenericFieldLabel label="Scope">{scope}</GenericFieldLabel>
<GenericFieldLabel label="Project ID">{projectId}</GenericFieldLabel>
<GenericFieldLabel label="Project Name">{projectName}</GenericFieldLabel>
{groupId && <GenericFieldLabel label="Group ID">{groupId}</GenericFieldLabel>}
{targetEnvironment && (
<GenericFieldLabel label="Environment">{targetEnvironment}</GenericFieldLabel>

View File

@@ -75,7 +75,7 @@ export const SECRET_SYNC_MAP: Record<SecretSync, { name: string; image: string }
image: "Flyio.svg"
},
[SecretSync.GitLab]: {
name: "Gitlab",
name: "GitLab",
image: "GitLab.png"
}
};

View File

@@ -7,3 +7,8 @@ export type TGitLabGroup = {
id: string;
name: string;
};
export enum GitLabAccessTokenType {
Personal = "personal",
Project = "project"
}

View File

@@ -1,6 +1,8 @@
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
import { GitLabAccessTokenType } from "../gitlab";
export enum GitlabConnectionMethod {
AccessToken = "access-token",
OAuth = "oauth"
@@ -12,6 +14,7 @@ export type TGitlabConnection = TRootAppConnection & { app: AppConnection.Gitlab
credentials: {
instanceUrl?: string;
accessToken: string;
accessTokenType: GitLabAccessTokenType;
};
}
| {

View File

@@ -20,6 +20,7 @@ import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/
import { isInfisicalCloud } from "@app/helpers/platform";
import { useGetAppConnectionOption } from "@app/hooks/api/appConnections";
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import { GitLabAccessTokenType } from "@app/hooks/api/appConnections/gitlab";
import {
GitlabConnectionMethod,
TGitlabConnection
@@ -41,6 +42,7 @@ const formSchema = z.discriminatedUnion("method", [
method: z.literal(GitlabConnectionMethod.AccessToken),
credentials: z.object({
accessToken: z.string().min(1, "Access token is required"),
accessTokenType: z.nativeEnum(GitLabAccessTokenType),
instanceUrl: z
.string()
.trim()
@@ -90,6 +92,7 @@ export const GitLabConnectionForm = ({ appConnection, onSubmit: formSubmit }: Pr
method: GitlabConnectionMethod.AccessToken,
credentials: {
accessToken: "",
accessTokenType: GitLabAccessTokenType.Personal,
instanceUrl: ""
}
} as FormData))
@@ -151,7 +154,7 @@ export const GitLabConnectionForm = ({ appConnection, onSubmit: formSubmit }: Pr
break;
default:
throw new Error("Unhandled Gitlab Connection method");
throw new Error("Unhandled GitLab Connection method");
}
} catch (error) {
console.error("Error handling form submission:", error);
@@ -169,7 +172,7 @@ export const GitLabConnectionForm = ({ appConnection, onSubmit: formSubmit }: Pr
isMissingConfig = false;
break;
default:
throw new Error(`Unhandled Gitlab Connection method: ${selectedMethod}`);
throw new Error(`Unhandled GitLab Connection method: ${selectedMethod}`);
}
const methodDetails = getAppConnectionMethodDetails(selectedMethod);
@@ -211,7 +214,7 @@ export const GitLabConnectionForm = ({ appConnection, onSubmit: formSubmit }: Pr
? `Environment variables have not been configured. ${
isInfisicalCloud()
? "Please contact Infisical."
: `See Docs to configure Gitlab ${methodDetails.name} Connections.`
: `See Docs to configure GitLab ${methodDetails.name} Connections.`
}`
: error?.message
}
@@ -245,24 +248,59 @@ export const GitLabConnectionForm = ({ appConnection, onSubmit: formSubmit }: Pr
/>
{selectedMethod === GitlabConnectionMethod.AccessToken && (
<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>
)}
/>
<>
<Controller
name="credentials.accessTokenType"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Access Token Type"
>
<Select
isDisabled={isUpdate}
value={value}
onValueChange={(val) => {
onChange(val);
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">
@@ -280,10 +318,10 @@ export const GitLabConnectionForm = ({ appConnection, onSubmit: formSubmit }: Pr
}
>
{isRedirecting && selectedMethod === GitlabConnectionMethod.OAuth
? "Redirecting to Gitlab..."
? "Redirecting to GitLab..."
: isUpdate
? "Reconnect to Gitlab"
: "Connect to Gitlab"}
? "Reconnect to GitLab"
: "Connect to GitLab"}
</Button>
<ModalClose asChild>
<Button colorSchema="secondary" variant="plain">