All files / src/entities/variables schema.ts

100% Statements 23/23
83.33% Branches 5/6
100% Functions 1/1
100% Lines 23/23

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 1354x 4x                   4x   12x 12x 12x 7x   5x 4x     1x           4x 4x         4x         4x           4x         4x         4x         4x       4x                         4x                                   4x                                       4x               4x                      
import { z } from "zod";
import { flexibleBoolean } from "../utils";
 
// ============================================================================
// manage_variable - CQRS Command Tool (discriminated union schema)
// Actions: create, update, delete
// Uses z.discriminatedUnion() for type-safe action handling.
// Schema pipeline flattens to flat JSON Schema for AI clients that don't support oneOf.
// ============================================================================
 
// --- Helper for variable type validation with intelligent coercion ---
const flexibleVariableType = z.preprocess(
  val => {
    Eif (typeof val === "string") {
      const normalized = val.toLowerCase().trim();
      if (["env_var", "env", "environment", "var", "variable"].includes(normalized)) {
        return "env_var";
      }
      if (["file", "file_var"].includes(normalized)) {
        return "file";
      }
    }
    return val;
  },
  z.enum(["env_var", "file"])
);
 
// --- Shared fields ---
const namespaceField = z.string().describe("Namespace path (group or project)");
const keyField = z
  .string()
  .describe(
    "The key of the CI/CD variable. Maximum 255 characters, only alphanumeric and underscore characters allowed."
  );
const variableTypeField = flexibleVariableType
  .optional()
  .describe(
    'The type of variable: "env_var" for environment variables (default) or "file" for file variables.'
  );
const environmentScopeField = z
  .string()
  .optional()
  .describe(
    'The environment scope. Use "*" for all environments (default), or specify like "production", "staging".'
  );
const protectedField = flexibleBoolean
  .optional()
  .describe(
    "Whether this variable is protected. Protected variables are only available to protected branches/tags."
  );
const maskedField = flexibleBoolean
  .optional()
  .describe(
    "Whether this variable should be masked in job logs. MASKING REQUIREMENTS: Value must be at least 8 characters, single line with no spaces, only A-Z a-z 0-9 + / = . ~ - _ @ : characters."
  );
const rawField = flexibleBoolean
  .optional()
  .describe(
    "Whether variable expansion is disabled. When true, variables like $OTHER_VAR in the value will NOT be expanded."
  );
const descriptionField = z
  .string()
  .optional()
  .describe("Optional description explaining the purpose of this variable (GitLab 16.2+).");
const filterField = z
  .object({
    environment_scope: z
      .string()
      .optional()
      .describe(
        "Filter to specify which environment scope variant to update/delete when multiple variables exist with the same key."
      ),
  })
  .optional()
  .describe("Filter parameters to identify the specific variable");
 
// --- Action: create ---
const CreateVariableSchema = z.object({
  action: z.literal("create").describe("Create a new CI/CD variable"),
  namespace: namespaceField,
  key: keyField,
  value: z
    .string()
    .describe(
      "The value of the CI/CD variable. For file type variables, this is the file content."
    ),
  variable_type: variableTypeField,
  environment_scope: environmentScopeField,
  protected: protectedField,
  masked: maskedField,
  raw: rawField,
  description: descriptionField,
});
 
// --- Action: update ---
const UpdateVariableSchema = z.object({
  action: z.literal("update").describe("Update an existing CI/CD variable"),
  namespace: namespaceField,
  key: keyField,
  value: z
    .string()
    .optional()
    .describe(
      "The value of the CI/CD variable. For file type variables, this is the file content."
    ),
  variable_type: variableTypeField,
  environment_scope: environmentScopeField,
  protected: protectedField,
  masked: maskedField,
  raw: rawField,
  description: descriptionField,
  filter: filterField,
});
 
// --- Action: delete ---
const DeleteVariableSchema = z.object({
  action: z.literal("delete").describe("Delete a CI/CD variable"),
  namespace: namespaceField,
  key: keyField,
  filter: filterField,
});
 
// --- Discriminated union combining all actions ---
export const ManageVariableSchema = z.discriminatedUnion("action", [
  CreateVariableSchema,
  UpdateVariableSchema,
  DeleteVariableSchema,
]);
 
// ============================================================================
// Type exports
// ============================================================================
 
export type ManageVariableInput = z.infer<typeof ManageVariableSchema>;