feat: secret variables in environments (#3779)

Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com>
This commit is contained in:
Nivedin
2024-02-08 21:58:42 +05:30
committed by GitHub
parent 16803acb26
commit 00862eb192
55 changed files with 2141 additions and 439 deletions

View File

@@ -2,22 +2,38 @@ import * as E from "fp-ts/Either"
import { pipe } from "fp-ts/function"
import { InferredEntity, createVersionedEntity } from "verzod"
import { z } from "zod"
import V0_VERSION from "./v/0"
import V1_VERSION from "./v/1"
const versionedObject = z.object({
v: z.number(),
})
export const Environment = createVersionedEntity({
latestVersion: 0,
latestVersion: 1,
versionMap: {
0: V0_VERSION
0: V0_VERSION,
1: V1_VERSION,
},
getVersion(data) {
const versionCheck = versionedObject.safeParse(data)
if (versionCheck.success) return versionCheck.data.v
// For V0 we have to check the schema
const result = V0_VERSION.schema.safeParse(data)
return result.success ? 0 : null
},
getVersion(x) {
return V0_VERSION.schema.safeParse(x).success
? 0
: null
}
})
export type Environment = InferredEntity<typeof Environment>
export type EnvironmentVariable = InferredEntity<
typeof Environment
>["variables"][number]
const REGEX_ENV_VAR = /<<([^>]*)>>/g // "<<myVariable>>"
/**
@@ -31,6 +47,8 @@ const ENV_MAX_EXPAND_LIMIT = 10
*/
const ENV_EXPAND_LOOP = "ENV_EXPAND_LOOP" as const
export const EnvironmentSchemaVersion = 1
export function parseBodyEnvVariablesE(
body: string,
env: Environment["variables"]
@@ -43,7 +61,11 @@ export function parseBodyEnvVariablesE(
const found = env.find(
(envVar) => envVar.key === key.replace(/[<>]/g, "")
)
return found ? found.value : key
if (found && "value" in found) {
return found.value
}
return key
})
depth++
@@ -68,7 +90,10 @@ export const parseBodyEnvVariables = (
export function parseTemplateStringE(
str: string,
variables: Environment["variables"]
variables:
| Environment["variables"]
| { secret: true; value: string; key: string }[],
maskValue = false
) {
if (!variables || !str) {
return E.right(str)
@@ -78,10 +103,21 @@ export function parseTemplateStringE(
let depth = 0
while (result.match(REGEX_ENV_VAR) != null && depth <= ENV_MAX_EXPAND_LIMIT) {
result = decodeURI(encodeURI(result)).replace(
REGEX_ENV_VAR,
(_, p1) => variables.find((x) => x.key === p1)?.value || ""
)
result = decodeURI(encodeURI(result)).replace(REGEX_ENV_VAR, (_, p1) => {
const variable = variables.find((x) => x && x.key === p1)
if (variable && "value" in variable) {
// Mask the value if it is a secret and explicitly specified
if (variable.secret && maskValue) {
return "*".repeat(
(variable as { secret: true; value: string; key: string }).value
.length
)
}
return variable.value
}
return ""
})
depth++
}
@@ -90,14 +126,52 @@ export function parseTemplateStringE(
: E.right(result)
}
export type NonSecretEnvironmentVariable = Extract<
EnvironmentVariable,
{ secret: false }
>
export type NonSecretEnvironment = Omit<Environment, "variables"> & {
variables: NonSecretEnvironmentVariable[]
}
/**
* @deprecated Use `parseTemplateStringE` instead
*/
export const parseTemplateString = (
str: string,
variables: Environment["variables"]
variables:
| Environment["variables"]
| { secret: true; value: string; key: string }[],
maskValue = false
) =>
pipe(
parseTemplateStringE(str, variables),
parseTemplateStringE(str, variables, maskValue),
E.getOrElse(() => str)
)
export const translateToNewEnvironmentVariables = (
x: any
): Environment["variables"][number] => {
return {
key: x.key,
value: x.value,
secret: false,
}
}
export const translateToNewEnvironment = (x: any): Environment => {
if (x.v && x.v === EnvironmentSchemaVersion) return x
// Legacy
const id = x.id ?? ""
const name = x.name ?? "Untitled"
const variables = (x.variables ?? []).map(translateToNewEnvironmentVariables)
return {
v: EnvironmentSchemaVersion,
id,
name,
variables,
}
}

View File

@@ -9,10 +9,10 @@ export const V0_SCHEMA = z.object({
key: z.string(),
value: z.string(),
})
)
),
})
export default defineVersion({
initial: true,
schema: V0_SCHEMA
schema: V0_SCHEMA,
})

View File

@@ -0,0 +1,42 @@
import { z } from "zod"
import { defineVersion } from "verzod"
import { V0_SCHEMA } from "./0"
export const V1_SCHEMA = z.object({
v: z.literal(1),
id: z.string(),
name: z.string(),
variables: z.array(
z.union([
z.object({
key: z.string(),
secret: z.literal(true),
}),
z.object({
key: z.string(),
value: z.string(),
secret: z.literal(false),
}),
])
),
})
export default defineVersion({
initial: false,
schema: V1_SCHEMA,
up(old: z.infer<typeof V0_SCHEMA>) {
const result: z.infer<typeof V1_SCHEMA> = {
...old,
v: 1,
id: old.id ?? "",
variables: old.variables.map((variable) => {
return {
...variable,
secret: false,
}
}),
}
return result
},
})