-
-
Value Sets
-
-
-
-
-
-
-
-
- Variable conflicts and precedence
-
- Conflicts occur when one or more variables applied to a
- workspace have the same type and the same key.
- Workspace-specific variables always overwrite conflicting
- variables from variable sets. When different variable sets
- contain conflicts, HCP Terraform prioritizes them first based
- on the variable set scope and then by the lexical precedence
- of the variable set name. Learn more about variable precedence
-
-
-
- {variableSet.map((variableSet) => (
-
-
-
- {variableSet.name}
-
- {variableSet.description ?? "Add a description..."}
-
-
-
-
- ))}
-
+ {variableSets.length === 0 && (
+
+ )}
+ {variableSets.length !== 0 && (
+
+
+
Variable Sets
+
+
+
- >
+
+
+
)}
>
);
diff --git a/packages/api/src/router/system.ts b/packages/api/src/router/system.ts
index 7a27dce2c..2070aa18d 100644
--- a/packages/api/src/router/system.ts
+++ b/packages/api/src/router/system.ts
@@ -118,6 +118,25 @@ export const systemRouter = createTRPCRouter({
})),
),
+ byId: protectedProcedure
+ .meta({
+ authorizationCheck: ({ canUser, input }) =>
+ canUser.perform(Permission.SystemGet).on({ type: "system", id: input }),
+ })
+ .input(z.string().uuid())
+ .query(({ ctx: { db }, input }) =>
+ db
+ .select()
+ .from(system)
+ .innerJoin(workspace, eq(system.workspaceId, workspace.id))
+ .leftJoin(environment, eq(environment.systemId, system.id))
+ .where(eq(system.id, input))
+ .then((rows) => ({
+ ...rows[0]!.system,
+ environments: rows.map((r) => r.environment).filter(isPresent),
+ })),
+ ),
+
create: protectedProcedure
.meta({
authorizationCheck: ({ canUser, input }) =>
diff --git a/packages/api/src/router/variable-set.ts b/packages/api/src/router/variable-set.ts
index 3d9ec9d54..e0a1d9814 100644
--- a/packages/api/src/router/variable-set.ts
+++ b/packages/api/src/router/variable-set.ts
@@ -1,8 +1,6 @@
-import _ from "lodash";
-import { isPresent } from "ts-is-present";
import { z } from "zod";
-import { eq, takeFirst } from "@ctrlplane/db";
+import { asc, eq, takeFirst } from "@ctrlplane/db";
import "@ctrlplane/db/schema";
@@ -10,6 +8,7 @@ import {
createVariableSet,
updateVariableSet,
variableSet,
+ variableSetAssignment,
variableSetValue,
} from "@ctrlplane/db/schema";
import { Permission } from "@ctrlplane/validators/auth";
@@ -23,27 +22,12 @@ export const variableSetRouter = createTRPCRouter({
canUser.perform(Permission.SystemGet).on({ type: "system", id: input }),
})
.input(z.string().uuid())
- .query(async ({ ctx, input }) =>
- ctx.db
- .select()
- .from(variableSet)
- .leftJoin(
- variableSetValue,
- eq(variableSetValue.variableSetId, variableSet.id),
- )
- .where(eq(variableSet.systemId, input))
- .then((rows) =>
- _.chain(rows)
- .groupBy((r) => r.variable_set.id)
- .entries()
- .map(([, rows]) => ({
- ...rows[0]!.variable_set,
- values: rows
- .map((row) => row.variable_set_value)
- .filter(isPresent),
- }))
- .value(),
- ),
+ .query(({ ctx, input }) =>
+ ctx.db.query.variableSet.findMany({
+ where: eq(variableSet.systemId, input),
+ with: { values: true, assignments: { with: { environment: true } } },
+ orderBy: [asc(variableSet.name)],
+ }),
),
byId: protectedProcedure
@@ -55,21 +39,10 @@ export const variableSetRouter = createTRPCRouter({
})
.input(z.string().uuid())
.query(async ({ ctx, input }) =>
- ctx.db
- .select()
- .from(variableSet)
- .leftJoin(
- variableSetValue,
- eq(variableSetValue.variableSetId, variableSet.id),
- )
- .where(eq(variableSet.id, input))
- .then((rows) => {
- if (rows.length === 0) return null;
- return {
- ...rows[0]!.variable_set,
- values: rows.map((row) => row.variable_set_value).filter(isPresent),
- };
- }),
+ ctx.db.query.variableSet.findFirst({
+ where: eq(variableSet.id, input),
+ with: { values: true, assignments: { with: { environment: true } } },
+ }),
),
create: protectedProcedure
@@ -87,27 +60,28 @@ export const variableSetRouter = createTRPCRouter({
.values(input)
.returning()
.then(takeFirst);
- const values = await tx
- .insert(variableSetValue)
- .values(
- input.values.map((value) => ({ ...value, variableSetId: vs.id })),
- )
- .returning();
- return { ...vs, values };
+ await tx.insert(variableSetValue).values(
+ input.values.map((value) => ({
+ key: value.key,
+ value: value.value,
+ variableSetId: vs.id,
+ })),
+ );
+ if (input.environmentIds.length > 0)
+ await tx.insert(variableSetAssignment).values(
+ input.environmentIds.map((environmentId) => ({
+ variableSetId: vs.id,
+ environmentId,
+ })),
+ );
+ return tx.query.variableSet.findFirst({
+ where: eq(variableSet.id, vs.id),
+ with: { values: true, assignments: { with: { environment: true } } },
+ });
}),
),
- delete: protectedProcedure
- .meta({
- authorizationCheck: ({ canUser, input }) =>
- canUser
- .perform(Permission.SystemUpdate)
- .on({ type: "variableSet", id: input }),
- })
- .input(z.string().uuid())
- .mutation(() => {}),
-
- set: protectedProcedure
+ update: protectedProcedure
.meta({
authorizationCheck: ({ canUser, input }) =>
canUser
@@ -117,10 +91,54 @@ export const variableSetRouter = createTRPCRouter({
.input(
z.object({
id: z.string().uuid(),
- data: updateVariableSet.and(
- z.object({ values: z.record(z.string()).optional() }),
- ),
+ data: updateVariableSet,
}),
)
- .mutation(() => {}),
+ .mutation(({ ctx, input }) =>
+ ctx.db.transaction(async (tx) => {
+ await tx
+ .update(variableSet)
+ .set(input.data)
+ .where(eq(variableSet.id, input.id))
+ .returning()
+ .then(takeFirst);
+
+ if (input.data.values != null) {
+ await tx
+ .delete(variableSetValue)
+ .where(eq(variableSetValue.variableSetId, input.id));
+ await tx.insert(variableSetValue).values(
+ input.data.values.map((value) => ({
+ key: value.key,
+ value: value.value,
+ variableSetId: input.id,
+ })),
+ );
+ }
+
+ if (input.data.environmentIds != null) {
+ await tx
+ .delete(variableSetAssignment)
+ .where(eq(variableSetAssignment.variableSetId, input.id));
+ await tx.insert(variableSetAssignment).values(
+ input.data.environmentIds.map((environmentId) => ({
+ variableSetId: input.id,
+ environmentId,
+ })),
+ );
+ }
+ }),
+ ),
+
+ delete: protectedProcedure
+ .meta({
+ authorizationCheck: ({ canUser, input }) =>
+ canUser
+ .perform(Permission.SystemUpdate)
+ .on({ type: "variableSet", id: input }),
+ })
+ .input(z.string().uuid())
+ .mutation(({ ctx, input }) =>
+ ctx.db.delete(variableSet).where(eq(variableSet.id, input)),
+ ),
});
diff --git a/packages/db/drizzle/0016_cute_vengeance.sql b/packages/db/drizzle/0016_cute_vengeance.sql
new file mode 100644
index 000000000..74a05b76a
--- /dev/null
+++ b/packages/db/drizzle/0016_cute_vengeance.sql
@@ -0,0 +1,21 @@
+CREATE TABLE IF NOT EXISTS "variable_set_assignment" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid () NOT NULL,
+ "variable_set_id" uuid NOT NULL,
+ "environment_id" uuid NOT NULL
+);
+--> statement-breakpoint
+ALTER TABLE "variable_set_value" DROP COLUMN "value";
+
+ALTER TABLE "variable_set_value" ADD COLUMN "value" jsonb NOT NULL;
+--> statement-breakpoint
+DO $$ BEGIN
+ ALTER TABLE "variable_set_assignment" ADD CONSTRAINT "variable_set_assignment_variable_set_id_variable_set_id_fk" FOREIGN KEY ("variable_set_id") REFERENCES "public"."variable_set"("id") ON DELETE cascade ON UPDATE no action;
+EXCEPTION
+ WHEN duplicate_object THEN null;
+END $$;
+--> statement-breakpoint
+DO $$ BEGIN
+ ALTER TABLE "variable_set_assignment" ADD CONSTRAINT "variable_set_assignment_environment_id_environment_id_fk" FOREIGN KEY ("environment_id") REFERENCES "public"."environment"("id") ON DELETE cascade ON UPDATE no action;
+EXCEPTION
+ WHEN duplicate_object THEN null;
+END $$;
\ No newline at end of file
diff --git a/packages/db/drizzle/meta/0016_snapshot.json b/packages/db/drizzle/meta/0016_snapshot.json
new file mode 100644
index 000000000..73b297a15
--- /dev/null
+++ b/packages/db/drizzle/meta/0016_snapshot.json
@@ -0,0 +1,3654 @@
+{
+ "id": "8ed975cc-5bb8-412b-aff2-facbf678d3e3",
+ "prevId": "a083a371-e9f5-462f-96f6-83a16463fcd7",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.account": {
+ "name": "account",
+ "schema": "",
+ "columns": {
+ "userId": {
+ "name": "userId",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "providerAccountId": {
+ "name": "providerAccountId",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "token_type": {
+ "name": "token_type",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "session_state": {
+ "name": "session_state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "account_userId_user_id_fk": {
+ "name": "account_userId_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": ["userId"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "account_provider_providerAccountId_pk": {
+ "name": "account_provider_providerAccountId_pk",
+ "columns": ["provider", "providerAccountId"]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "sessionToken": {
+ "name": "sessionToken",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "userId": {
+ "name": "userId",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires": {
+ "name": "expires",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "session_userId_user_id_fk": {
+ "name": "session_userId_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": ["userId"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "emailVerified": {
+ "name": "emailVerified",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "image": {
+ "name": "image",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "active_workspace_id": {
+ "name": "active_workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "null"
+ },
+ "password_hash": {
+ "name": "password_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "null"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_active_workspace_id_workspace_id_fk": {
+ "name": "user_active_workspace_id_workspace_id_fk",
+ "tableFrom": "user",
+ "tableTo": "workspace",
+ "columnsFrom": ["active_workspace_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.user_api_key": {
+ "name": "user_api_key",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key_preview": {
+ "name": "key_preview",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key_hash": {
+ "name": "key_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key_prefix": {
+ "name": "key_prefix",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "user_api_key_key_prefix_key_hash_index": {
+ "name": "user_api_key_key_prefix_key_hash_index",
+ "columns": [
+ {
+ "expression": "key_prefix",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "key_hash",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "user_api_key_user_id_user_id_fk": {
+ "name": "user_api_key_user_id_user_id_fk",
+ "tableFrom": "user_api_key",
+ "tableTo": "user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.dashboard": {
+ "name": "dashboard",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "workspace_id": {
+ "name": "workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboard_workspace_id_workspace_id_fk": {
+ "name": "dashboard_workspace_id_workspace_id_fk",
+ "tableFrom": "dashboard",
+ "tableTo": "workspace",
+ "columnsFrom": ["workspace_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.dashboard_widget": {
+ "name": "dashboard_widget",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "dashboard_id": {
+ "name": "dashboard_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "widget": {
+ "name": "widget",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "x": {
+ "name": "x",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "y": {
+ "name": "y",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "w": {
+ "name": "w",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "h": {
+ "name": "h",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboard_widget_dashboard_id_dashboard_id_fk": {
+ "name": "dashboard_widget_dashboard_id_dashboard_id_fk",
+ "tableFrom": "dashboard_widget",
+ "tableTo": "dashboard",
+ "columnsFrom": ["dashboard_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.deployment_variable": {
+ "name": "deployment_variable",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "deployment_id": {
+ "name": "deployment_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "default_value_id": {
+ "name": "default_value_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "NULL"
+ },
+ "schema": {
+ "name": "schema",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "deployment_variable_deployment_id_key_index": {
+ "name": "deployment_variable_deployment_id_key_index",
+ "columns": [
+ {
+ "expression": "deployment_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "deployment_variable_deployment_id_deployment_id_fk": {
+ "name": "deployment_variable_deployment_id_deployment_id_fk",
+ "tableFrom": "deployment_variable",
+ "tableTo": "deployment",
+ "columnsFrom": ["deployment_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "deployment_variable_default_value_id_deployment_variable_value_id_fk": {
+ "name": "deployment_variable_default_value_id_deployment_variable_value_id_fk",
+ "tableFrom": "deployment_variable",
+ "tableTo": "deployment_variable_value",
+ "columnsFrom": ["default_value_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.deployment_variable_set": {
+ "name": "deployment_variable_set",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "deployment_id": {
+ "name": "deployment_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "variable_set_id": {
+ "name": "variable_set_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "deployment_variable_set_deployment_id_variable_set_id_index": {
+ "name": "deployment_variable_set_deployment_id_variable_set_id_index",
+ "columns": [
+ {
+ "expression": "deployment_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "variable_set_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "deployment_variable_set_deployment_id_deployment_id_fk": {
+ "name": "deployment_variable_set_deployment_id_deployment_id_fk",
+ "tableFrom": "deployment_variable_set",
+ "tableTo": "deployment",
+ "columnsFrom": ["deployment_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "deployment_variable_set_variable_set_id_variable_set_id_fk": {
+ "name": "deployment_variable_set_variable_set_id_variable_set_id_fk",
+ "tableFrom": "deployment_variable_set",
+ "tableTo": "variable_set",
+ "columnsFrom": ["variable_set_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.deployment_variable_value": {
+ "name": "deployment_variable_value",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "variable_id": {
+ "name": "variable_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_filter": {
+ "name": "target_filter",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "NULL"
+ }
+ },
+ "indexes": {
+ "deployment_variable_value_variable_id_value_index": {
+ "name": "deployment_variable_value_variable_id_value_index",
+ "columns": [
+ {
+ "expression": "variable_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "value",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "deployment_variable_value_variable_id_deployment_variable_id_fk": {
+ "name": "deployment_variable_value_variable_id_deployment_variable_id_fk",
+ "tableFrom": "deployment_variable_value",
+ "tableTo": "deployment_variable",
+ "columnsFrom": ["variable_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "restrict"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.deployment": {
+ "name": "deployment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "system_id": {
+ "name": "system_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_agent_id": {
+ "name": "job_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "job_agent_config": {
+ "name": "job_agent_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ }
+ },
+ "indexes": {
+ "deployment_system_id_slug_index": {
+ "name": "deployment_system_id_slug_index",
+ "columns": [
+ {
+ "expression": "system_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "slug",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "deployment_system_id_system_id_fk": {
+ "name": "deployment_system_id_system_id_fk",
+ "tableFrom": "deployment",
+ "tableTo": "system",
+ "columnsFrom": ["system_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "deployment_job_agent_id_job_agent_id_fk": {
+ "name": "deployment_job_agent_id_job_agent_id_fk",
+ "tableFrom": "deployment",
+ "tableTo": "job_agent",
+ "columnsFrom": ["job_agent_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.deployment_meta_dependency": {
+ "name": "deployment_meta_dependency",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deployment_id": {
+ "name": "deployment_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "depends_on_id": {
+ "name": "depends_on_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "deployment_meta_dependency_depends_on_id_deployment_id_index": {
+ "name": "deployment_meta_dependency_depends_on_id_deployment_id_index",
+ "columns": [
+ {
+ "expression": "depends_on_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "deployment_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "deployment_meta_dependency_deployment_id_deployment_id_fk": {
+ "name": "deployment_meta_dependency_deployment_id_deployment_id_fk",
+ "tableFrom": "deployment_meta_dependency",
+ "tableTo": "deployment",
+ "columnsFrom": ["deployment_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "deployment_meta_dependency_depends_on_id_deployment_id_fk": {
+ "name": "deployment_meta_dependency_depends_on_id_deployment_id_fk",
+ "tableFrom": "deployment_meta_dependency",
+ "tableTo": "deployment",
+ "columnsFrom": ["depends_on_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.environment": {
+ "name": "environment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "system_id": {
+ "name": "system_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "''"
+ },
+ "policy_id": {
+ "name": "policy_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "target_filter": {
+ "name": "target_filter",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "NULL"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "environment_system_id_system_id_fk": {
+ "name": "environment_system_id_system_id_fk",
+ "tableFrom": "environment",
+ "tableTo": "system",
+ "columnsFrom": ["system_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "environment_policy_id_environment_policy_id_fk": {
+ "name": "environment_policy_id_environment_policy_id_fk",
+ "tableFrom": "environment",
+ "tableTo": "environment_policy",
+ "columnsFrom": ["policy_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.environment_policy": {
+ "name": "environment_policy",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "system_id": {
+ "name": "system_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "approval_required": {
+ "name": "approval_required",
+ "type": "environment_policy_approval_requirement",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'manual'"
+ },
+ "success_status": {
+ "name": "success_status",
+ "type": "environment_policy_deployment_success_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'all'"
+ },
+ "minimum_success": {
+ "name": "minimum_success",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "concurrency_type": {
+ "name": "concurrency_type",
+ "type": "concurrency_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'all'"
+ },
+ "concurrency_limit": {
+ "name": "concurrency_limit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "duration": {
+ "name": "duration",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "evaluate_with": {
+ "name": "evaluate_with",
+ "type": "evaluation_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'none'"
+ },
+ "evaluate": {
+ "name": "evaluate",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "NULL"
+ },
+ "release_sequencing": {
+ "name": "release_sequencing",
+ "type": "release_sequencing_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'cancel'"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "environment_policy_system_id_system_id_fk": {
+ "name": "environment_policy_system_id_system_id_fk",
+ "tableFrom": "environment_policy",
+ "tableTo": "system",
+ "columnsFrom": ["system_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.environment_policy_approval": {
+ "name": "environment_policy_approval",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "policy_id": {
+ "name": "policy_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "release_id": {
+ "name": "release_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "approval_status_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ }
+ },
+ "indexes": {
+ "environment_policy_approval_policy_id_release_id_index": {
+ "name": "environment_policy_approval_policy_id_release_id_index",
+ "columns": [
+ {
+ "expression": "policy_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "release_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "environment_policy_approval_policy_id_environment_policy_id_fk": {
+ "name": "environment_policy_approval_policy_id_environment_policy_id_fk",
+ "tableFrom": "environment_policy_approval",
+ "tableTo": "environment_policy",
+ "columnsFrom": ["policy_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "environment_policy_approval_release_id_release_id_fk": {
+ "name": "environment_policy_approval_release_id_release_id_fk",
+ "tableFrom": "environment_policy_approval",
+ "tableTo": "release",
+ "columnsFrom": ["release_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.environment_policy_deployment": {
+ "name": "environment_policy_deployment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "policy_id": {
+ "name": "policy_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "environment_id": {
+ "name": "environment_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "environment_policy_deployment_policy_id_environment_id_index": {
+ "name": "environment_policy_deployment_policy_id_environment_id_index",
+ "columns": [
+ {
+ "expression": "policy_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "environment_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "environment_policy_deployment_policy_id_environment_policy_id_fk": {
+ "name": "environment_policy_deployment_policy_id_environment_policy_id_fk",
+ "tableFrom": "environment_policy_deployment",
+ "tableTo": "environment_policy",
+ "columnsFrom": ["policy_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "environment_policy_deployment_environment_id_environment_id_fk": {
+ "name": "environment_policy_deployment_environment_id_environment_id_fk",
+ "tableFrom": "environment_policy_deployment",
+ "tableTo": "environment",
+ "columnsFrom": ["environment_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.environment_policy_release_window": {
+ "name": "environment_policy_release_window",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "policy_id": {
+ "name": "policy_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "start_time": {
+ "name": "start_time",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "end_time": {
+ "name": "end_time",
+ "type": "timestamp (0) with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "recurrence": {
+ "name": "recurrence",
+ "type": "recurrence_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "environment_policy_release_window_policy_id_environment_policy_id_fk": {
+ "name": "environment_policy_release_window_policy_id_environment_policy_id_fk",
+ "tableFrom": "environment_policy_release_window",
+ "tableTo": "environment_policy",
+ "columnsFrom": ["policy_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.github_organization": {
+ "name": "github_organization",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "installation_id": {
+ "name": "installation_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_name": {
+ "name": "organization_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "added_by_user_id": {
+ "name": "added_by_user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "workspace_id": {
+ "name": "workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'main'"
+ }
+ },
+ "indexes": {
+ "unique_installation_workspace": {
+ "name": "unique_installation_workspace",
+ "columns": [
+ {
+ "expression": "installation_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "workspace_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "github_organization_added_by_user_id_user_id_fk": {
+ "name": "github_organization_added_by_user_id_user_id_fk",
+ "tableFrom": "github_organization",
+ "tableTo": "user",
+ "columnsFrom": ["added_by_user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "github_organization_workspace_id_workspace_id_fk": {
+ "name": "github_organization_workspace_id_workspace_id_fk",
+ "tableFrom": "github_organization",
+ "tableTo": "workspace",
+ "columnsFrom": ["workspace_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.github_user": {
+ "name": "github_user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_user_id": {
+ "name": "github_user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_username": {
+ "name": "github_username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "github_user_user_id_user_id_fk": {
+ "name": "github_user_user_id_user_id_fk",
+ "tableFrom": "github_user",
+ "tableTo": "user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.target": {
+ "name": "target",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "version": {
+ "name": "version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "workspace_id": {
+ "name": "workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "locked_at": {
+ "name": "locked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "target_identifier_workspace_id_index": {
+ "name": "target_identifier_workspace_id_index",
+ "columns": [
+ {
+ "expression": "identifier",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "workspace_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "target_provider_id_target_provider_id_fk": {
+ "name": "target_provider_id_target_provider_id_fk",
+ "tableFrom": "target",
+ "tableTo": "target_provider",
+ "columnsFrom": ["provider_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "target_workspace_id_workspace_id_fk": {
+ "name": "target_workspace_id_workspace_id_fk",
+ "tableFrom": "target",
+ "tableTo": "workspace",
+ "columnsFrom": ["workspace_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.target_metadata": {
+ "name": "target_metadata",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "target_metadata_key_target_id_index": {
+ "name": "target_metadata_key_target_id_index",
+ "columns": [
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "target_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "target_metadata_target_id_target_id_fk": {
+ "name": "target_metadata_target_id_target_id_fk",
+ "tableFrom": "target_metadata",
+ "tableTo": "target",
+ "columnsFrom": ["target_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.target_relationship": {
+ "name": "target_relationship",
+ "schema": "",
+ "columns": {
+ "uuid": {
+ "name": "uuid",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_id": {
+ "name": "source_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "relationship_type": {
+ "name": "relationship_type",
+ "type": "target_relationship_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "target_relationship_target_id_source_id_index": {
+ "name": "target_relationship_target_id_source_id_index",
+ "columns": [
+ {
+ "expression": "target_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "source_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "target_relationship_source_id_target_id_fk": {
+ "name": "target_relationship_source_id_target_id_fk",
+ "tableFrom": "target_relationship",
+ "tableTo": "target",
+ "columnsFrom": ["source_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "target_relationship_target_id_target_id_fk": {
+ "name": "target_relationship_target_id_target_id_fk",
+ "tableFrom": "target_relationship",
+ "tableTo": "target",
+ "columnsFrom": ["target_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.target_schema": {
+ "name": "target_schema",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "workspace_id": {
+ "name": "workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "version": {
+ "name": "version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "json_schema": {
+ "name": "json_schema",
+ "type": "json",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "target_schema_version_kind_workspace_id_index": {
+ "name": "target_schema_version_kind_workspace_id_index",
+ "columns": [
+ {
+ "expression": "version",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "workspace_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "target_schema_workspace_id_workspace_id_fk": {
+ "name": "target_schema_workspace_id_workspace_id_fk",
+ "tableFrom": "target_schema",
+ "tableTo": "workspace",
+ "columnsFrom": ["workspace_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.target_view": {
+ "name": "target_view",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "workspace_id": {
+ "name": "workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "''"
+ },
+ "filter": {
+ "name": "filter",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "target_view_workspace_id_workspace_id_fk": {
+ "name": "target_view_workspace_id_workspace_id_fk",
+ "tableFrom": "target_view",
+ "tableTo": "workspace",
+ "columnsFrom": ["workspace_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.target_provider": {
+ "name": "target_provider",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "workspace_id": {
+ "name": "workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "target_provider_workspace_id_name_index": {
+ "name": "target_provider_workspace_id_name_index",
+ "columns": [
+ {
+ "expression": "workspace_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "target_provider_workspace_id_workspace_id_fk": {
+ "name": "target_provider_workspace_id_workspace_id_fk",
+ "tableFrom": "target_provider",
+ "tableTo": "workspace",
+ "columnsFrom": ["workspace_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.target_provider_google": {
+ "name": "target_provider_google",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "target_provider_id": {
+ "name": "target_provider_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_ids": {
+ "name": "project_ids",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "target_provider_google_target_provider_id_target_provider_id_fk": {
+ "name": "target_provider_google_target_provider_id_target_provider_id_fk",
+ "tableFrom": "target_provider_google",
+ "tableTo": "target_provider",
+ "columnsFrom": ["target_provider_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.release": {
+ "name": "release",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "version": {
+ "name": "version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "deployment_id": {
+ "name": "deployment_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "release_deployment_id_version_index": {
+ "name": "release_deployment_id_version_index",
+ "columns": [
+ {
+ "expression": "deployment_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "version",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "release_deployment_id_deployment_id_fk": {
+ "name": "release_deployment_id_deployment_id_fk",
+ "tableFrom": "release",
+ "tableTo": "deployment",
+ "columnsFrom": ["deployment_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.release_dependency": {
+ "name": "release_dependency",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "release_id": {
+ "name": "release_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "deployment_id": {
+ "name": "deployment_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_metadata_group_id": {
+ "name": "target_metadata_group_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "rule_type": {
+ "name": "rule_type",
+ "type": "release_dependency_rule_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rule": {
+ "name": "rule",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "release_dependency_release_id_deployment_id_target_metadata_group_id_index": {
+ "name": "release_dependency_release_id_deployment_id_target_metadata_group_id_index",
+ "columns": [
+ {
+ "expression": "release_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "deployment_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "target_metadata_group_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "release_dependency_release_id_release_id_fk": {
+ "name": "release_dependency_release_id_release_id_fk",
+ "tableFrom": "release_dependency",
+ "tableTo": "release",
+ "columnsFrom": ["release_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "release_dependency_deployment_id_deployment_id_fk": {
+ "name": "release_dependency_deployment_id_deployment_id_fk",
+ "tableFrom": "release_dependency",
+ "tableTo": "deployment",
+ "columnsFrom": ["deployment_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "release_dependency_target_metadata_group_id_target_metadata_group_id_fk": {
+ "name": "release_dependency_target_metadata_group_id_target_metadata_group_id_fk",
+ "tableFrom": "release_dependency",
+ "tableTo": "target_metadata_group",
+ "columnsFrom": ["target_metadata_group_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.release_job_trigger": {
+ "name": "release_job_trigger",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "release_job_trigger_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "caused_by_id": {
+ "name": "caused_by_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "release_id": {
+ "name": "release_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "environment_id": {
+ "name": "environment_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "release_job_trigger_job_id_job_id_fk": {
+ "name": "release_job_trigger_job_id_job_id_fk",
+ "tableFrom": "release_job_trigger",
+ "tableTo": "job",
+ "columnsFrom": ["job_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "release_job_trigger_caused_by_id_user_id_fk": {
+ "name": "release_job_trigger_caused_by_id_user_id_fk",
+ "tableFrom": "release_job_trigger",
+ "tableTo": "user",
+ "columnsFrom": ["caused_by_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "release_job_trigger_release_id_release_id_fk": {
+ "name": "release_job_trigger_release_id_release_id_fk",
+ "tableFrom": "release_job_trigger",
+ "tableTo": "release",
+ "columnsFrom": ["release_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "release_job_trigger_target_id_target_id_fk": {
+ "name": "release_job_trigger_target_id_target_id_fk",
+ "tableFrom": "release_job_trigger",
+ "tableTo": "target",
+ "columnsFrom": ["target_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "release_job_trigger_environment_id_environment_id_fk": {
+ "name": "release_job_trigger_environment_id_environment_id_fk",
+ "tableFrom": "release_job_trigger",
+ "tableTo": "environment",
+ "columnsFrom": ["environment_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "release_job_trigger_job_id_unique": {
+ "name": "release_job_trigger_job_id_unique",
+ "nullsNotDistinct": false,
+ "columns": ["job_id"]
+ }
+ }
+ },
+ "public.release_metadata": {
+ "name": "release_metadata",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "release_id": {
+ "name": "release_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "release_metadata_key_release_id_index": {
+ "name": "release_metadata_key_release_id_index",
+ "columns": [
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "release_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "release_metadata_release_id_release_id_fk": {
+ "name": "release_metadata_release_id_release_id_fk",
+ "tableFrom": "release_metadata",
+ "tableTo": "release",
+ "columnsFrom": ["release_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.system": {
+ "name": "system",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "workspace_id": {
+ "name": "workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "system_workspace_id_slug_index": {
+ "name": "system_workspace_id_slug_index",
+ "columns": [
+ {
+ "expression": "workspace_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "slug",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "system_workspace_id_workspace_id_fk": {
+ "name": "system_workspace_id_workspace_id_fk",
+ "tableFrom": "system",
+ "tableTo": "workspace",
+ "columnsFrom": ["workspace_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.runbook": {
+ "name": "runbook",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "system_id": {
+ "name": "system_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_agent_id": {
+ "name": "job_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "job_agent_config": {
+ "name": "job_agent_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "runbook_system_id_system_id_fk": {
+ "name": "runbook_system_id_system_id_fk",
+ "tableFrom": "runbook",
+ "tableTo": "system",
+ "columnsFrom": ["system_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "runbook_job_agent_id_job_agent_id_fk": {
+ "name": "runbook_job_agent_id_job_agent_id_fk",
+ "tableFrom": "runbook",
+ "tableTo": "job_agent",
+ "columnsFrom": ["job_agent_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.runbook_job_trigger": {
+ "name": "runbook_job_trigger",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "runbook_id": {
+ "name": "runbook_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "runbook_job_trigger_job_id_job_id_fk": {
+ "name": "runbook_job_trigger_job_id_job_id_fk",
+ "tableFrom": "runbook_job_trigger",
+ "tableTo": "job",
+ "columnsFrom": ["job_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "runbook_job_trigger_runbook_id_runbook_id_fk": {
+ "name": "runbook_job_trigger_runbook_id_runbook_id_fk",
+ "tableFrom": "runbook_job_trigger",
+ "tableTo": "runbook",
+ "columnsFrom": ["runbook_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "runbook_job_trigger_job_id_unique": {
+ "name": "runbook_job_trigger_job_id_unique",
+ "nullsNotDistinct": false,
+ "columns": ["job_id"]
+ }
+ }
+ },
+ "public.team": {
+ "name": "team",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "text": {
+ "name": "text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "workspace_id": {
+ "name": "workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_workspace_id_workspace_id_fk": {
+ "name": "team_workspace_id_workspace_id_fk",
+ "tableFrom": "team",
+ "tableTo": "workspace",
+ "columnsFrom": ["workspace_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.team_member": {
+ "name": "team_member",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "team_member_team_id_user_id_index": {
+ "name": "team_member_team_id_user_id_index",
+ "columns": [
+ {
+ "expression": "team_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "team_member_team_id_team_id_fk": {
+ "name": "team_member_team_id_team_id_fk",
+ "tableFrom": "team_member",
+ "tableTo": "team",
+ "columnsFrom": ["team_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "team_member_user_id_user_id_fk": {
+ "name": "team_member_user_id_user_id_fk",
+ "tableFrom": "team_member",
+ "tableTo": "user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.job": {
+ "name": "job",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "job_agent_id": {
+ "name": "job_agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "job_agent_config": {
+ "name": "job_agent_config",
+ "type": "json",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "job_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reason": {
+ "name": "reason",
+ "type": "job_reason",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'policy_passing'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "job_job_agent_id_job_agent_id_fk": {
+ "name": "job_job_agent_id_job_agent_id_fk",
+ "tableFrom": "job",
+ "tableTo": "job_agent",
+ "columnsFrom": ["job_agent_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.job_metadata": {
+ "name": "job_metadata",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "job_metadata_key_job_id_index": {
+ "name": "job_metadata_key_job_id_index",
+ "columns": [
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "job_metadata_job_id_job_id_fk": {
+ "name": "job_metadata_job_id_job_id_fk",
+ "tableFrom": "job_metadata",
+ "tableTo": "job",
+ "columnsFrom": ["job_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.job_variable": {
+ "name": "job_variable",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "json",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "job_variable_job_id_key_index": {
+ "name": "job_variable_job_id_key_index",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "job_variable_job_id_job_id_fk": {
+ "name": "job_variable_job_id_job_id_fk",
+ "tableFrom": "job_variable",
+ "tableTo": "job",
+ "columnsFrom": ["job_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.workspace": {
+ "name": "workspace",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "google_service_account_email": {
+ "name": "google_service_account_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "workspace_slug_unique": {
+ "name": "workspace_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": ["slug"]
+ }
+ }
+ },
+ "public.workspace_email_domain_matching": {
+ "name": "workspace_email_domain_matching",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "workspace_id": {
+ "name": "workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "domain": {
+ "name": "domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role_id": {
+ "name": "role_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "verified": {
+ "name": "verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "verification_code": {
+ "name": "verification_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "verification_email": {
+ "name": "verification_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "workspace_email_domain_matching_workspace_id_domain_index": {
+ "name": "workspace_email_domain_matching_workspace_id_domain_index",
+ "columns": [
+ {
+ "expression": "workspace_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "domain",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "workspace_email_domain_matching_workspace_id_workspace_id_fk": {
+ "name": "workspace_email_domain_matching_workspace_id_workspace_id_fk",
+ "tableFrom": "workspace_email_domain_matching",
+ "tableTo": "workspace",
+ "columnsFrom": ["workspace_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "workspace_email_domain_matching_role_id_role_id_fk": {
+ "name": "workspace_email_domain_matching_role_id_role_id_fk",
+ "tableFrom": "workspace_email_domain_matching",
+ "tableTo": "role",
+ "columnsFrom": ["role_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.variable_set": {
+ "name": "variable_set",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "system_id": {
+ "name": "system_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "variable_set_system_id_system_id_fk": {
+ "name": "variable_set_system_id_system_id_fk",
+ "tableFrom": "variable_set",
+ "tableTo": "system",
+ "columnsFrom": ["system_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.variable_set_assignment": {
+ "name": "variable_set_assignment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "variable_set_id": {
+ "name": "variable_set_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "environment_id": {
+ "name": "environment_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "variable_set_assignment_variable_set_id_variable_set_id_fk": {
+ "name": "variable_set_assignment_variable_set_id_variable_set_id_fk",
+ "tableFrom": "variable_set_assignment",
+ "tableTo": "variable_set",
+ "columnsFrom": ["variable_set_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "variable_set_assignment_environment_id_environment_id_fk": {
+ "name": "variable_set_assignment_environment_id_environment_id_fk",
+ "tableFrom": "variable_set_assignment",
+ "tableTo": "environment",
+ "columnsFrom": ["environment_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.variable_set_value": {
+ "name": "variable_set_value",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "variable_set_id": {
+ "name": "variable_set_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "variable_set_value_variable_set_id_key_index": {
+ "name": "variable_set_value_variable_set_id_key_index",
+ "columns": [
+ {
+ "expression": "variable_set_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "variable_set_value_variable_set_id_variable_set_id_fk": {
+ "name": "variable_set_value_variable_set_id_variable_set_id_fk",
+ "tableFrom": "variable_set_value",
+ "tableTo": "variable_set",
+ "columnsFrom": ["variable_set_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.workspace_invite_token": {
+ "name": "workspace_invite_token",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "role_id": {
+ "name": "role_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "workspace_id": {
+ "name": "workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "workspace_invite_token_role_id_role_id_fk": {
+ "name": "workspace_invite_token_role_id_role_id_fk",
+ "tableFrom": "workspace_invite_token",
+ "tableTo": "role",
+ "columnsFrom": ["role_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "workspace_invite_token_workspace_id_workspace_id_fk": {
+ "name": "workspace_invite_token_workspace_id_workspace_id_fk",
+ "tableFrom": "workspace_invite_token",
+ "tableTo": "workspace",
+ "columnsFrom": ["workspace_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "workspace_invite_token_created_by_user_id_fk": {
+ "name": "workspace_invite_token_created_by_user_id_fk",
+ "tableFrom": "workspace_invite_token",
+ "tableTo": "user",
+ "columnsFrom": ["created_by"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "workspace_invite_token_token_unique": {
+ "name": "workspace_invite_token_token_unique",
+ "nullsNotDistinct": false,
+ "columns": ["token"]
+ }
+ }
+ },
+ "public.target_metadata_group": {
+ "name": "target_metadata_group",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "workspace_id": {
+ "name": "workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "keys": {
+ "name": "keys",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "include_null_combinations": {
+ "name": "include_null_combinations",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "target_metadata_group_workspace_id_workspace_id_fk": {
+ "name": "target_metadata_group_workspace_id_workspace_id_fk",
+ "tableFrom": "target_metadata_group",
+ "tableTo": "workspace",
+ "columnsFrom": ["workspace_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.runbook_variable": {
+ "name": "runbook_variable",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "runbook_id": {
+ "name": "runbook_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "schema": {
+ "name": "schema",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "required": {
+ "name": "required",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {
+ "runbook_variable_runbook_id_key_index": {
+ "name": "runbook_variable_runbook_id_key_index",
+ "columns": [
+ {
+ "expression": "runbook_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "runbook_variable_runbook_id_runbook_id_fk": {
+ "name": "runbook_variable_runbook_id_runbook_id_fk",
+ "tableFrom": "runbook_variable",
+ "tableTo": "runbook",
+ "columnsFrom": ["runbook_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.entity_role": {
+ "name": "entity_role",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "role_id": {
+ "name": "role_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "entity_type": {
+ "name": "entity_type",
+ "type": "entity_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "entity_id": {
+ "name": "entity_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope_id": {
+ "name": "scope_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope_type": {
+ "name": "scope_type",
+ "type": "scope_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "entity_role_role_id_entity_type_entity_id_scope_id_scope_type_index": {
+ "name": "entity_role_role_id_entity_type_entity_id_scope_id_scope_type_index",
+ "columns": [
+ {
+ "expression": "role_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "entity_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "entity_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "scope_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "scope_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "entity_role_role_id_role_id_fk": {
+ "name": "entity_role_role_id_role_id_fk",
+ "tableFrom": "entity_role",
+ "tableTo": "role",
+ "columnsFrom": ["role_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.role": {
+ "name": "role",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "workspace_id": {
+ "name": "workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "role_workspace_id_workspace_id_fk": {
+ "name": "role_workspace_id_workspace_id_fk",
+ "tableFrom": "role",
+ "tableTo": "workspace",
+ "columnsFrom": ["workspace_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.role_permission": {
+ "name": "role_permission",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "role_id": {
+ "name": "role_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "permission": {
+ "name": "permission",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "role_permission_role_id_permission_index": {
+ "name": "role_permission_role_id_permission_index",
+ "columns": [
+ {
+ "expression": "role_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "permission",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "role_permission_role_id_role_id_fk": {
+ "name": "role_permission_role_id_role_id_fk",
+ "tableFrom": "role_permission",
+ "tableTo": "role",
+ "columnsFrom": ["role_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.job_agent": {
+ "name": "job_agent",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "workspace_id": {
+ "name": "workspace_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config": {
+ "name": "config",
+ "type": "json",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ }
+ },
+ "indexes": {
+ "job_agent_workspace_id_name_index": {
+ "name": "job_agent_workspace_id_name_index",
+ "columns": [
+ {
+ "expression": "workspace_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "job_agent_workspace_id_workspace_id_fk": {
+ "name": "job_agent_workspace_id_workspace_id_fk",
+ "tableFrom": "job_agent",
+ "tableTo": "workspace",
+ "columnsFrom": ["workspace_id"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ }
+ },
+ "enums": {
+ "public.environment_policy_approval_requirement": {
+ "name": "environment_policy_approval_requirement",
+ "schema": "public",
+ "values": ["manual", "automatic"]
+ },
+ "public.approval_status_type": {
+ "name": "approval_status_type",
+ "schema": "public",
+ "values": ["pending", "approved", "rejected"]
+ },
+ "public.concurrency_type": {
+ "name": "concurrency_type",
+ "schema": "public",
+ "values": ["all", "some"]
+ },
+ "public.environment_policy_deployment_success_type": {
+ "name": "environment_policy_deployment_success_type",
+ "schema": "public",
+ "values": ["all", "some", "optional"]
+ },
+ "public.evaluation_type": {
+ "name": "evaluation_type",
+ "schema": "public",
+ "values": ["semver", "regex", "filter", "none"]
+ },
+ "public.recurrence_type": {
+ "name": "recurrence_type",
+ "schema": "public",
+ "values": ["hourly", "daily", "weekly", "monthly"]
+ },
+ "public.release_sequencing_type": {
+ "name": "release_sequencing_type",
+ "schema": "public",
+ "values": ["wait", "cancel"]
+ },
+ "public.target_relationship_type": {
+ "name": "target_relationship_type",
+ "schema": "public",
+ "values": ["depends_on", "created_by"]
+ },
+ "public.release_dependency_rule_type": {
+ "name": "release_dependency_rule_type",
+ "schema": "public",
+ "values": ["regex", "semver"]
+ },
+ "public.release_job_trigger_type": {
+ "name": "release_job_trigger_type",
+ "schema": "public",
+ "values": [
+ "new_release",
+ "new_target",
+ "target_changed",
+ "api",
+ "redeploy",
+ "force_deploy"
+ ]
+ },
+ "public.job_reason": {
+ "name": "job_reason",
+ "schema": "public",
+ "values": [
+ "policy_passing",
+ "policy_override",
+ "env_policy_override",
+ "config_policy_override"
+ ]
+ },
+ "public.job_status": {
+ "name": "job_status",
+ "schema": "public",
+ "values": [
+ "completed",
+ "cancelled",
+ "skipped",
+ "in_progress",
+ "action_required",
+ "pending",
+ "failure",
+ "invalid_job_agent",
+ "invalid_integration",
+ "external_run_not_found"
+ ]
+ },
+ "public.entity_type": {
+ "name": "entity_type",
+ "schema": "public",
+ "values": ["user", "team"]
+ },
+ "public.scope_type": {
+ "name": "scope_type",
+ "schema": "public",
+ "values": [
+ "release",
+ "target",
+ "targetProvider",
+ "targetMetadataGroup",
+ "workspace",
+ "environment",
+ "environmentPolicy",
+ "deploymentVariable",
+ "variableSet",
+ "system",
+ "deployment",
+ "job",
+ "jobAgent",
+ "runbook",
+ "targetView"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
diff --git a/packages/db/drizzle/meta/_journal.json b/packages/db/drizzle/meta/_journal.json
index c46327662..53877fbef 100644
--- a/packages/db/drizzle/meta/_journal.json
+++ b/packages/db/drizzle/meta/_journal.json
@@ -113,6 +113,13 @@
"when": 1729035549648,
"tag": "0015_furry_omega_red",
"breakpoints": true
+ },
+ {
+ "idx": 16,
+ "version": "7",
+ "when": 1729285953388,
+ "tag": "0016_cute_vengeance",
+ "breakpoints": true
}
]
}
diff --git a/packages/db/src/schema/environment.ts b/packages/db/src/schema/environment.ts
index d1cacddbf..eeb7510aa 100644
--- a/packages/db/src/schema/environment.ts
+++ b/packages/db/src/schema/environment.ts
@@ -1,7 +1,7 @@
import type { ReleaseCondition } from "@ctrlplane/validators/releases";
import type { TargetCondition } from "@ctrlplane/validators/targets";
import type { InferSelectModel } from "drizzle-orm";
-import { sql } from "drizzle-orm";
+import { relations, sql } from "drizzle-orm";
import {
bigint,
integer,
@@ -21,6 +21,7 @@ import { targetCondition } from "@ctrlplane/validators/targets";
import { release } from "./release.js";
import { system } from "./system.js";
+import { variableSetAssignment } from "./variable-sets.js";
export const environment = pgTable("environment", {
id: uuid("id").primaryKey().defaultRandom(),
@@ -47,6 +48,10 @@ export const createEnvironment = createInsertSchema(environment, {
export const updateEnvironment = createEnvironment.partial();
export type InsertEnvironment = z.infer
;
+export const environmentRelations = relations(environment, ({ many }) => ({
+ assignments: many(variableSetAssignment),
+}));
+
export const approvalRequirement = pgEnum(
"environment_policy_approval_requirement",
["manual", "automatic"],
diff --git a/packages/db/src/schema/variable-sets.ts b/packages/db/src/schema/variable-sets.ts
index 0ca920f0f..b232a7151 100644
--- a/packages/db/src/schema/variable-sets.ts
+++ b/packages/db/src/schema/variable-sets.ts
@@ -1,8 +1,10 @@
-import type { InferInsertModel } from "drizzle-orm";
-import { pgTable, text, uniqueIndex, uuid } from "drizzle-orm/pg-core";
+import type { InferSelectModel } from "drizzle-orm";
+import { relations } from "drizzle-orm";
+import { jsonb, pgTable, text, uniqueIndex, uuid } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
+import { environment } from "./environment.js";
import { system } from "./system.js";
export const variableSet = pgTable("variable_set", {
@@ -14,6 +16,35 @@ export const variableSet = pgTable("variable_set", {
.references(() => system.id, { onDelete: "cascade" }),
});
+export type VariableSet = InferSelectModel;
+export const createVariableSet = createInsertSchema(variableSet)
+ .omit({ id: true })
+ .extend({
+ values: z.array(
+ z.object({
+ key: z.string().trim().min(3),
+ value: z.any(),
+ }),
+ ),
+ environmentIds: z.array(z.string().uuid()),
+ });
+
+export const updateVariableSet = createVariableSet.partial().extend({
+ values: z
+ .array(
+ z.object({
+ key: z.string().trim().min(3),
+ value: z.any(),
+ }),
+ )
+ .optional(),
+});
+
+export const variableSetRelations = relations(variableSet, ({ many }) => ({
+ values: many(variableSetValue),
+ assignments: many(variableSetAssignment),
+}));
+
export const variableSetValue = pgTable(
"variable_set_value",
{
@@ -23,34 +54,47 @@ export const variableSetValue = pgTable(
.references(() => variableSet.id, { onDelete: "cascade" }),
key: text("key").notNull(),
- value: text("value").notNull(),
+ value: jsonb("value").$type().notNull(),
},
(t) => ({ uniq: uniqueIndex().on(t.variableSetId, t.key) }),
);
-export type VariableSet = InferInsertModel;
-export const createVariableSet = createInsertSchema(variableSet)
- .omit({ id: true })
- .and(
- z.object({
- values: z.array(
- z.object({
- key: z.string().trim().min(3),
- value: z.string(),
- }),
- ),
+export type VariableSetValue = InferSelectModel;
+
+export const variableSetValueRelations = relations(
+ variableSetValue,
+ ({ one }) => ({
+ variableSet: one(variableSet, {
+ fields: [variableSetValue.variableSetId],
+ references: [variableSet.id],
}),
- );
+ }),
+);
-export const updateVariableSet = createInsertSchema(variableSet)
- .omit({ id: true })
- .partial()
- .and(
- z
- .object({
- values: z.array(z.object({ key: z.string(), value: z.string() })),
- })
- .partial(),
- );
-
-export type VariableSetValue = InferInsertModel;
+export const variableSetAssignment = pgTable("variable_set_assignment", {
+ id: uuid("id").notNull().primaryKey().defaultRandom(),
+ variableSetId: uuid("variable_set_id")
+ .notNull()
+ .references(() => variableSet.id, { onDelete: "cascade" }),
+ environmentId: uuid("environment_id")
+ .notNull()
+ .references(() => environment.id, { onDelete: "cascade" }),
+});
+
+export type VariableSetAssignment = InferSelectModel<
+ typeof variableSetAssignment
+>;
+
+export const variableSetAssignmentRelations = relations(
+ variableSetAssignment,
+ ({ one }) => ({
+ variableSet: one(variableSet, {
+ fields: [variableSetAssignment.variableSetId],
+ references: [variableSet.id],
+ }),
+ environment: one(environment, {
+ fields: [variableSetAssignment.environmentId],
+ references: [environment.id],
+ }),
+ }),
+);
diff --git a/packages/job-dispatch/package.json b/packages/job-dispatch/package.json
index a6039c52b..dea66bdfa 100644
--- a/packages/job-dispatch/package.json
+++ b/packages/job-dispatch/package.json
@@ -21,7 +21,8 @@
"format": "prettier --check . --ignore-path ../../.gitignore",
"lint": "eslint",
"typecheck": "tsc --noEmit --emitDeclarationOnly false",
- "with-env": "dotenv -e ../../.env --"
+ "with-env": "dotenv -e ../../.env --",
+ "test": "vitest"
},
"dependencies": {
"@ctrlplane/db": "workspace:*",
@@ -50,7 +51,7 @@
"prettier": "catalog:",
"typescript": "^5.6.2",
"vite-tsconfig-paths": "^5.0.1",
- "vitest": "^2.1.1"
+ "vitest": "^2.1.2"
},
"prettier": "@ctrlplane/prettier-config"
}
diff --git a/packages/job-dispatch/src/__test__/job-variables-deployment.test.ts b/packages/job-dispatch/src/__test__/job-variables-deployment.test.ts
new file mode 100644
index 000000000..3d82eeb23
--- /dev/null
+++ b/packages/job-dispatch/src/__test__/job-variables-deployment.test.ts
@@ -0,0 +1,559 @@
+import type * as SCHEMA from "@ctrlplane/db/schema";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+
+import { db } from "@ctrlplane/db/client";
+import { JobStatus } from "@ctrlplane/validators/jobs";
+
+import * as jobVariablesDeployment from "../job-variables-deployment/job-variables-deployment.js";
+import * as utils from "../job-variables-deployment/utils.js";
+
+vi.mock("../job-variables-deployment", async () => ({
+ ...(await vi.importActual("../job-variables-deployment")),
+}));
+
+vi.mock("../job-variables-deployment/utils", async () => ({
+ ...(await vi.importActual("../job-variables-deployment/utils")),
+ getJob: vi.fn(),
+ getDeploymentVariables: vi.fn(),
+ getTarget: vi.fn(),
+ getEnvironment: vi.fn(),
+ getVariableValues: vi.fn(),
+ getFirstMatchedTarget: vi.fn(),
+}));
+
+type Job = {
+ job: SCHEMA.Job;
+ release_job_trigger: SCHEMA.ReleaseJobTrigger;
+};
+
+const job: Job = {
+ job: {
+ id: "0",
+ jobAgentId: null,
+ jobAgentConfig: {},
+ externalId: null,
+ status: JobStatus.Pending,
+ message: null,
+ reason: "policy_passing",
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ },
+ release_job_trigger: {
+ id: "0",
+ jobId: "0",
+ type: "new_release",
+ releaseId: "0",
+ targetId: "0",
+ environmentId: "0",
+ createdAt: new Date(),
+ causedById: null,
+ },
+};
+
+const target: SCHEMA.Target = {
+ id: "0",
+ name: "test",
+ workspaceId: "0",
+ config: {},
+ updatedAt: new Date(),
+ version: "0",
+ kind: "test",
+ identifier: "test",
+ providerId: "0",
+ lockedAt: null,
+};
+
+const release: SCHEMA.Release = {
+ id: "0",
+ name: "test",
+ config: {},
+ createdAt: new Date(),
+ version: "0",
+ deploymentId: "0",
+};
+
+const variableValues: SCHEMA.DeploymentVariableValue[] = [
+ {
+ id: "0",
+ variableId: "0",
+ value: "test1",
+ targetFilter: null,
+ },
+ {
+ id: "1",
+ variableId: "0",
+ value: "test2",
+ targetFilter: null,
+ },
+ {
+ id: "2",
+ variableId: "0",
+ value: "test3",
+ targetFilter: null,
+ },
+];
+
+type variable = {
+ deployment_variable: SCHEMA.DeploymentVariable;
+ release: SCHEMA.Release;
+};
+
+describe("job-variables-deployment", () => {
+ beforeEach(() => vi.clearAllMocks());
+
+ it("should return direct match value if it exists", async () => {
+ vi.mocked(utils.getJob).mockResolvedValue(job);
+
+ const variables: variable[] = [
+ {
+ deployment_variable: {
+ id: "0",
+ config: { type: "string", inputType: "text" },
+ description: "test",
+ deploymentId: "0",
+ key: "test",
+ defaultValueId: "0",
+ },
+ release,
+ },
+ ];
+ vi.mocked(utils.getDeploymentVariables).mockResolvedValue(variables);
+ vi.mocked(utils.getTarget).mockResolvedValue(target);
+
+ vi.mocked(utils.getVariableValues).mockResolvedValue(variableValues);
+ vi.mocked(utils.getFirstMatchedTarget).mockResolvedValue(
+ variableValues[1]!,
+ );
+ vi.mocked(utils.getEnvironment).mockResolvedValue(undefined);
+
+ const result = await jobVariablesDeployment.determineVariablesForReleaseJob(
+ db,
+ {
+ ...job.job,
+ releaseJobTrigger: job.release_job_trigger,
+ },
+ );
+
+ expect(result).toHaveLength(1);
+ expect(result[0]!.value).toEqual(variableValues[1]!.value);
+ });
+
+ it("should return default value if no direct match exists", async () => {
+ vi.mocked(utils.getJob).mockResolvedValue(job);
+
+ const variables: variable[] = [
+ {
+ deployment_variable: {
+ id: "0",
+ config: { type: "string", inputType: "text" },
+ description: "test",
+ deploymentId: "0",
+ key: "test",
+ defaultValueId: "0",
+ },
+ release,
+ },
+ ];
+ vi.mocked(utils.getDeploymentVariables).mockResolvedValue(variables);
+ vi.mocked(utils.getTarget).mockResolvedValue(target);
+
+ vi.mocked(utils.getVariableValues).mockResolvedValue(variableValues);
+ vi.mocked(utils.getFirstMatchedTarget).mockResolvedValue(null);
+ vi.mocked(utils.getEnvironment).mockResolvedValue(undefined);
+
+ const result = await jobVariablesDeployment.determineVariablesForReleaseJob(
+ db,
+ {
+ ...job.job,
+ releaseJobTrigger: job.release_job_trigger,
+ },
+ );
+
+ expect(result).toHaveLength(1);
+ expect(result[0]!.value).toEqual(variableValues[0]!.value);
+ });
+
+ it("should return first value if no direct match or default value exists", async () => {
+ vi.mocked(utils.getJob).mockResolvedValue(job);
+
+ const variables: variable[] = [
+ {
+ deployment_variable: {
+ id: "0",
+ config: { type: "string", inputType: "text" },
+ description: "test",
+ deploymentId: "0",
+ key: "test",
+ defaultValueId: null,
+ },
+ release,
+ },
+ ];
+ vi.mocked(utils.getDeploymentVariables).mockResolvedValue(variables);
+ vi.mocked(utils.getTarget).mockResolvedValue(target);
+
+ vi.mocked(utils.getVariableValues).mockResolvedValue(variableValues);
+ vi.mocked(utils.getFirstMatchedTarget).mockResolvedValue(null);
+ vi.mocked(utils.getEnvironment).mockResolvedValue(undefined);
+
+ const result = await jobVariablesDeployment.determineVariablesForReleaseJob(
+ db,
+ {
+ ...job.job,
+ releaseJobTrigger: job.release_job_trigger,
+ },
+ );
+
+ expect(result).toHaveLength(1);
+ expect(result[0]!.value).toEqual(variableValues[0]!.value);
+ });
+
+ it("should include variables from matched variable set", async () => {
+ vi.mocked(utils.getJob).mockResolvedValue(job);
+
+ const variables: variable[] = [
+ {
+ deployment_variable: {
+ id: "0",
+ config: { type: "string", inputType: "text" },
+ description: "test",
+ deploymentId: "0",
+ key: "test",
+ defaultValueId: "0",
+ },
+ release,
+ },
+ ];
+ vi.mocked(utils.getDeploymentVariables).mockResolvedValue(variables);
+ vi.mocked(utils.getTarget).mockResolvedValue(target);
+
+ vi.mocked(utils.getVariableValues).mockResolvedValue(variableValues);
+ vi.mocked(utils.getFirstMatchedTarget).mockResolvedValue(
+ variableValues[1]!,
+ );
+
+ vi.mocked(utils.getEnvironment).mockResolvedValue({
+ id: "0",
+ name: "test",
+ description: null,
+ systemId: "0",
+ policyId: null,
+ targetFilter: null,
+ deletedAt: null,
+ assignments: [
+ {
+ id: "0",
+ variableSetId: "0",
+ environmentId: "0",
+ variableSet: {
+ id: "0",
+ name: "test",
+ description: null,
+ systemId: "0",
+ values: [
+ {
+ id: "0",
+ value: "test4",
+ key: "test2",
+ variableSetId: "0",
+ },
+ ],
+ },
+ },
+ ],
+ });
+
+ const result = await jobVariablesDeployment.determineVariablesForReleaseJob(
+ db,
+ {
+ ...job.job,
+ releaseJobTrigger: job.release_job_trigger,
+ },
+ );
+
+ expect(result).toHaveLength(2);
+ expect(result[0]!.value).toEqual(variableValues[1]!.value);
+ expect(result[1]!.value).toEqual("test4");
+ });
+
+ it("should not include variables from matched variable set if direct match exists and key matches", async () => {
+ vi.mocked(utils.getJob).mockResolvedValue(job);
+
+ const variables: variable[] = [
+ {
+ deployment_variable: {
+ id: "0",
+ config: { type: "string", inputType: "text" },
+ description: "test",
+ deploymentId: "0",
+ key: "test",
+ defaultValueId: "0",
+ },
+ release,
+ },
+ ];
+ vi.mocked(utils.getDeploymentVariables).mockResolvedValue(variables);
+ vi.mocked(utils.getTarget).mockResolvedValue(target);
+
+ vi.mocked(utils.getVariableValues).mockResolvedValue(variableValues);
+ vi.mocked(utils.getFirstMatchedTarget).mockResolvedValue(
+ variableValues[1]!,
+ );
+
+ vi.mocked(utils.getEnvironment).mockResolvedValue({
+ id: "0",
+ name: "test",
+ description: null,
+ systemId: "0",
+ policyId: null,
+ targetFilter: null,
+ deletedAt: null,
+ assignments: [
+ {
+ id: "0",
+ variableSetId: "0",
+ environmentId: "0",
+ variableSet: {
+ id: "0",
+ name: "test",
+ description: null,
+ systemId: "0",
+ values: [
+ {
+ id: "0",
+ value: "test4",
+ key: "test",
+ variableSetId: "0",
+ },
+ ],
+ },
+ },
+ ],
+ });
+
+ const result = await jobVariablesDeployment.determineVariablesForReleaseJob(
+ db,
+ {
+ ...job.job,
+ releaseJobTrigger: job.release_job_trigger,
+ },
+ );
+
+ expect(result).toHaveLength(1);
+ expect(result[0]!.value).toEqual(variableValues[1]!.value);
+ });
+
+ it("should not include variables from matched variable set if default value exists and key matches", async () => {
+ vi.mocked(utils.getJob).mockResolvedValue(job);
+
+ const variables: variable[] = [
+ {
+ deployment_variable: {
+ id: "0",
+ config: { type: "string", inputType: "text" },
+ description: "test",
+ deploymentId: "0",
+ key: "test",
+ defaultValueId: "0",
+ },
+ release,
+ },
+ ];
+ vi.mocked(utils.getDeploymentVariables).mockResolvedValue(variables);
+ vi.mocked(utils.getTarget).mockResolvedValue(target);
+
+ vi.mocked(utils.getVariableValues).mockResolvedValue(variableValues);
+ vi.mocked(utils.getFirstMatchedTarget).mockResolvedValue(null);
+
+ vi.mocked(utils.getEnvironment).mockResolvedValue({
+ id: "0",
+ name: "test",
+ description: null,
+ systemId: "0",
+ policyId: null,
+ targetFilter: null,
+ deletedAt: null,
+ assignments: [
+ {
+ id: "0",
+ variableSetId: "0",
+ environmentId: "0",
+ variableSet: {
+ id: "0",
+ name: "test",
+ description: null,
+ systemId: "0",
+ values: [
+ {
+ id: "0",
+ value: "test4",
+ key: "test",
+ variableSetId: "0",
+ },
+ ],
+ },
+ },
+ ],
+ });
+
+ const result = await jobVariablesDeployment.determineVariablesForReleaseJob(
+ db,
+ {
+ ...job.job,
+ releaseJobTrigger: job.release_job_trigger,
+ },
+ );
+
+ expect(result).toHaveLength(1);
+ expect(result[0]!.value).toEqual(variableValues[0]!.value);
+ });
+
+ it("should override value with variable set value if key matches and no direct match or default value exists", async () => {
+ vi.mocked(utils.getJob).mockResolvedValue(job);
+
+ const variables: variable[] = [
+ {
+ deployment_variable: {
+ id: "0",
+ config: { type: "string", inputType: "text" },
+ description: "test",
+ deploymentId: "0",
+ key: "test",
+ defaultValueId: null,
+ },
+ release,
+ },
+ ];
+ vi.mocked(utils.getDeploymentVariables).mockResolvedValue(variables);
+ vi.mocked(utils.getTarget).mockResolvedValue(target);
+
+ vi.mocked(utils.getVariableValues).mockResolvedValue(variableValues);
+ vi.mocked(utils.getFirstMatchedTarget).mockResolvedValue(null);
+
+ vi.mocked(utils.getEnvironment).mockResolvedValue({
+ id: "0",
+ name: "test",
+ description: null,
+ systemId: "0",
+ policyId: null,
+ targetFilter: null,
+ deletedAt: null,
+ assignments: [
+ {
+ id: "0",
+ variableSetId: "0",
+ environmentId: "0",
+ variableSet: {
+ id: "0",
+ name: "test",
+ description: null,
+ systemId: "0",
+ values: [
+ {
+ id: "0",
+ value: "test4",
+ key: "test",
+ variableSetId: "0",
+ },
+ ],
+ },
+ },
+ ],
+ });
+
+ const result = await jobVariablesDeployment.determineVariablesForReleaseJob(
+ db,
+ {
+ ...job.job,
+ releaseJobTrigger: job.release_job_trigger,
+ },
+ );
+
+ expect(result).toHaveLength(1);
+ expect(result[0]!.value).toEqual("test4");
+ });
+
+ it("should override value with variable set value from first alphabetically sorted variable set if no direct match or default value exists and more than one variable set is assigned", async () => {
+ vi.mocked(utils.getJob).mockResolvedValue(job);
+
+ const variables: variable[] = [
+ {
+ deployment_variable: {
+ id: "0",
+ config: { type: "string", inputType: "text" },
+ description: "test",
+ deploymentId: "0",
+ key: "test",
+ defaultValueId: null,
+ },
+ release,
+ },
+ ];
+ vi.mocked(utils.getDeploymentVariables).mockResolvedValue(variables);
+ vi.mocked(utils.getTarget).mockResolvedValue(target);
+
+ vi.mocked(utils.getVariableValues).mockResolvedValue(variableValues);
+ vi.mocked(utils.getFirstMatchedTarget).mockResolvedValue(null);
+
+ vi.mocked(utils.getEnvironment).mockResolvedValue({
+ id: "0",
+ name: "test",
+ description: null,
+ systemId: "0",
+ policyId: null,
+ targetFilter: null,
+ deletedAt: null,
+ assignments: [
+ {
+ id: "0",
+ variableSetId: "0",
+ environmentId: "0",
+ variableSet: {
+ id: "0",
+ name: "testb",
+ description: null,
+ systemId: "0",
+ values: [
+ {
+ id: "0",
+ value: "test4",
+ key: "test",
+ variableSetId: "0",
+ },
+ ],
+ },
+ },
+ {
+ id: "1",
+ variableSetId: "1",
+ environmentId: "0",
+ variableSet: {
+ id: "1",
+ name: "testa",
+ description: null,
+ systemId: "0",
+ values: [
+ {
+ id: "1",
+ value: "test5",
+ key: "test",
+ variableSetId: "1",
+ },
+ ],
+ },
+ },
+ ],
+ });
+
+ const result = await jobVariablesDeployment.determineVariablesForReleaseJob(
+ db,
+ {
+ ...job.job,
+ releaseJobTrigger: job.release_job_trigger,
+ },
+ );
+
+ expect(result).toHaveLength(1);
+ expect(result[0]!.value).toEqual("test5");
+ });
+});
diff --git a/packages/job-dispatch/src/job-variables-deployment.ts b/packages/job-dispatch/src/job-variables-deployment.ts
deleted file mode 100644
index 70ce86a6b..000000000
--- a/packages/job-dispatch/src/job-variables-deployment.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-import type { Tx } from "@ctrlplane/db";
-import { isPresent } from "ts-is-present";
-
-import { and, eq, takeFirstOrNull } from "@ctrlplane/db";
-import * as schema from "@ctrlplane/db/schema";
-
-export const createReleaseVariables = async (
- tx: Tx,
- jobId: string,
-): Promise => {
- // Fetch the job and its associated deployment
- const job = await tx
- .select()
- .from(schema.job)
- .innerJoin(
- schema.releaseJobTrigger,
- eq(schema.releaseJobTrigger.jobId, schema.job.id),
- )
- .where(eq(schema.job.id, jobId))
- .then(takeFirstOrNull);
-
- if (job == null) throw new Error(`Job with id ${jobId} not found`);
-
- const jobVariables = await determineVariablesForReleaseJob(tx, {
- ...job.job,
- releaseJobTrigger: job.release_job_trigger,
- });
-
- if (jobVariables.length > 0)
- await tx.insert(schema.jobVariable).values(jobVariables);
-};
-
-const determineVariablesForReleaseJob = async (
- tx: Tx,
- job: schema.Job & { releaseJobTrigger: schema.ReleaseJobTrigger },
-): Promise => {
- const variables = await tx
- .select()
- .from(schema.deploymentVariable)
- .innerJoin(
- schema.release,
- eq(schema.release.deploymentId, schema.deploymentVariable.deploymentId),
- )
- .where(eq(schema.release.id, job.releaseJobTrigger.releaseId));
-
- if (variables.length === 0) return [];
-
- const jobTarget = await tx
- .select()
- .from(schema.target)
- .where(eq(schema.target.id, job.releaseJobTrigger.targetId))
- .then(takeFirstOrNull);
-
- if (!jobTarget) throw new Error(`Target not found for job ${job.id}`);
-
- const jobVariables: schema.JobVariable[] = [];
-
- await Promise.all(
- variables.map((variable) =>
- determineReleaseVariableValue(
- tx,
- variable.deployment_variable.id,
- variable.deployment_variable.defaultValueId,
- jobTarget,
- ).then(
- (value) =>
- value != null &&
- jobVariables.push({
- jobId: job.id,
- key: variable.deployment_variable.key,
- value: value.value,
- }),
- ),
- ),
- );
-
- return jobVariables;
-};
-
-const determineReleaseVariableValue = async (
- tx: Tx,
- variableId: string,
- defaultValueId: string | null,
- jobTarget: schema.Target,
-): Promise => {
- const deploymentVariableValues = await tx
- .select()
- .from(schema.deploymentVariableValue)
- .orderBy(schema.deploymentVariableValue.value)
- .where(eq(schema.deploymentVariableValue.variableId, variableId));
-
- if (deploymentVariableValues.length === 0) return null;
-
- const defaultValue = deploymentVariableValues.find(
- (v) => v.id === defaultValueId,
- );
- const valuesWithFilters = deploymentVariableValues.filter(
- (v) => v.targetFilter != null,
- );
-
- const valuesMatchedByFilter = await Promise.all(
- valuesWithFilters.map(async (value) => {
- const matchedTarget = await tx
- .select()
- .from(schema.target)
- .where(
- and(
- eq(schema.target.id, jobTarget.id),
- schema.targetMatchesMetadata(tx, value.targetFilter),
- ),
- )
- .then(takeFirstOrNull);
-
- if (matchedTarget != null) return value;
- }),
- ).then((values) => values.filter(isPresent));
-
- if (valuesMatchedByFilter.length > 0) return valuesMatchedByFilter[0]!;
-
- if (defaultValue != null) return defaultValue;
-
- return deploymentVariableValues[0]!;
-};
diff --git a/packages/job-dispatch/src/job-variables-deployment/job-variables-deployment.ts b/packages/job-dispatch/src/job-variables-deployment/job-variables-deployment.ts
new file mode 100644
index 000000000..2ae80087e
--- /dev/null
+++ b/packages/job-dispatch/src/job-variables-deployment/job-variables-deployment.ts
@@ -0,0 +1,148 @@
+import type { Tx } from "@ctrlplane/db";
+
+import * as schema from "@ctrlplane/db/schema";
+
+import * as utils from "./utils.js";
+
+export const createReleaseVariables = async (
+ tx: Tx,
+ jobId: string,
+): Promise => {
+ // Fetch the job and its associated deployment
+ const job = await utils.getJob(tx, jobId);
+
+ if (job == null) throw new Error(`Job with id ${jobId} not found`);
+
+ const jobVariables = await determineVariablesForReleaseJob(tx, {
+ ...job.job,
+ releaseJobTrigger: job.release_job_trigger,
+ });
+
+ if (jobVariables.length > 0)
+ await tx.insert(schema.jobVariable).values(jobVariables);
+};
+
+export const determineVariablesForReleaseJob = async (
+ tx: Tx,
+ job: schema.Job & { releaseJobTrigger: schema.ReleaseJobTrigger },
+): Promise => {
+ const variables = await utils.getDeploymentVariables(
+ tx,
+ job.releaseJobTrigger.releaseId,
+ );
+
+ if (variables.length === 0) return [];
+
+ const jobTarget = await utils.getTarget(tx, job.releaseJobTrigger.targetId);
+
+ if (!jobTarget) throw new Error(`Target not found for job ${job.id}`);
+
+ const jobVariables: schema.JobVariable[] = [];
+
+ const directMatches: string[] = [];
+
+ await Promise.all(
+ variables.map((variable) =>
+ determineReleaseVariableValue(
+ tx,
+ variable.deployment_variable.id,
+ variable.deployment_variable.defaultValueId,
+ jobTarget,
+ ).then((value) => {
+ if (value == null) return;
+
+ jobVariables.push({
+ jobId: job.id,
+ key: variable.deployment_variable.key,
+ value: value.value.value,
+ });
+
+ if (value.directMatch)
+ directMatches.push(variable.deployment_variable.key);
+ }),
+ ),
+ );
+
+ const env = await utils.getEnvironment(
+ tx,
+ job.releaseJobTrigger.environmentId,
+ );
+
+ if (!env) return jobVariables;
+ const assignments = env.assignments.sort((a, b) =>
+ a.variableSet.name.localeCompare(b.variableSet.name),
+ );
+
+ for (const assignment of assignments) {
+ const { variableSet } = assignment;
+ for (const val of variableSet.values) {
+ const existingKeys = jobVariables.map((v) => v.key);
+ if (!existingKeys.includes(val.key)) {
+ jobVariables.push({
+ jobId: job.id,
+ key: val.key,
+ value: val.value,
+ });
+ directMatches.push(val.key);
+ continue;
+ }
+
+ if (directMatches.includes(val.key)) continue;
+
+ const existingVariableIdx = jobVariables.findIndex(
+ (v) => v.key === val.key,
+ );
+
+ if (existingVariableIdx === -1) continue;
+
+ jobVariables[existingVariableIdx]!.value = val.value;
+ directMatches.push(val.key);
+ }
+ }
+
+ return jobVariables;
+};
+
+export const determineReleaseVariableValue = async (
+ tx: Tx,
+ variableId: string,
+ defaultValueId: string | null,
+ jobTarget: schema.Target,
+): Promise<{
+ value: schema.DeploymentVariableValue;
+ directMatch: boolean;
+} | null> => {
+ const deploymentVariableValues = await utils.getVariableValues(
+ tx,
+ variableId,
+ );
+
+ if (deploymentVariableValues.length === 0) return null;
+
+ const defaultValue = deploymentVariableValues.find(
+ (v) => v.id === defaultValueId,
+ );
+
+ const firstMatchedValue = await utils.getFirstMatchedTarget(
+ tx,
+ jobTarget.id,
+ deploymentVariableValues,
+ );
+
+ if (firstMatchedValue != null)
+ return {
+ value: firstMatchedValue,
+ directMatch: true,
+ };
+
+ if (defaultValue != null)
+ return {
+ value: defaultValue,
+ directMatch: true,
+ };
+
+ return {
+ value: deploymentVariableValues[0]!,
+ directMatch: false,
+ };
+};
diff --git a/packages/job-dispatch/src/job-variables-deployment/utils.ts b/packages/job-dispatch/src/job-variables-deployment/utils.ts
new file mode 100644
index 000000000..5e07ea768
--- /dev/null
+++ b/packages/job-dispatch/src/job-variables-deployment/utils.ts
@@ -0,0 +1,81 @@
+import type { Tx } from "@ctrlplane/db";
+import type { TargetCondition } from "@ctrlplane/validators/targets";
+
+import { and, eq, takeFirstOrNull } from "@ctrlplane/db";
+import * as SCHEMA from "@ctrlplane/db/schema";
+
+export const getJob = (tx: Tx, jobId: string) =>
+ tx
+ .select()
+ .from(SCHEMA.job)
+ .innerJoin(
+ SCHEMA.releaseJobTrigger,
+ eq(SCHEMA.releaseJobTrigger.jobId, SCHEMA.job.id),
+ )
+ .where(eq(SCHEMA.job.id, jobId))
+ .then(takeFirstOrNull);
+
+export const getDeploymentVariables = (tx: Tx, releaseId: string) =>
+ tx
+ .select()
+ .from(SCHEMA.deploymentVariable)
+ .innerJoin(
+ SCHEMA.release,
+ eq(SCHEMA.release.deploymentId, SCHEMA.deploymentVariable.deploymentId),
+ )
+ .where(eq(SCHEMA.release.id, releaseId));
+
+export const getTarget = (tx: Tx, targetId: string) =>
+ tx
+ .select()
+ .from(SCHEMA.target)
+ .where(eq(SCHEMA.target.id, targetId))
+ .then(takeFirstOrNull);
+
+export const getEnvironment = (tx: Tx, environmentId: string) =>
+ tx.query.environment.findFirst({
+ where: eq(SCHEMA.environment.id, environmentId),
+ with: {
+ assignments: { with: { variableSet: { with: { values: true } } } },
+ },
+ });
+
+export const getVariableValues = (tx: Tx, variableId: string) =>
+ tx
+ .select()
+ .from(SCHEMA.deploymentVariableValue)
+ .orderBy(SCHEMA.deploymentVariableValue.value)
+ .where(eq(SCHEMA.deploymentVariableValue.variableId, variableId));
+
+export const getMatchedTarget = (
+ tx: Tx,
+ targetId: string,
+ targetFilter: TargetCondition | null,
+) =>
+ tx
+ .select()
+ .from(SCHEMA.target)
+ .where(
+ and(
+ eq(SCHEMA.target.id, targetId),
+ SCHEMA.targetMatchesMetadata(tx, targetFilter),
+ ),
+ )
+ .then(takeFirstOrNull);
+
+export const getFirstMatchedTarget = (
+ tx: Tx,
+ targetId: string,
+ values: SCHEMA.DeploymentVariableValue[],
+) => {
+ const promises = values.map(async (value) => {
+ const matchedTarget = await getMatchedTarget(
+ tx,
+ targetId,
+ value.targetFilter,
+ );
+ return matchedTarget != null ? value : null;
+ });
+
+ return Promise.all(promises).then(takeFirstOrNull);
+};
diff --git a/packages/job-dispatch/src/release-job-trigger.ts b/packages/job-dispatch/src/release-job-trigger.ts
index 1929f9b37..c4be30705 100644
--- a/packages/job-dispatch/src/release-job-trigger.ts
+++ b/packages/job-dispatch/src/release-job-trigger.ts
@@ -20,7 +20,7 @@ import {
targetMatchesMetadata,
} from "@ctrlplane/db/schema";
-import { createReleaseVariables } from "./job-variables-deployment.js";
+import { createReleaseVariables } from "./job-variables-deployment/job-variables-deployment.js";
type FilterFunc = (
tx: Tx,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 74628be5b..e8a0d7cdf 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1168,7 +1168,7 @@ importers:
specifier: ^5.0.1
version: 5.0.1(typescript@5.6.2)(vite@5.4.8(@types/node@22.7.4)(terser@5.34.1))
vitest:
- specifier: ^2.1.1
+ specifier: ^2.1.2
version: 2.1.2(@types/node@22.7.4)(jsdom@25.0.1)(terser@5.34.1)
packages/logger:
@@ -1415,7 +1415,7 @@ importers:
version: 1.13.4(eslint@9.11.1(jiti@2.1.0))
eslint-plugin-import:
specifier: ^2.29.1
- version: 2.30.0(eslint@9.11.1(jiti@2.1.0))
+ version: 2.30.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.1.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.1.0))
eslint-plugin-jsx-a11y:
specifier: ^6.9.0
version: 6.10.0(eslint@9.11.1(jiti@2.1.0))
@@ -17921,16 +17921,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.12.0(eslint-import-resolver-node@0.3.9)(eslint@9.11.1(jiti@2.1.0)):
+ eslint-module-utils@2.12.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.1.0))(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint@9.11.1(jiti@2.1.0)):
dependencies:
debug: 3.2.7
optionalDependencies:
+ '@typescript-eslint/parser': 8.7.0(eslint@9.11.1(jiti@2.1.0))(typescript@5.6.2)
eslint: 9.11.1(jiti@2.1.0)
eslint-import-resolver-node: 0.3.9
transitivePeerDependencies:
- supports-color
- eslint-plugin-import@2.30.0(eslint@9.11.1(jiti@2.1.0)):
+ eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.1.0))(typescript@5.6.2))(eslint@9.11.1(jiti@2.1.0)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8
@@ -17941,7 +17942,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.11.1(jiti@2.1.0)
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.0(eslint-import-resolver-node@0.3.9)(eslint@9.11.1(jiti@2.1.0))
+ eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@2.1.0))(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint@9.11.1(jiti@2.1.0))
hasown: 2.0.2
is-core-module: 2.15.1
is-glob: 4.0.3
@@ -17951,6 +17952,8 @@ snapshots:
object.values: 1.2.0
semver: 6.3.1
tsconfig-paths: 3.15.0
+ optionalDependencies:
+ '@typescript-eslint/parser': 8.7.0(eslint@9.11.1(jiti@2.1.0))(typescript@5.6.2)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack