Repository: nektos/act (language: go)

Issue:
potential data races
### Bug report info

```plain text
none
```


### Command used with act

```sh
none
```


### Describe issue

Hello developer, I used a static data race detector to check this project and found suspicious data race defects. I have done preliminary filtering and categorized the defects, choosing only one from each category. However, due to my unfamiliarity with the overall architecture of the project, there may still be false positives after filtering. Below are the potential data race issues I found(not all stack traces are listed):
Potential race condition:
```
 Access1:
        if err = fn(ctx); err != nil {
           ^
act/cmd/root.go:663:3 ->
act/cmd/root.go:651:7

 Access2:
        return err
               ^
act/cmd/root.go:666:9
===================================================
Potential race condition:
 Access1:
        if err = fn(ctx); err != nil {
           ^
act/cmd/root.go:663:3 ->
act/cmd/root.go:657:8

 Access2:
        return err
               ^
act/cmd/root.go:666:9
```


### Link to GitHub repository

_No response_

### Workflow content

```yml
none
```


### Relevant log output

```sh
none
```


### Additional information

_No response_


Discussion / hints:

Fix patch:
diff --git a/cmd/root.go b/cmd/root.go
index fe4fe7c6f05..1742e8a1ed9 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -18,13 +18,13 @@ import (
 	gitignore "github.com/sabhiram/go-gitignore"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
+	"gopkg.in/yaml.v3"
 
 	"github.com/nektos/act/pkg/artifacts"
 	"github.com/nektos/act/pkg/common"
 	"github.com/nektos/act/pkg/container"
 	"github.com/nektos/act/pkg/model"
 	"github.com/nektos/act/pkg/runner"
-	"gopkg.in/yaml.v3"
 )
 
 // Execute is the entry point to running the CLI
@@ -623,45 +623,47 @@ func defaultImageSurvey(actrc string) error {
 }
 
 func watchAndRun(ctx context.Context, fn common.Executor) error {
-	recurse := true
-	checkIntervalInSeconds := 2
 	dir, err := os.Getwd()
 	if err != nil {
 		return err
 	}
 
-	var ignore *gitignore.GitIgnore
-	if _, err := os.Stat(filepath.Join(dir, ".gitignore")); !os.IsNotExist(err) {
-		ignore, _ = gitignore.CompileIgnoreFile(filepath.Join(dir, ".gitignore"))
-	} else {
-		ignore = &gitignore.GitIgnore{}
+	ignoreFile := filepath.Join(dir, ".gitignore")
+	ignore := &gitignore.GitIgnore{}
+	if info, err := os.Stat(ignoreFile); err == nil && !info.IsDir() {
+		ignore, err = gitignore.CompileIgnoreFile(ignoreFile)
+		if err != nil {
+			return fmt.Errorf("compile %q: %w", ignoreFile, err)
+		}
 	}
 
 	folderWatcher := fswatch.NewFolderWatcher(
 		dir,
-		recurse,
+		true,
 		ignore.MatchesPath,
-		checkIntervalInSeconds,
+		2, // 2 seconds
 	)
 
 	folderWatcher.Start()
+	defer folderWatcher.Stop()
 
-	go func() {
-		for folderWatcher.IsRunning() {
-			if err = fn(ctx); err != nil {
-				break
-			}
-			log.Debugf("Watching %s for changes", dir)
-			for changes := range folderWatcher.ChangeDetails() {
-				log.Debugf("%s", changes.String())
-				if err = fn(ctx); err != nil {
-					break
-				}
-				log.Debugf("Watching %s for changes", dir)
+	// run once before watching
+	if err := fn(ctx); err != nil {
+		return err
+	}
+
+	for folderWatcher.IsRunning() {
+		log.Debugf("Watching %s for changes", dir)
+		select {
+		case <-ctx.Done():
+			return nil
+		case changes := <-folderWatcher.ChangeDetails():
+			log.Debugf("%s", changes.String())
+			if err := fn(ctx); err != nil {
+				return err
 			}
 		}
-	}()
-	<-ctx.Done()
-	folderWatcher.Stop()
-	return err
+	}
+
+	return nil
 }


Test patch: