UNPKG

tilt-ts-core

Version:

A TypeScript implementation of a Tilt-like development tool for Kubernetes live development workflows

719 lines (697 loc) 21.7 kB
import { ZodTypeAny } from 'zod'; import { EventEmitter } from 'events'; /** * Registry configuration for mapping between host and cluster URLs */ type RegistryConfig = { /** URL used for pushing images from the host (e.g., "localhost:36269") */ hostUrl: string; /** URL used by the cluster to pull images (e.g., "k3d-registry:5000") */ clusterUrl?: string; /** Registry username for authentication */ username?: string; /** Registry password for authentication */ password?: string; }; /** * Sets the default registry configuration for docker_build operations * * @param config - Registry configuration with host and cluster URLs * @returns The registry configuration for chaining * * @example * ```typescript * // Set up registry for k3d environment * default_registry({ * hostUrl: "localhost:36269", * clusterUrl: "k3d-registry:5000" * }); * * // Now docker_build will use this registry by default * const built = await docker_build("my-app", "./context", { * dockerfile: "./Dockerfile" * }); * * // Use the cluster image name directly * await k8s_yaml("./deploy.yaml") * .updateImages(() => built.clusterImageName) * .apply(); * ``` */ declare function default_registry(config: RegistryConfig): RegistryConfig; /** * Gets the current default registry configuration */ declare function get_default_registry(): RegistryConfig | null; /** * Resets the default registry configuration (useful for testing) */ declare function reset_default_registry(): void; type LiveSync = { type: "sync"; src: string; dest: string; include?: string[]; exclude?: string[]; }; type LiveRun = { type: "run"; cmd: string[]; dir?: string; env?: Record<string, string>; whenFilesChanged?: string[]; }; type LiveStep = LiveSync | LiveRun; type ResourceSelector = { kind: "Deployment" | "StatefulSet" | "DaemonSet"; name: string; namespace?: string; container?: string; labelSelector?: string; }; type DockerBuildOpts = { dockerfile?: string; args?: Record<string, string>; target?: string; tags?: string[]; live_update?: LiveStep[]; cache?: boolean; registry?: RegistryConfig; }; type BuiltImage = { logicalName: string; /** Image reference used for pushing from host (e.g., "localhost:36269/dev/my-app:dev-123") */ imageRef: string; /** Image reference used by cluster (e.g., "k3d-registry:5000/dev/my-app:dev-123") */ clusterImageName: string; digest?: string; live_update?: LiveStep[]; }; type ApplyInput = { type: "yamlText"; text: string; } | { type: "yamlFile"; path: string; } | { type: "yamlFiles"; paths: string[]; }; type K8sApplyOpts = { rewriteImages?: Record<string, string>; }; type K8sApplyOptions = { validate?: boolean; dryRun?: boolean; validateContext?: boolean; cache?: boolean; resourceName?: string; continueOnError?: boolean; }; type LiveUpdateBinding = { selector: ResourceSelector; steps: LiveStep[]; }; type RunOpts = { cwd?: string; env?: Record<string, string>; stdin?: "inherit" | "null"; }; declare function docker_build(logicalName: string, contextDir: string, opts?: DockerBuildOpts): Promise<BuiltImage>; type K8sResource = { apiVersion?: string; kind?: string; metadata?: { name?: string; namespace?: string; labels?: Record<string, string>; annotations?: Record<string, string>; }; spec?: any; data?: any; [key: string]: any; }; declare class YamlWrapper { private resources; constructor(input: string | K8sResource[]); /** * Transform each resource with a function */ map(transform: (resource: K8sResource, index: number) => K8sResource): YamlWrapper; /** * Filter resources by predicate */ filter(predicate: (resource: K8sResource) => boolean): YamlWrapper; /** * Update container images using a transform function */ updateImages(transformFn: (image: string) => string): YamlWrapper; /** * Get all resources as array */ toArray(): K8sResource[]; /** * Convert to YAML string */ toYaml(): string; /** * Get count of resources */ count(): number; } declare const byKind: (kind: string) => (resource: K8sResource) => boolean; declare const byName: (name: string) => (resource: K8sResource) => boolean; declare const byNamespace: (namespace: string) => (resource: K8sResource) => boolean; declare class K8sApplier extends YamlWrapper { private resourceId?; _sourceFiles?: string[]; constructor(input: string | K8sResource[]); /** * Extract docker image names that this k8s resource depends on */ private extractImageDependencies; /** * Recursively find image references in a k8s resource */ private findImagesInResource; /** * Extract logical image name from full image reference * e.g., "docker.io/frank1147/usercode:tag" -> "frank1147/usercode" */ private extractLogicalName; /** * Generate flat temporary file for per-apply operations * Uses existing k8s-apply-{hash}.yaml approach */ private generateFlatTempFile; /** * Generate a resource name based on the k8s resources contained */ private generateResourceName; log(): this; map(transform: (resource: K8sResource, index: number) => K8sResource): K8sApplier; filter(predicate: (resource: K8sResource) => boolean): K8sApplier; updateImages(transformFn: (image: string) => string): K8sApplier; /** * Apply the YAML resources to the Kubernetes cluster */ apply(options?: Partial<K8sApplyOptions>): Promise<void>; } /** * Create a new K8sApplier from YAML input * * @param input - YAML string * @returns K8sApplier for chaining operations * * @example * ```typescript * await k8s(yamlString) * .filter(byKind("Deployment")) * .updateImages(image => `registry.com/${image}`) * .apply(); * ``` */ declare function k8s(input: string): K8sApplier; /** * Smart YAML loader that automatically detects input type * * @param input - File path, YAML content string, or array of mixed inputs * @returns K8sApplier for chaining operations * * @example * ```typescript * // Load from file * await k8s_yaml("./deploy.yaml") * .updateImages(image => `registry.com/${image}`) * .apply(); * * // Load from YAML string * await k8s_yaml(yamlContent) * .filter(byKind("Deployment")) * .apply(); * * // Load from multiple mixed sources * await k8s_yaml(["./app.yaml", yamlString, "./service.yaml"]) * .updateImages(image => `registry.com/${image}`) * .apply(); * ``` */ declare function k8s_yaml(input: string | string[]): K8sApplier; /** * Load YAML from one or more files with wildcard support and early validation * * @param paths - File paths or glob patterns (supports *, ?, []) * @returns K8sApplier for chaining operations * * @example * ```typescript * // Single file * await k8s_file("./deploy/app.yaml") * .updateImages(image => `registry.com/${image}`) * .apply(); * * // Multiple files * await k8s_file("./app.yaml", "./service.yaml", "./ingress.yaml") * .filter(byKind("Deployment")) * .apply(); * * // Wildcard patterns * await k8s_file("./deploy/*.yaml", "./config/**\/*.yml") * .apply(); * ``` */ declare function k8s_file(...paths: string[]): K8sApplier; /** * Start live updates for a specific binding (legacy mode) */ declare function live_update(bind: LiveUpdateBinding): Promise<void>; /** * Start live updates using the LiveUpdateManager (new mode) * This is the main function to be called at the end of tiltfile.ts */ declare function live_update(): Promise<void>; /** * Get the current Kubernetes context */ declare function k8s_context(): Promise<string>; /** * Allow additional Kubernetes contexts for Tilt operations * Similar to Tilt's allow_k8s_contexts function * * @param contexts - A string or array of context names to allow */ declare function allow_k8s_contexts(contexts: string | string[]): void; /** * Validate that the current context is safe for development * Throws an error if the context is not allowed */ declare function validate_k8s_context(): Promise<void>; /** * Set the Kubernetes context after validating it's safe * This function combines setting and allowing the context in one step * * @param context - The context name to switch to * @param validate - Whether to validate the context is safe (default: true) */ declare function set_k8s_context(context: string, validate?: boolean): Promise<void>; /** * Reset allowed contexts to defaults * Useful for testing or resetting state */ declare function reset_allowed_contexts(): void; /** * Set the current Kubernetes namespace * * @param namespace - The namespace to set as current */ declare function set_k8s_namespace(namespace: string): Promise<void>; /** * Get currently allowed contexts and patterns * Useful for debugging and introspection */ declare function get_allowed_contexts(): { contexts: string[]; patterns: string[]; }; declare function sync(src: string, dest: string, include?: string[], exclude?: string[]): LiveSync; declare function run(cmd: string[], whenFilesChanged: string[], options?: { dir?: string; env?: Record<string, string>; } | undefined): LiveRun; declare class ConfigMap { name: string; data: Record<string, string>; binaryData: Record<string, string>; constructor(name: string); static fromFile(name: string, ...files: Array<{ key?: string; path: string; } | string>): ConfigMap; static fromDirectory(name: string, dirPath: string): ConfigMap; private addFile; private isUtf8; toJSON(): Record<string, any>; toYAML(): string; } type SecretType = "Opaque" | "kubernetes.io/dockerconfigjson" | "kubernetes.io/basic-auth" | "kubernetes.io/ssh-auth" | "kubernetes.io/tls"; type Value = string | Buffer; type BuilderOpts = { type?: SecretType; namespace?: string; labels?: Record<string, string>; annotations?: Record<string, string>; useStringData?: boolean; schema?: ZodTypeAny; }; declare class Secret { name: string; namespace?: string; type: SecretType; labels: Record<string, string>; annotations: Record<string, string>; private useStringDataFlag; private data; private stringData; private constructor(); static create(name: string): Secret; static fromValues(name: string, values: Record<string, Value>, opts?: BuilderOpts): Secret; static fromEnvFile(name: string, envFilePath: string, opts?: BuilderOpts): Secret; static dockerConfigJson(name: string, dockerConfig: Record<string, unknown> | string, opts?: Omit<BuilderOpts, "schema">): Secret; setNamespace(ns: string): this; setType(t: SecretType): this; setLabels(labels: Record<string, string>): this; setAnnotations(annotations: Record<string, string>): this; useStringData(): this; add(key: string, value: Value): this; addFile(keyOrPath: string, filePath?: string): this; addFiles(...files: Array<{ key?: string; path: string; } | string>): this; toJSON(): Record<string, unknown>; toYAML(): string; private b64; private isProbablyText; } declare function exec(cmd: string[], opts?: RunOpts): Promise<void>; declare function execCapture(cmd: string[], opts?: Omit<RunOpts, "stdin">): Promise<string>; declare class Logger { private logger; debug(message: string, attributes?: Record<string, any>): void; info(message: string, attributes?: Record<string, any>): void; warn(message: string, attributes?: Record<string, any>): void; error(message: string, attributes?: Record<string, any>): void; } declare const logger: Logger; /** * Registry entry for a built image */ type ImageRegistryEntry = { builtImage: BuiltImage; registryConfig?: RegistryConfig; }; /** * Global registry to track built images and their live update configurations */ declare class ImageRegistry { private images; private stateFile; constructor(); private saveState; private loadState; /** * Register a built image with its logical name and live update config */ register(logicalName: string, builtImage: BuiltImage, registryConfig?: RegistryConfig): void; /** * Get a registered image by logical name */ get(logicalName: string): ImageRegistryEntry | undefined; /** * Get all registered images */ getAll(): ImageRegistryEntry[]; /** * Check if a logical name is registered */ has(logicalName: string): boolean; /** * Clear all registered images (useful for testing) */ clear(): void; /** * Force reload state from disk (useful when called from different module contexts) */ forceReloadState(sessionId?: string): void; /** * Clean up state file (call at session end) */ cleanup(): void; /** * Get all images that have live update configurations */ getLiveUpdateImages(): ImageRegistryEntry[]; } declare const imageRegistry: ImageRegistry; /** * Sets the default K8s apply options for all K8sApplier.apply() operations * * @param options - Default K8s apply options * @returns The options for chaining * * @example * ```typescript * // Set global defaults for all k8s applies * k8s_defaults({ * validate: true, * continueOnError: false, * cache: true * }); * * // Now all k8s applies will use these defaults unless overridden * await k8s_yaml("./deploy.yaml").apply(); // Uses global defaults * * // Local options override globals * await k8s_yaml("./deploy.yaml").apply({ * validate: false // This overrides the global validate: true * }); * ``` */ declare function k8s_defaults(options: Partial<K8sApplyOptions>): K8sApplyOptions; /** * Gets the current default K8s apply options configuration */ declare function get_k8s_defaults(): K8sApplyOptions; /** * Resets the default K8s apply options configuration (useful for testing) */ declare function reset_k8s_defaults(): void; /** * Global ignore patterns management for file watchers * * Provides a centralized system for managing ignore patterns that can be * extended via CLI flags or programmatic configuration. */ declare const DEFAULT_IGNORE_PATTERNS: string[]; /** * Set global ignore patterns by combining defaults with custom patterns * @param customPatterns Additional patterns to add to defaults */ declare function setGlobalIgnorePatterns(customPatterns: string[]): void; /** * Get current global ignore patterns * @returns Copy of current ignore patterns array */ declare function getGlobalIgnorePatterns(): string[]; /** * Reset ignore patterns to defaults only */ declare function resetIgnorePatterns(): void; /** * Get only the custom patterns (excluding defaults) * @returns Custom patterns that were added */ declare function getCustomIgnorePatterns(): string[]; /** * Singleton manager for coordinating live updates across all resources */ declare class LiveUpdateManager { private static instance; private registrations; private correlations; private isStarted; private stateFile; private constructor(); static getInstance(): LiveUpdateManager; private saveState; private loadState; /** * Register a built image for live updates */ registerImage(builtImage: BuiltImage): void; /** * Register a Kubernetes resource for live updates */ registerDeployment(resource: K8sResource, source?: string): void; /** * Force reload state from disk (useful when called from different module contexts) */ forceReloadState(sessionId?: string): void; /** * Clean up state file (call at session end) */ cleanup(): void; /** * Get all registered images */ private getRegisteredImages; /** * Get all registered deployments */ private getRegisteredDeployments; /** * Correlate images with deployments and start live updates */ start(): Promise<void>; /** * Stop all live updates and clean up */ stop(): Promise<void>; /** * Get current status for debugging */ getStatus(): { isStarted: boolean; registrationCount: number; correlationCount: number; images: string[]; deployments: string[]; }; /** * Reset manager state (useful for testing) */ reset(): void; } declare const liveUpdateManager: LiveUpdateManager; type ResourceType = 'docker_build' | 'k8s_file' | 'k8s_yaml'; type ResourceStatus = 'pending' | 'building' | 'ready' | 'error'; type TrackedResource = { id: string; type: ResourceType; name: string; status: ResourceStatus; path?: string; context?: string; imageRef?: string; buildTime?: number; dependencies?: string[]; lastUpdate: Date; error?: string; }; /** * Tracks resources deployed by tiltfile.ts and emits events for UI consumption */ declare class ResourceTracker extends EventEmitter { private resources; private idCounter; private static instance; private constructor(); static getInstance(): ResourceTracker; /** * Track a docker build operation */ trackDockerBuild(name: string, context: string): string; /** * Track a k8s file application */ trackK8sFile(path: string, dependencies?: string[]): string; /** * Track a k8s yaml application */ trackK8sYaml(name: string, dependencies?: string[]): string; /** * Update resource status */ updateStatus(id: string, status: ResourceStatus, updates?: Partial<TrackedResource>): void; /** * Mark build as started and track build time */ startBuild(id: string): void; /** * Mark build as completed with results */ completeBuild(id: string, imageRef?: string, buildTimeMs?: number): void; /** * Mark resource as failed with error */ markError(id: string, error: string): void; /** * Get a resource by ID */ get(id: string): TrackedResource | undefined; /** * Get all tracked resources */ getAll(): TrackedResource[]; /** * Get resources by type */ getByType(type: ResourceType): TrackedResource[]; /** * Get docker builds that a k8s resource depends on */ getDependencies(resourceId: string): TrackedResource[]; /** * Find k8s resources that depend on a docker build */ getDependents(dockerBuildName: string): TrackedResource[]; /** * Clear all tracked resources */ reset(): void; /** * Get summary statistics */ getStats(): { total: number; byStatus: Record<ResourceStatus, number>; byType: Record<ResourceType, number>; }; /** * Emit events to console for UI consumption */ private emitToConsole; } declare const resourceTracker: ResourceTracker; interface WetRepositoryConfig { path: string; stage?: string; flatStructure?: boolean; } interface WetRepositoryOptions { flatStructure?: boolean; } /** * Configure wet repository settings for materialized YAML output * * @param pathOrConfig - Repository path string or full config object * @param stage - Stage/environment subdirectory (optional) * @param options - Additional options like flatStructure * * @example * ```typescript * // Basic usage * wet_repository("./wet-repo", "dev"); * * // With flat structure (no resource type subfolders) * wet_repository("./wet-repo", "dev", { flatStructure: true }); * * // Full config object * wet_repository({ * path: "./wet-repo", * stage: "dev", * flatStructure: false * }); * ``` */ declare function wet_repository(pathOrConfig: string | WetRepositoryConfig, stage?: string, options?: WetRepositoryOptions): void; /** * Get the current wet repository configuration * Returns default .tilt-ts configuration if none set */ declare function getWetRepositoryConfig(): WetRepositoryConfig; /** * Reset wet repository configuration to default */ declare function resetWetRepositoryConfig(): void; /** * Tiltfile start hook - initialize new run tracking * Call this at the beginning of tiltfile execution */ declare function __tiltfile_start_hook(): Promise<void>; /** * Tiltfile completion hook - cleanup orphaned files and generate comprehensive kustomization * Call this at the end of tiltfile execution (in finally block) */ declare function __tiltfile_completion_hook(): Promise<void>; export { type ApplyInput, type BuiltImage, ConfigMap, DEFAULT_IGNORE_PATTERNS, type DockerBuildOpts, type K8sApplyOptions, type K8sApplyOpts, type LiveRun, type LiveStep, type LiveSync, type LiveUpdateBinding, type ResourceSelector, type RunOpts, Secret, YamlWrapper, __tiltfile_completion_hook, __tiltfile_start_hook, allow_k8s_contexts, byKind, byName, byNamespace, default_registry, docker_build, exec, execCapture, getCustomIgnorePatterns, getGlobalIgnorePatterns, getWetRepositoryConfig, get_allowed_contexts, get_default_registry, get_k8s_defaults, imageRegistry, k8s, k8s_context, k8s_defaults, k8s_file, k8s_yaml, liveUpdateManager, live_update, logger, resetIgnorePatterns, resetWetRepositoryConfig, reset_allowed_contexts, reset_default_registry, reset_k8s_defaults, resourceTracker, run, setGlobalIgnorePatterns, set_k8s_context, set_k8s_namespace, sync, validate_k8s_context, wet_repository };