Watch Configuration
Configure file watchers to automatically trigger actions when files change. Watch rules provide fine-grained control over what files trigger what actions, enabling efficient development workflows.
Overview
The watch system monitors file changes and executes actions like:
- Rebuild images when Dockerfile or dependencies change
- Reapply Kubernetes manifests when configuration changes
- Restart containers for fast iteration without rebuilds
- Run custom scripts for migrations, tests, or builds
Watch rules are separate from file sync, which handles source code hot reloading.
Quick Example
{
"services": [
{
"name": "backend",
"path": "./backend",
"fileSync": true,
"fileSyncIgnore": ["*.pyc", "__pycache__/"],
"watch": [
{
"patterns": ["Dockerfile", "requirements.txt"],
"actions": ["rebuildImage"],
"description": "Rebuild on infrastructure changes"
},
{
"patterns": ["k8s/**/*.yaml"],
"actions": ["reapplyK8sManifest"],
"description": "Update Kubernetes resources"
}
]
}
]
}
Watch Rule Schema
Required Fields
patterns
Array of glob patterns to watch.
{
"patterns": ["Dockerfile", "requirements.txt", "k8s/**/*.yaml"]
}
Examples:
"Dockerfile"- Exact filename"*.py"- All Python files"src/**/*.ts"- All TypeScript files in src/"db/migrations/**/*.sql"- All SQL files in migrations/
actions
Array of actions to execute sequentially when files match.
{
"actions": ["rebuildImage"]
}
Built-in actions:
rebuildImage- Build, push, and deploy new imagereapplyK8sManifest- Apply Kubernetes manifestsrestartContainer- Fast container restart (5s)
Custom actions:
Reference scripts defined in the scripts object:
{
"scripts": {
"runTests": {
"command": "npm test",
"target": "host"
}
},
"actions": ["runTests", "rebuildImage"]
}
Optional Fields
scripts
Named scripts that can be referenced in actions.
{
"scripts": {
"buildBinary": {
"command": "go build -o /app/main .",
"target": "container",
"workingDir": "src",
"timeout": "5m"
},
"runMigrations": {
"path": "./scripts/migrate.sh",
"target": "container"
}
}
}
Script fields:
command- Inline command (mutually exclusive withpath)path- Path to script file (mutually exclusive withcommand)target- Where to run:"host"(default) or"container"workingDir- Working directory (relative to service path)timeout- Execution timeout (e.g.,"5m","30s") - default:"5m"
onStartup
Run actions once when watch session starts.
{
"onStartup": true
}
Useful for initial setup or ensuring dependencies are installed on startup.
continueOnError
Continue pipeline even if steps fail.
{
"continueOnError": true
}
Default: false (stop on first failure)
Use for non-critical steps like linting.
description
Human-readable description.
{
"description": "Rebuild image when dependencies change"
}
debounce
Debounce time in milliseconds.
{
"debounce": 2000
}
Default: 500ms
Batches rapid file changes (e.g., git checkout) into single execution.
ignorePatterns
Additional patterns to ignore within matches.
{
"patterns": ["**/*.py"],
"ignorePatterns": ["tests/**/*.py"]
}
Built-in Actions
rebuildImage
Builds Docker image, pushes to registry, and updates deployment.
Steps:
docker build -t <image>:<tag> .docker push <image>:<tag>kubectl set image deployment/<service> <service>=<image>:<tag>
Example:
{
"patterns": ["Dockerfile", "requirements.txt"],
"actions": ["rebuildImage"],
"description": "Rebuild when infrastructure changes"
}
When to use:
- Dockerfile changes
- Dependency file changes (requirements.txt, package.json, go.mod)
- Build configuration changes
Note: Automatically deploys new image - no need for restartContainer.
reapplyK8sManifest
Applies all Kubernetes manifests in the service's manifests directory.
Steps:
kubectl apply -f <manifest1>.yamlkubectl apply -f <manifest2>.yaml- ... (one per manifest file)
Example:
{
"patterns": ["k8s/**/*.yaml"],
"actions": ["reapplyK8sManifest"],
"description": "Update Kubernetes resources"
}
When to use:
- ConfigMap changes
- Service port changes
- Resource limit adjustments
- Environment variable updates
Combine with restartContainer for ConfigMap reloads:
{
"patterns": ["k8s/configmap.yaml"],
"actions": ["reapplyK8sManifest", "restartContainer"],
"description": "Update ConfigMap and restart to reload"
}
restartContainer
Performs rolling restart of deployment.
Steps:
kubectl rollout restart deployment/<service>kubectl rollout status deployment/<service>
Example:
{
"patterns": ["**/*.go"],
"actions": ["restartContainer"],
"debounce": 2000,
"description": "Fast restart on Go changes"
}
When to use:
- Fast iteration with in-container builds
- Configuration reloads
- Process restarts without full rebuild
Performance: ~5 seconds (vs ~60 seconds for full rebuild)
Custom Scripts
Host Execution
Run scripts on your local machine.
Use cases:
- Build steps (npm run build, go build)
- Code generation (protoc, swagger-gen)
- Linting and formatting
- Running tests
Example:
{
"patterns": ["**/*.proto"],
"scripts": {
"generateProto": {
"command": "protoc --go_out=. *.proto",
"target": "host",
"workingDir": "src/proto"
}
},
"actions": ["generateProto"]
}
Container Execution
Run scripts inside the running container.
Use cases:
- Database migrations
- Seeding data
- In-container builds
- Application commands
Example:
{
"patterns": ["db/migrations/**/*.sql"],
"scripts": {
"runMigrations": {
"command": "python manage.py migrate",
"target": "container"
}
},
"actions": ["runMigrations"]
}
Note: GWS automatically resolves Kubernetes details (namespace, pod selector, container name).
Script Files
For complex multi-line scripts, use external files:
gws.json:
{
"scripts": {
"deploymentCheck": {
"path": "./scripts/check-deployment.sh",
"target": "host",
"timeout": "2m"
}
},
"actions": ["deploymentCheck"]
}
scripts/check-deployment.sh:
#!/bin/bash
set -e
echo "Checking deployment status..."
kubectl get deployment $SERVICE_NAME
kubectl wait --for=condition=available deployment/$SERVICE_NAME --timeout=120s
echo "Deployment ready!"
Sequential Actions
Execute multiple actions in order.
{
"patterns": ["**/*.go"],
"scripts": {
"runTests": {
"command": "go test ./...",
"target": "host",
"timeout": "10m"
}
},
"actions": ["runTests", "rebuildImage"],
"description": "Test before building"
}
Execution:
- Run tests on host
- If tests pass, rebuild image
- If any step fails, stop (unless
continueOnError: true)
File Sync vs Watch
When to Use File Sync
File Sync (fileSync: true) handles source code:
- Hot reloading
- Instant updates
- No container restart needed
Example files:
*.py,*.js,*.ts,*.go- Templates, views, HTML
- CSS, images, assets
When to Use Watch
Watch (watch: [...]) handles infrastructure:
- Container rebuilds
- Kubernetes updates
- Custom workflows
Example files:
Dockerfilerequirements.txt,package.json,go.modk8s/**/*.yaml- Migration files
Separation Strategy
{
"services": [
{
"name": "backend",
"fileSync": true, // Source code hot reload
"fileSyncIgnore": [
"*.pyc",
"__pycache__/",
"*.log"
],
"watch": [ // Infrastructure changes
{
"patterns": ["Dockerfile"],
"actions": ["rebuildImage"]
},
{
"patterns": ["requirements.txt"],
"actions": ["rebuildImage"]
}
]
}
]
}
Common Patterns
Python Django
{
"fileSync": true,
"fileSyncIgnore": ["*.pyc", "__pycache__/", "*.egg-info/", ".pytest_cache/"],
"watch": [
{
"patterns": ["Dockerfile", "requirements.txt", "pyproject.toml"],
"actions": ["rebuildImage"],
"description": "Rebuild on infrastructure changes"
},
{
"patterns": ["db/migrations/**/*.sql"],
"scripts": {
"runMigrations": {
"command": "python manage.py migrate",
"target": "container"
}
},
"actions": ["runMigrations"],
"description": "Run migrations"
},
{
"patterns": ["k8s/configmap.yaml"],
"actions": ["reapplyK8sManifest", "restartContainer"],
"description": "Update ConfigMap and restart"
}
]
}
Node.js Express
{
"fileSync": true,
"fileSyncIgnore": ["node_modules/", "dist/", "*.map"],
"watch": [
{
"patterns": ["Dockerfile"],
"actions": ["rebuildImage"],
"description": "Rebuild on Dockerfile changes"
},
{
"patterns": ["package.json", "package-lock.json"],
"scripts": {
"installDeps": {
"command": "npm ci && npm run build",
"target": "host"
}
},
"actions": ["installDeps", "restartContainer"],
"description": "Install dependencies and restart"
}
]
}
Go Microservice
{
"fileSync": true,
"watch": [
{
"patterns": ["**/*.go"],
"scripts": {
"buildBinary": {
"command": "go build -o /app/main .",
"target": "container"
}
},
"actions": ["buildBinary", "restartContainer"],
"debounce": 2000,
"description": "Build and restart on Go changes"
},
{
"patterns": ["go.mod", "go.sum"],
"actions": ["rebuildImage"],
"description": "Rebuild on dependency changes"
},
{
"patterns": ["Dockerfile"],
"actions": ["rebuildImage"],
"description": "Rebuild on Dockerfile changes"
}
]
}
Monorepo
{
"services": [
{
"name": "frontend",
"path": "./frontend",
"fileSync": true,
"fileSyncIgnore": ["node_modules/", ".next/"],
"watch": [
{
"patterns": ["Dockerfile", "package.json"],
"actions": ["rebuildImage"]
}
]
},
{
"name": "backend",
"path": "./backend",
"fileSync": true,
"fileSyncIgnore": ["*.pyc", "__pycache__/"],
"watch": [
{
"patterns": ["Dockerfile", "requirements.txt"],
"actions": ["rebuildImage"]
}
]
},
{
"name": "shared-config",
"path": "./config",
"fileSync": false,
"watch": [
{
"patterns": ["**/*.yaml"],
"actions": ["reapplyK8sManifest"],
"description": "Update shared Kubernetes resources"
}
]
}
]
}
Debugging
Check Watch Status
gws status
Shows active watch sessions and rules.
View Watch Logs
# View logs for specific service
gws logs <service>
# View logs for watch orchestrator
gws logs --watch
Test Watch Rules
- Start services:
gws up - Modify a watched file
- Check logs:
gws logs <service> --follow - Verify action executed
Common Issues
Watch not triggering:
- Check pattern syntax (use forward slashes)
- Verify file is not in
fileSyncIgnore - Check debounce time (default 500ms)
Action fails:
- Check logs:
gws logs <service> - Verify script commands are correct
- Check timeout (default 5m)
Duplicate executions:
- Increase
debounce(e.g., 2000ms) - Check for overlapping patterns
Best Practices
1. Separate Source from Infrastructure
DO:
{
"fileSync": true, // Source code
"watch": [
{"patterns": ["Dockerfile"], "actions": ["rebuildImage"]} // Infrastructure
]
}
DON'T:
{
"fileSync": false,
"watch": [
{"patterns": ["**/*.py"], "actions": ["rebuildImage"]} // Too broad!
]
}
2. Use Descriptive Names
{
"description": "Rebuild image when dependencies change",
"scripts": {
"runMigrations": { /* ... */ }, // Good
"script1": { /* ... */ } // Bad
}
}
3. Set Appropriate Debounce
{
"patterns": ["**/*.go"],
"debounce": 2000, // 2 seconds for rapid typing
"actions": ["rebuildImage"]
}
4. Use continueOnError for Non-Critical Steps
{
"scripts": {
"runLinter": {
"command": "eslint .",
"target": "host"
}
},
"actions": ["runLinter", "rebuildImage"],
"continueOnError": true, // Lint warnings don't block deployment
"description": "Lint and build (warnings allowed)"
}
5. Leverage onStartup for Initial Setup
{
"patterns": ["package.json"],
"scripts": {
"installDeps": {
"command": "npm ci",
"target": "host"
}
},
"actions": ["installDeps"],
"onStartup": true, // Ensure deps installed on startup
"description": "Install dependencies"
}