All files / src/entities/refs schema.ts

100% Statements 16/16
100% Branches 0/0
100% Functions 0/0
100% Lines 16/16

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 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 15531x 31x                   31x           31x         31x                   31x                                 31x               31x             31x                                         31x             31x                 31x                 31x             31x                         31x             31x                                  
import { z } from 'zod';
import { requiredId } from '../utils';
 
// ============================================================================
// manage_ref - CQRS Command Tool (discriminated union schema)
// Actions: create_branch, delete_branch, protect_branch, unprotect_branch,
//          update_branch_protection, create_tag, delete_tag, protect_tag, unprotect_tag
// Uses z.discriminatedUnion() for type-safe action handling.
// ============================================================================
 
// --- Shared fields ---
const projectIdField = requiredId.describe(
  "Project ID or URL-encoded path (e.g., 'my-group/my-project')",
);
 
// Access levels for protection rules
// 0 = No access, 30 = Developers, 40 = Maintainers, 60 = Admins
const accessLevelField = z
  .number()
  .int()
  .describe('Access level: 0=No access, 30=Developers, 40=Maintainers, 60=Admins');
 
const accessListField = z
  .array(
    z.object({
      user_id: z.number().optional().describe('User ID'),
      group_id: z.number().optional().describe('Group ID'),
      access_level: accessLevelField.optional().describe('Access level'),
    }),
  )
  .optional();
 
const branchProtectionAccessFields = {
  allowed_to_push: accessListField.describe(
    'Push access list (EE): role access_level entries work on Free; user_id/group_id entries require Premium or Ultimate. Unapplied entries are reported in not_applied.',
  ),
  allowed_to_merge: accessListField.describe(
    'Merge access list (EE): role access_level entries work on Free; user_id/group_id entries require Premium or Ultimate. Unapplied entries are reported in not_applied.',
  ),
  allowed_to_unprotect: accessListField.describe(
    'Unprotect access list (EE): role access_level entries work on Free; user_id/group_id entries require Premium or Ultimate. Unapplied entries are reported in not_applied.',
  ),
  code_owner_approval_required: z
    .boolean()
    .optional()
    .describe('Require code owner approval (Premium feature)'),
};
 
// --- Action: create_branch ---
const CreateBranchSchema = z.object({
  action: z.literal('create_branch').describe('Create a new branch from an existing ref'),
  project_id: projectIdField,
  branch: z.string().describe('Name for the new branch'),
  ref: z.string().describe('Source branch name, tag, or commit SHA to create from'),
});
 
// --- Action: delete_branch ---
const DeleteBranchSchema = z.object({
  action: z.literal('delete_branch').describe('Delete a branch from the repository'),
  project_id: projectIdField,
  branch: z.string().describe('Branch name to delete'),
});
 
// --- Action: protect_branch ---
const ProtectBranchSchema = z.object({
  action: z.literal('protect_branch').describe('Add protection rules to a branch or pattern'),
  project_id: projectIdField,
  name: z.string().describe("Branch name or wildcard pattern (e.g., 'main', 'release-*')"),
  push_access_level: accessLevelField.optional().describe('Who can push (default: 40=Maintainers)'),
  merge_access_level: accessLevelField
    .optional()
    .describe('Who can merge (default: 40=Maintainers)'),
  unprotect_access_level: accessLevelField
    .optional()
    .describe(
      'Who can unprotect (EE; default: 40=Maintainers). If GitLab does not apply this setting, the result names it in not_applied.',
    ),
  allow_force_push: z
    .boolean()
    .optional()
    .describe('Allow force push to protected branch (default: false)'),
  ...branchProtectionAccessFields,
});
 
// --- Action: unprotect_branch ---
const UnprotectBranchSchema = z.object({
  action: z.literal('unprotect_branch').describe('Remove protection from a branch'),
  project_id: projectIdField,
  name: z.string().describe('Branch name or wildcard pattern to unprotect'),
});
 
// --- Action: update_branch_protection ---
const UpdateBranchProtectionSchema = z.object({
  action: z.literal('update_branch_protection').describe('Update protection rules for a branch'),
  project_id: projectIdField,
  name: z.string().describe('Branch name or wildcard pattern'),
  allow_force_push: z.boolean().optional().describe('Allow force push to protected branch'),
  ...branchProtectionAccessFields,
});
 
// --- Action: create_tag ---
const CreateTagSchema = z.object({
  action: z.literal('create_tag').describe('Create a new tag in the repository'),
  project_id: projectIdField,
  tag_name: z.string().describe("Name for the new tag (e.g., 'v1.0.0')"),
  ref: z.string().describe('Source branch name or commit SHA to create tag from'),
  message: z.string().optional().describe('Annotation message (creates annotated tag if provided)'),
});
 
// --- Action: delete_tag ---
const DeleteTagSchema = z.object({
  action: z.literal('delete_tag').describe('Delete a tag from the repository'),
  project_id: projectIdField,
  tag_name: z.string().describe('Tag name to delete'),
});
 
// --- Action: protect_tag ---
const ProtectTagSchema = z.object({
  action: z.literal('protect_tag').describe('Add protection rules to a tag pattern (all tiers)'),
  project_id: projectIdField,
  name: z.string().describe("Tag name or wildcard pattern (e.g., 'v*', 'release-*')"),
  create_access_level: accessLevelField
    .optional()
    .describe('Who can create matching tags (default: 40=Maintainers)'),
  allowed_to_create: accessListField.describe(
    'Create access list (EE): role access_level entries work on Free; user_id/group_id entries require Premium or Ultimate. Unapplied entries are reported in not_applied.',
  ),
});
 
// --- Action: unprotect_tag ---
const UnprotectTagSchema = z.object({
  action: z.literal('unprotect_tag').describe('Remove protection from a tag pattern (all tiers)'),
  project_id: projectIdField,
  name: z.string().describe('Tag name or wildcard pattern to unprotect'),
});
 
// --- Discriminated union combining all actions ---
export const ManageRefSchema = z.discriminatedUnion('action', [
  CreateBranchSchema,
  DeleteBranchSchema,
  ProtectBranchSchema,
  UnprotectBranchSchema,
  UpdateBranchProtectionSchema,
  CreateTagSchema,
  DeleteTagSchema,
  ProtectTagSchema,
  UnprotectTagSchema,
]);
 
// ============================================================================
// Type exports
// ============================================================================
 
export type ManageRefInput = z.infer<typeof ManageRefSchema>;