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
TypeScript
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 };