Using Act's Watch Mode to Auto-Re-Run Workflows: A Complete Guide

Act's watch mode (-w/--watch) continuously monitors your repository for file changes and automatically re-executes workflows using the go-fswatch library, respecting .gitignore patterns and polling every 2 seconds.

The nektos/act CLI tool provides a powerful watch mode that eliminates manual re-invocation during GitHub Actions workflow development. By enabling the --watch flag, developers can iterate rapidly on workflows while Act handles automatic detection and re-execution whenever files change in the local repository.

Enabling Watch Mode with the --watch Flag

In cmd/root.go, the watch functionality is exposed through a Boolean flag registered at line 68:

rootCmd.Flags().BoolP("watch", "w", false, "watch the contents of the local repo and run when files change")

When executing a workflow, the application checks this flag at lines 93-95:

if watch, err := cmd.Flags().GetBool("watch"); err != nil { … } else if watch {
    err = watchAndRun(ctx, r.NewPlanExecutor(plan))
    …
}

If enabled, control passes to the watchAndRun helper instead of single execution.

The watchAndRun Implementation

The watchAndRun function, located in cmd/root.go, orchestrates the continuous monitoring and execution cycle.

Initial Execution

Before monitoring begins, the function executes the workflow immediately to provide baseline feedback (lines 84-86):

if err := fn(ctx); err != nil { return err }

File System Watcher Setup

The function initializes a FolderWatcher from the go-fswatch library (lines 74-78):

folderWatcher := fswatch.NewFolderWatcher(dir, true, ignore.MatchesPath, 2)

This configuration watches the current working directory (dir), respects .gitignore patterns via the ignore.MatchesPath callback, and polls every 2 seconds for changes.

Gitignore Integration

Act automatically detects and compiles .gitignore rules to prevent unnecessary re-runs (lines 65-72):

ignoreFile := filepath.Join(dir, ".gitignore")
if info, err := os.Stat(ignoreFile); err == nil && !info.IsDir() {
    ignore, err = gitignore.CompileIgnoreFile(ignoreFile)
}

The Watch Loop

The core monitoring loop (lines 92-102) blocks on change notifications and triggers re-execution:

for folderWatcher.IsRunning() {
    select {
    case <-earlyCancelCtx.Done(): return nil
    case changes := <-folderWatcher.ChangeDetails():
        log.Debugf("%s", changes.String())
        if err := fn(ctx); err != nil { return err }
    }
}

Graceful shutdown is handled via defer folderWatcher.Stop() (lines 81-83), ensuring the watcher terminates cleanly when the user aborts with Ctrl-C.

Performance and Resource Management

Watch mode optimizes performance by reusing resources across executions:

  • Plan reuse: The workflow plan is built once before the loop begins, and r.NewPlanExecutor(plan) returns an executor that is invoked repeatedly
  • Persistent services: Cache servers, artifact servers, and background services remain active throughout the watch session
  • Container optimization: When combined with --reuse, Docker containers persist between executions, reducing startup overhead

Practical Usage Examples

Activate watch mode using these command patterns:


# Monitor and auto-re-run the default push workflow

act --watch

# Watch a specific event type

act push --watch

# Combine with performance flags for rapid iteration

act --watch --quiet --reuse

The --quiet flag suppresses verbose output, while --reuse maintains containers between runs for faster feedback loops.

Summary

  • Flag location: The -w/--watch flag is defined in cmd/root.go at line 68 and consumed at lines 93-95
  • Core implementation: The watchAndRun function handles continuous execution using the go-fswatch library
  • Polling behavior: File system checks occur every 2 seconds with automatic .gitignore respect
  • Initial run: Workflows execute immediately upon starting watch mode before monitoring begins
  • Resource efficiency: Plan executors and background services persist across re-runs during the watch session

Frequently Asked Questions

Does watch mode rebuild the workflow plan on every change?

No. According to the source code in cmd/root.go, the plan is constructed once before entering the watch loop. The watchAndRun function receives a pre-built plan executor (r.NewPlanExecutor(plan)) and invokes it repeatedly, minimizing overhead to container re-execution only.

How does Act avoid infinite loops from generated files or logs?

Act respects your repository's .gitignore patterns. As implemented in cmd/root.go lines 65-72, the watcher compiles the .gitignore file and uses ignore.MatchesPath as a filter when creating the FolderWatcher, ensuring that build artifacts and ignored paths do not trigger re-runs.

Can I use watch mode with specific workflow events or job filters?

Yes. The --watch flag operates after standard workflow selection. You can combine it with event specifiers like act pull_request --watch or job filters, and these selections persist for every automatic re-execution during the watch session.

What happens to running containers when I press Ctrl-C during watch mode?

The implementation uses an earlyCancelCtx context and defer folderWatcher.Stop() to ensure graceful shutdown. When you interrupt the process, the file system watcher stops immediately and the application exits cleanly without orphaning the background services that were started at the beginning of the session.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →