mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat(secret-sync): add Gitlab PR comments suggestions
This commit is contained in:
@@ -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."
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,3 +2,8 @@ export enum GitLabConnectionMethod {
|
||||
OAuth = "oauth",
|
||||
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({
|
||||
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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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"
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||

|
||||
</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.
|
||||

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

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

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

|
||||
</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
|
||||
|
||||

|
||||
<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
|
||||
|
||||

|
||||
</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
|
||||

|
||||
</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
|
||||
|
||||

|
||||

|
||||
</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.
|
||||
|
||||

|
||||
|
||||
<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
|
||||

|
||||
</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.
|
||||

|
||||
</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.
|
||||

|
||||
</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>
|
||||
|
||||
@@ -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
|
||||

|
||||
</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.
|
||||

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

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

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

|
||||
</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.
|
||||

|
||||
</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.
|
||||

|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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**.
|
||||

|
||||
|
||||
- **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**.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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't see the project you'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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,3 +7,8 @@ export type TGitLabGroup = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export enum GitLabAccessTokenType {
|
||||
Personal = "personal",
|
||||
Project = "project"
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
| {
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user