mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 21:51:56 +00:00
Merge pull request #3835 from Infisical/feat/gitlabSecretSync
feat(secret-sync): Add gitlab secret sync
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import { TSecretSyncListItem } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
export const GITLAB_SYNC_LIST_OPTION: TSecretSyncListItem = {
|
||||
name: "GitLab",
|
||||
destination: SecretSync.GitLab,
|
||||
connection: AppConnection.GitLab,
|
||||
canImportSecrets: false
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
export enum GitLabSyncScope {
|
||||
Project = "project",
|
||||
Group = "group"
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import { GitbeakerRequestError } from "@gitbeaker/rest";
|
||||
|
||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||
import {
|
||||
getGitLabClient,
|
||||
GitLabConnectionMethod,
|
||||
refreshGitLabToken,
|
||||
TGitLabConnection
|
||||
} from "@app/services/app-connection/gitlab";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
import { TGitLabSyncWithCredentials, TGitLabVariable } from "@app/services/secret-sync/gitlab/gitlab-sync-types";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
import { SECRET_SYNC_NAME_MAP } from "../secret-sync-maps";
|
||||
import { GitLabSyncScope } from "./gitlab-sync-enums";
|
||||
|
||||
interface TGitLabVariablePayload {
|
||||
key?: string;
|
||||
value: string;
|
||||
variable_type?: "env_var" | "file";
|
||||
environment_scope?: string;
|
||||
protected?: boolean;
|
||||
masked?: boolean;
|
||||
masked_and_hidden?: boolean;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
interface TGitLabVariableCreate extends TGitLabVariablePayload {
|
||||
key: string;
|
||||
}
|
||||
|
||||
interface TGitLabVariableUpdate extends Omit<TGitLabVariablePayload, "key"> {}
|
||||
|
||||
type TGitLabSyncFactoryDeps = {
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "updateById">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
};
|
||||
|
||||
const getValidAccessToken = async (
|
||||
connection: TGitLabConnection,
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "updateById">,
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
||||
): Promise<string> => {
|
||||
if (
|
||||
connection.method === GitLabConnectionMethod.OAuth &&
|
||||
connection.credentials.refreshToken &&
|
||||
new Date(connection.credentials.expiresAt) < new Date()
|
||||
) {
|
||||
const accessToken = await refreshGitLabToken(
|
||||
connection.credentials.refreshToken,
|
||||
connection.id,
|
||||
connection.orgId,
|
||||
appConnectionDAL,
|
||||
kmsService,
|
||||
connection.credentials.instanceUrl
|
||||
);
|
||||
return accessToken;
|
||||
}
|
||||
return connection.credentials.accessToken;
|
||||
};
|
||||
|
||||
const getGitLabVariables = async ({
|
||||
accessToken,
|
||||
connection,
|
||||
scope,
|
||||
resourceId,
|
||||
targetEnvironment
|
||||
}: {
|
||||
accessToken: string;
|
||||
connection: TGitLabConnection;
|
||||
scope: GitLabSyncScope;
|
||||
resourceId: string;
|
||||
targetEnvironment?: string;
|
||||
}): Promise<TGitLabVariable[]> => {
|
||||
try {
|
||||
const client = await getGitLabClient(
|
||||
accessToken,
|
||||
connection.credentials.instanceUrl,
|
||||
connection.method === GitLabConnectionMethod.OAuth
|
||||
);
|
||||
|
||||
let variables: TGitLabVariable[] = [];
|
||||
|
||||
if (scope === GitLabSyncScope.Project) {
|
||||
variables = await client.ProjectVariables.all(resourceId);
|
||||
} else {
|
||||
variables = await client.GroupVariables.all(resourceId);
|
||||
}
|
||||
|
||||
if (targetEnvironment) {
|
||||
variables = variables.filter((v) => v.environmentScope === targetEnvironment);
|
||||
}
|
||||
|
||||
return variables;
|
||||
} catch (error) {
|
||||
if (error instanceof GitbeakerRequestError) {
|
||||
throw new SecretSyncError({
|
||||
error: new Error(
|
||||
`Failed to fetch variables: ${error.message ?? "Unknown error"}${error.cause?.description && error.message !== "Unauthorized" ? `. Cause: ${error.cause.description}` : ""}`
|
||||
)
|
||||
});
|
||||
}
|
||||
throw new SecretSyncError({
|
||||
error
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const createGitLabVariable = async ({
|
||||
accessToken,
|
||||
connection,
|
||||
scope,
|
||||
resourceId,
|
||||
variable
|
||||
}: {
|
||||
accessToken: string;
|
||||
connection: TGitLabConnection;
|
||||
scope: GitLabSyncScope;
|
||||
resourceId: string;
|
||||
variable: TGitLabVariableCreate;
|
||||
}): Promise<void> => {
|
||||
try {
|
||||
const client = await getGitLabClient(
|
||||
accessToken,
|
||||
connection.credentials.instanceUrl,
|
||||
connection.method === GitLabConnectionMethod.OAuth
|
||||
);
|
||||
|
||||
const payload = {
|
||||
key: variable.key,
|
||||
value: variable.value,
|
||||
variableType: "env_var",
|
||||
environmentScope: variable.environment_scope || "*",
|
||||
protected: variable.protected || false,
|
||||
masked: variable.masked || false,
|
||||
masked_and_hidden: variable.masked_and_hidden || false,
|
||||
raw: false
|
||||
};
|
||||
|
||||
if (scope === GitLabSyncScope.Project) {
|
||||
await client.ProjectVariables.create(resourceId, payload.key, payload.value, {
|
||||
variableType: "env_var",
|
||||
environmentScope: payload.environmentScope,
|
||||
protected: payload.protected,
|
||||
masked: payload.masked,
|
||||
masked_and_hidden: payload.masked_and_hidden,
|
||||
raw: false
|
||||
});
|
||||
} else {
|
||||
await client.GroupVariables.create(resourceId, payload.key, payload.value, {
|
||||
variableType: "env_var",
|
||||
environmentScope: payload.environmentScope,
|
||||
protected: payload.protected,
|
||||
masked: payload.masked,
|
||||
...(payload.masked_and_hidden && { masked_and_hidden: payload.masked_and_hidden }),
|
||||
raw: false
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof GitbeakerRequestError) {
|
||||
throw new SecretSyncError({
|
||||
error: new Error(
|
||||
`Failed to create variable: ${error.message ?? "Unknown error"}${error.cause?.description && error.message !== "Unauthorized" ? `. Cause: ${error.cause.description}` : ""}`
|
||||
),
|
||||
secretKey: variable.key
|
||||
});
|
||||
}
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: variable.key
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const updateGitLabVariable = async ({
|
||||
accessToken,
|
||||
connection,
|
||||
scope,
|
||||
resourceId,
|
||||
key,
|
||||
variable,
|
||||
targetEnvironment
|
||||
}: {
|
||||
accessToken: string;
|
||||
connection: TGitLabConnection;
|
||||
scope: GitLabSyncScope;
|
||||
resourceId: string;
|
||||
key: string;
|
||||
variable: TGitLabVariableUpdate;
|
||||
targetEnvironment?: string;
|
||||
}): Promise<void> => {
|
||||
try {
|
||||
const client = await getGitLabClient(
|
||||
accessToken,
|
||||
connection.credentials.instanceUrl,
|
||||
connection.method === GitLabConnectionMethod.OAuth
|
||||
);
|
||||
|
||||
const options = {
|
||||
...(variable.environment_scope && { environmentScope: variable.environment_scope }),
|
||||
...(variable.protected !== undefined && { protected: variable.protected }),
|
||||
...(variable.masked !== undefined && { masked: variable.masked })
|
||||
};
|
||||
|
||||
if (targetEnvironment) {
|
||||
options.environmentScope = targetEnvironment;
|
||||
}
|
||||
|
||||
if (scope === GitLabSyncScope.Project) {
|
||||
await client.ProjectVariables.edit(resourceId, key, variable.value, {
|
||||
...options,
|
||||
filter: { environment_scope: targetEnvironment || "*" }
|
||||
});
|
||||
} else {
|
||||
await client.GroupVariables.edit(resourceId, key, variable.value, {
|
||||
...options,
|
||||
filter: { environment_scope: targetEnvironment || "*" }
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof GitbeakerRequestError) {
|
||||
throw new SecretSyncError({
|
||||
error: new Error(
|
||||
`Failed to update variable: ${error.message ?? "Unknown error"}${error.cause?.description && error.message !== "Unauthorized" ? `. Cause: ${error.cause.description}` : ""}`
|
||||
),
|
||||
secretKey: key
|
||||
});
|
||||
}
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: key
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const deleteGitLabVariable = async ({
|
||||
accessToken,
|
||||
connection,
|
||||
scope,
|
||||
resourceId,
|
||||
key,
|
||||
targetEnvironment,
|
||||
allVariables
|
||||
}: {
|
||||
accessToken: string;
|
||||
connection: TGitLabConnection;
|
||||
scope: GitLabSyncScope;
|
||||
resourceId: string;
|
||||
key: string;
|
||||
targetEnvironment?: string;
|
||||
allVariables?: TGitLabVariable[];
|
||||
}): Promise<void> => {
|
||||
if (allVariables && !allVariables.find((v) => v.key === key)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const client = await getGitLabClient(
|
||||
accessToken,
|
||||
connection.credentials.instanceUrl,
|
||||
connection.method === GitLabConnectionMethod.OAuth
|
||||
);
|
||||
|
||||
const options: { filter?: { environment_scope: string } } = {};
|
||||
if (targetEnvironment) {
|
||||
options.filter = { environment_scope: targetEnvironment || "*" };
|
||||
}
|
||||
|
||||
if (scope === GitLabSyncScope.Project) {
|
||||
await client.ProjectVariables.remove(resourceId, key, options);
|
||||
} else {
|
||||
await client.GroupVariables.remove(resourceId, key);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof GitbeakerRequestError) {
|
||||
throw new SecretSyncError({
|
||||
error: new Error(
|
||||
`Failed to delete variable: ${error.message ?? "Unknown error"}${error.cause?.description && error.message !== "Unauthorized" ? `. Cause: ${error.cause.description}` : ""}`
|
||||
),
|
||||
secretKey: key
|
||||
});
|
||||
}
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: key
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const GitLabSyncFns = {
|
||||
syncSecrets: async (
|
||||
secretSync: TGitLabSyncWithCredentials,
|
||||
secretMap: TSecretMap,
|
||||
{ appConnectionDAL, kmsService }: TGitLabSyncFactoryDeps
|
||||
): Promise<void> => {
|
||||
const { connection, environment, destinationConfig } = secretSync;
|
||||
const { scope, targetEnvironment } = destinationConfig;
|
||||
|
||||
const resourceId = scope === GitLabSyncScope.Project ? destinationConfig.projectId : destinationConfig.groupId;
|
||||
|
||||
const accessToken = await getValidAccessToken(connection, appConnectionDAL, kmsService);
|
||||
|
||||
try {
|
||||
const currentVariables = await getGitLabVariables({
|
||||
accessToken,
|
||||
connection,
|
||||
scope,
|
||||
resourceId,
|
||||
targetEnvironment
|
||||
});
|
||||
|
||||
const currentVariableMap = new Map(currentVariables.map((v) => [v.key, v]));
|
||||
|
||||
for (const [key, { value }] of Object.entries(secretMap)) {
|
||||
if (value?.length < 8 && destinationConfig.shouldMaskSecrets) {
|
||||
throw new SecretSyncError({
|
||||
message: `Secret ${key} is too short to be masked. GitLab requires a minimum of 8 characters for masked secrets.`,
|
||||
secretKey: key
|
||||
});
|
||||
}
|
||||
try {
|
||||
const existingVariable = currentVariableMap.get(key);
|
||||
|
||||
if (existingVariable) {
|
||||
if (
|
||||
existingVariable.value !== value ||
|
||||
existingVariable.environmentScope !== targetEnvironment ||
|
||||
existingVariable.protected !== destinationConfig.shouldProtectSecrets ||
|
||||
existingVariable.masked !== destinationConfig.shouldMaskSecrets
|
||||
) {
|
||||
await updateGitLabVariable({
|
||||
accessToken,
|
||||
connection,
|
||||
scope,
|
||||
resourceId,
|
||||
key,
|
||||
variable: {
|
||||
value,
|
||||
environment_scope: targetEnvironment,
|
||||
protected: destinationConfig.shouldProtectSecrets,
|
||||
masked: destinationConfig.shouldMaskSecrets || existingVariable.hidden
|
||||
},
|
||||
targetEnvironment
|
||||
});
|
||||
}
|
||||
} else {
|
||||
await createGitLabVariable({
|
||||
accessToken,
|
||||
connection,
|
||||
scope,
|
||||
resourceId,
|
||||
variable: {
|
||||
key,
|
||||
value,
|
||||
variable_type: "env_var",
|
||||
environment_scope: targetEnvironment || "*",
|
||||
protected: destinationConfig.shouldProtectSecrets || false,
|
||||
masked: destinationConfig.shouldMaskSecrets || false,
|
||||
masked_and_hidden: destinationConfig.shouldHideSecrets || false
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: key
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!secretSync.syncOptions.disableSecretDeletion) {
|
||||
for (const variable of currentVariables) {
|
||||
try {
|
||||
const shouldDelete =
|
||||
matchesSchema(variable.key, environment?.slug || "", secretSync.syncOptions.keySchema) &&
|
||||
!(variable.key in secretMap);
|
||||
|
||||
if (shouldDelete) {
|
||||
await deleteGitLabVariable({
|
||||
accessToken,
|
||||
connection,
|
||||
scope,
|
||||
resourceId,
|
||||
key: variable.key,
|
||||
targetEnvironment
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: variable.key
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof SecretSyncError) {
|
||||
throw error;
|
||||
}
|
||||
throw new SecretSyncError({
|
||||
message: "Failed to sync secrets",
|
||||
error
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
removeSecrets: async (
|
||||
secretSync: TGitLabSyncWithCredentials,
|
||||
secretMap: TSecretMap,
|
||||
{ appConnectionDAL, kmsService }: TGitLabSyncFactoryDeps
|
||||
): Promise<void> => {
|
||||
const { connection, destinationConfig } = secretSync;
|
||||
const { scope, targetEnvironment } = destinationConfig;
|
||||
|
||||
const resourceId = scope === GitLabSyncScope.Project ? destinationConfig.projectId : destinationConfig.groupId;
|
||||
|
||||
const accessToken = await getValidAccessToken(connection, appConnectionDAL, kmsService);
|
||||
|
||||
const allVariables = await getGitLabVariables({
|
||||
accessToken,
|
||||
connection,
|
||||
scope,
|
||||
resourceId,
|
||||
targetEnvironment
|
||||
});
|
||||
|
||||
for (const key of Object.keys(secretMap)) {
|
||||
try {
|
||||
await deleteGitLabVariable({
|
||||
accessToken,
|
||||
connection,
|
||||
scope,
|
||||
resourceId,
|
||||
key,
|
||||
targetEnvironment,
|
||||
allVariables
|
||||
});
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: key
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getSecrets: async (secretSync: TGitLabSyncWithCredentials): Promise<TSecretMap> => {
|
||||
throw new Error(`${SECRET_SYNC_NAME_MAP[secretSync.destination]} does not support importing secrets.`);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,97 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretSyncs } from "@app/lib/api-docs";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import {
|
||||
BaseSecretSyncSchema,
|
||||
GenericCreateSecretSyncFieldsSchema,
|
||||
GenericUpdateSecretSyncFieldsSchema
|
||||
} from "@app/services/secret-sync/secret-sync-schemas";
|
||||
import { TSyncOptionsConfig } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
import { GitLabSyncScope } from "./gitlab-sync-enums";
|
||||
|
||||
const GitLabSyncDestinationConfigSchema = z.discriminatedUnion("scope", [
|
||||
z.object({
|
||||
scope: z.literal(GitLabSyncScope.Project).describe(SecretSyncs.DESTINATION_CONFIG.GITLAB.scope),
|
||||
projectId: z.string().min(1, "Project ID is required").describe(SecretSyncs.DESTINATION_CONFIG.GITLAB.projectId),
|
||||
projectName: z
|
||||
.string()
|
||||
.min(1, "Project name is required")
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.GITLAB.projectName),
|
||||
targetEnvironment: z
|
||||
.string()
|
||||
.optional()
|
||||
.default("*")
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.GITLAB.targetEnvironment),
|
||||
shouldProtectSecrets: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(false)
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.GITLAB.shouldProtectSecrets),
|
||||
shouldMaskSecrets: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(false)
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.GITLAB.shouldMaskSecrets),
|
||||
shouldHideSecrets: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(false)
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.GITLAB.shouldHideSecrets)
|
||||
}),
|
||||
z.object({
|
||||
scope: z.literal(GitLabSyncScope.Group).describe(SecretSyncs.DESTINATION_CONFIG.GITLAB.scope),
|
||||
groupId: z.string().min(1, "Group ID is required").describe(SecretSyncs.DESTINATION_CONFIG.GITLAB.groupId),
|
||||
groupName: z.string().min(1, "Group name is required").describe(SecretSyncs.DESTINATION_CONFIG.GITLAB.groupName),
|
||||
targetEnvironment: z
|
||||
.string()
|
||||
.optional()
|
||||
.default("*")
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.GITLAB.targetEnvironment),
|
||||
shouldProtectSecrets: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(false)
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.GITLAB.shouldProtectSecrets),
|
||||
shouldMaskSecrets: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(false)
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.GITLAB.shouldMaskSecrets),
|
||||
shouldHideSecrets: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(false)
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.GITLAB.shouldHideSecrets)
|
||||
})
|
||||
]);
|
||||
|
||||
const GitLabSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: false };
|
||||
|
||||
export const GitLabSyncSchema = BaseSecretSyncSchema(SecretSync.GitLab, GitLabSyncOptionsConfig).extend({
|
||||
destination: z.literal(SecretSync.GitLab),
|
||||
destinationConfig: GitLabSyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const CreateGitLabSyncSchema = GenericCreateSecretSyncFieldsSchema(
|
||||
SecretSync.GitLab,
|
||||
GitLabSyncOptionsConfig
|
||||
).extend({
|
||||
destinationConfig: GitLabSyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const UpdateGitLabSyncSchema = GenericUpdateSecretSyncFieldsSchema(
|
||||
SecretSync.GitLab,
|
||||
GitLabSyncOptionsConfig
|
||||
).extend({
|
||||
destinationConfig: GitLabSyncDestinationConfigSchema.optional()
|
||||
});
|
||||
|
||||
export const GitLabSyncListItemSchema = z.object({
|
||||
name: z.literal("GitLab"),
|
||||
connection: z.literal(AppConnection.GitLab),
|
||||
destination: z.literal(SecretSync.GitLab),
|
||||
canImportSecrets: z.literal(false)
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { TGitLabConnection } from "@app/services/app-connection/gitlab";
|
||||
|
||||
import { CreateGitLabSyncSchema, GitLabSyncListItemSchema, GitLabSyncSchema } from "./gitlab-sync-schemas";
|
||||
|
||||
export type TGitLabSync = z.infer<typeof GitLabSyncSchema>;
|
||||
export type TGitLabSyncInput = z.infer<typeof CreateGitLabSyncSchema>;
|
||||
export type TGitLabSyncListItem = z.infer<typeof GitLabSyncListItemSchema>;
|
||||
|
||||
export type TGitLabSyncWithCredentials = TGitLabSync & {
|
||||
connection: TGitLabConnection;
|
||||
};
|
||||
|
||||
export type TGitLabVariable = {
|
||||
key: string;
|
||||
value: string;
|
||||
protected: boolean;
|
||||
masked: boolean;
|
||||
environmentScope?: string;
|
||||
hidden?: boolean;
|
||||
};
|
||||
|
||||
export type TGitLabVariableCreate = {
|
||||
key: string;
|
||||
value: string;
|
||||
variable_type?: "env_var" | "file";
|
||||
protected?: boolean;
|
||||
masked?: boolean;
|
||||
raw?: boolean;
|
||||
environment_scope?: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export type TGitLabVariableUpdate = {
|
||||
value: string;
|
||||
variable_type?: "env_var" | "file";
|
||||
protected?: boolean;
|
||||
masked?: boolean;
|
||||
raw?: boolean;
|
||||
environment_scope?: string;
|
||||
description?: string | null;
|
||||
};
|
||||
|
||||
export type TGitLabListVariables = {
|
||||
accessToken: string;
|
||||
projectId: string;
|
||||
environmentScope?: string;
|
||||
};
|
||||
|
||||
export type TGitLabCreateVariable = TGitLabListVariables & {
|
||||
variable: TGitLabVariableCreate;
|
||||
};
|
||||
|
||||
export type TGitLabUpdateVariable = TGitLabListVariables & {
|
||||
key: string;
|
||||
variable: TGitLabVariableUpdate;
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from "./gitlab-sync-constants";
|
||||
export * from "./gitlab-sync-fns";
|
||||
export * from "./gitlab-sync-schemas";
|
||||
export * from "./gitlab-sync-types";
|
||||
@@ -19,6 +19,7 @@ export enum SecretSync {
|
||||
Heroku = "heroku",
|
||||
Render = "render",
|
||||
Flyio = "flyio",
|
||||
GitLab = "gitlab",
|
||||
CloudflarePages = "cloudflare-pages"
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import { CloudflarePagesSyncFns } from "./cloudflare-pages/cloudflare-pages-fns"
|
||||
import { FLYIO_SYNC_LIST_OPTION, FlyioSyncFns } from "./flyio";
|
||||
import { GCP_SYNC_LIST_OPTION } from "./gcp";
|
||||
import { GcpSyncFns } from "./gcp/gcp-sync-fns";
|
||||
import { GITLAB_SYNC_LIST_OPTION, GitLabSyncFns } from "./gitlab";
|
||||
import { HC_VAULT_SYNC_LIST_OPTION, HCVaultSyncFns } from "./hc-vault";
|
||||
import { HEROKU_SYNC_LIST_OPTION, HerokuSyncFns } from "./heroku";
|
||||
import { HUMANITEC_SYNC_LIST_OPTION } from "./humanitec";
|
||||
@@ -66,6 +67,7 @@ const SECRET_SYNC_LIST_OPTIONS: Record<SecretSync, TSecretSyncListItem> = {
|
||||
[SecretSync.Heroku]: HEROKU_SYNC_LIST_OPTION,
|
||||
[SecretSync.Render]: RENDER_SYNC_LIST_OPTION,
|
||||
[SecretSync.Flyio]: FLYIO_SYNC_LIST_OPTION,
|
||||
[SecretSync.GitLab]: GITLAB_SYNC_LIST_OPTION,
|
||||
[SecretSync.CloudflarePages]: CLOUDFLARE_PAGES_SYNC_LIST_OPTION
|
||||
};
|
||||
|
||||
@@ -230,6 +232,8 @@ export const SecretSyncFns = {
|
||||
return RenderSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Flyio:
|
||||
return FlyioSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.GitLab:
|
||||
return GitLabSyncFns.syncSecrets(secretSync, schemaSecretMap, { appConnectionDAL, kmsService });
|
||||
case SecretSync.CloudflarePages:
|
||||
return CloudflarePagesSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
default:
|
||||
@@ -318,6 +322,9 @@ export const SecretSyncFns = {
|
||||
case SecretSync.Flyio:
|
||||
secretMap = await FlyioSyncFns.getSecrets(secretSync);
|
||||
break;
|
||||
case SecretSync.GitLab:
|
||||
secretMap = await GitLabSyncFns.getSecrets(secretSync);
|
||||
break;
|
||||
case SecretSync.CloudflarePages:
|
||||
secretMap = await CloudflarePagesSyncFns.getSecrets(secretSync);
|
||||
break;
|
||||
@@ -394,6 +401,8 @@ export const SecretSyncFns = {
|
||||
return RenderSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Flyio:
|
||||
return FlyioSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.GitLab:
|
||||
return GitLabSyncFns.removeSecrets(secretSync, schemaSecretMap, { appConnectionDAL, kmsService });
|
||||
case SecretSync.CloudflarePages:
|
||||
return CloudflarePagesSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
default:
|
||||
|
||||
@@ -22,6 +22,7 @@ export const SECRET_SYNC_NAME_MAP: Record<SecretSync, string> = {
|
||||
[SecretSync.Heroku]: "Heroku",
|
||||
[SecretSync.Render]: "Render",
|
||||
[SecretSync.Flyio]: "Fly.io",
|
||||
[SecretSync.GitLab]: "GitLab",
|
||||
[SecretSync.CloudflarePages]: "Cloudflare Pages"
|
||||
};
|
||||
|
||||
@@ -46,6 +47,7 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||
[SecretSync.Heroku]: AppConnection.Heroku,
|
||||
[SecretSync.Render]: AppConnection.Render,
|
||||
[SecretSync.Flyio]: AppConnection.Flyio,
|
||||
[SecretSync.GitLab]: AppConnection.GitLab,
|
||||
[SecretSync.CloudflarePages]: AppConnection.Cloudflare
|
||||
};
|
||||
|
||||
@@ -70,5 +72,6 @@ export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
|
||||
[SecretSync.Heroku]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Render]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Flyio]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.GitLab]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.CloudflarePages]: SecretSyncPlanType.Regular
|
||||
};
|
||||
|
||||
@@ -72,8 +72,15 @@ import {
|
||||
TAzureKeyVaultSyncListItem,
|
||||
TAzureKeyVaultSyncWithCredentials
|
||||
} from "./azure-key-vault";
|
||||
import {
|
||||
TCloudflarePagesSync,
|
||||
TCloudflarePagesSyncInput,
|
||||
TCloudflarePagesSyncListItem,
|
||||
TCloudflarePagesSyncWithCredentials
|
||||
} from "./cloudflare-pages/cloudflare-pages-types";
|
||||
import { TFlyioSync, TFlyioSyncInput, TFlyioSyncListItem, TFlyioSyncWithCredentials } from "./flyio/flyio-sync-types";
|
||||
import { TGcpSync, TGcpSyncInput, TGcpSyncListItem, TGcpSyncWithCredentials } from "./gcp";
|
||||
import { TGitLabSync, TGitLabSyncInput, TGitLabSyncListItem, TGitLabSyncWithCredentials } from "./gitlab";
|
||||
import {
|
||||
THCVaultSync,
|
||||
THCVaultSyncInput,
|
||||
@@ -106,12 +113,6 @@ import {
|
||||
TTerraformCloudSyncWithCredentials
|
||||
} from "./terraform-cloud";
|
||||
import { TVercelSync, TVercelSyncInput, TVercelSyncListItem, TVercelSyncWithCredentials } from "./vercel";
|
||||
import {
|
||||
TCloudflarePagesSync,
|
||||
TCloudflarePagesSyncInput,
|
||||
TCloudflarePagesSyncListItem,
|
||||
TCloudflarePagesSyncWithCredentials
|
||||
} from "./cloudflare-pages/cloudflare-pages-types";
|
||||
|
||||
export type TSecretSync =
|
||||
| TAwsParameterStoreSync
|
||||
@@ -134,6 +135,7 @@ export type TSecretSync =
|
||||
| THerokuSync
|
||||
| TRenderSync
|
||||
| TFlyioSync
|
||||
| TGitLabSync
|
||||
| TCloudflarePagesSync;
|
||||
|
||||
export type TSecretSyncWithCredentials =
|
||||
@@ -157,6 +159,7 @@ export type TSecretSyncWithCredentials =
|
||||
| THerokuSyncWithCredentials
|
||||
| TRenderSyncWithCredentials
|
||||
| TFlyioSyncWithCredentials
|
||||
| TGitLabSyncWithCredentials
|
||||
| TCloudflarePagesSyncWithCredentials;
|
||||
|
||||
export type TSecretSyncInput =
|
||||
@@ -180,6 +183,7 @@ export type TSecretSyncInput =
|
||||
| THerokuSyncInput
|
||||
| TRenderSyncInput
|
||||
| TFlyioSyncInput
|
||||
| TGitLabSyncInput
|
||||
| TCloudflarePagesSyncInput;
|
||||
|
||||
export type TSecretSyncListItem =
|
||||
@@ -203,6 +207,7 @@ export type TSecretSyncListItem =
|
||||
| THerokuSyncListItem
|
||||
| TRenderSyncListItem
|
||||
| TFlyioSyncListItem
|
||||
| TGitLabSyncListItem
|
||||
| TCloudflarePagesSyncListItem;
|
||||
|
||||
export type TSyncOptionsConfig = {
|
||||
|
||||
Reference in New Issue
Block a user