Skip to main content

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 image
  • reapplyK8sManifest - Apply Kubernetes manifests
  • restartContainer - 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 with path)
  • path - Path to script file (mutually exclusive with command)
  • 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:

  1. docker build -t <image>:<tag> .
  2. docker push <image>:<tag>
  3. 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>.yaml
  • kubectl 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:

  1. kubectl rollout restart deployment/<service>
  2. 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:

  1. Run tests on host
  2. If tests pass, rebuild image
  3. 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:

  • Dockerfile
  • requirements.txt, package.json, go.mod
  • k8s/**/*.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

  1. Start services: gws up
  2. Modify a watched file
  3. Check logs: gws logs <service> --follow
  4. 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"
}

Next Steps