From a6dd4f19d47e327510940f4d987426d1d42652b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BF=99=E4=B8=AA=E5=90=8D=E5=AD=97=E4=B8=8D=E7=9F=A5?= =?UTF-8?q?=E9=81=93=E8=83=BD=E4=B8=8D=E8=83=BD=E4=BF=AE=E6=94=B9?= Date: Mon, 3 Mar 2025 10:40:58 +0800 Subject: [PATCH 1/4] Update config.go --- runner/config.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/runner/config.go b/runner/config.go index 681be82d..021f034c 100644 --- a/runner/config.go +++ b/runner/config.go @@ -394,4 +394,12 @@ func (c *Config) WithArgs(args map[string]TomlInfo) { setValue2Struct(v, value.fieldPath, *value.Value) } } + + if len(c.Build.FullBin) > 0 { + c.Build.Bin = c.Build.FullBin + return + } + // Fix windows CMD processor + // CMD will not recognize relative path like ./tmp/server + c.Build.Bin, _ = filepath.Abs(c.Build.Bin) } From 5f76f6284b0f8ba6febc69ce09c7bffe7b40223d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BF=99=E4=B8=AA=E5=90=8D=E5=AD=97=E4=B8=8D=E7=9F=A5?= =?UTF-8?q?=E9=81=93=E8=83=BD=E4=B8=8D=E8=83=BD=E4=BF=AE=E6=94=B9?= Date: Mon, 3 Mar 2025 10:44:51 +0800 Subject: [PATCH 2/4] Update flag_test.go --- runner/flag_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/runner/flag_test.go b/runner/flag_test.go index abfa45e5..7ec6572b 100644 --- a/runner/flag_test.go +++ b/runner/flag_test.go @@ -118,6 +118,13 @@ func TestConfigRuntimeArgs(t *testing.T) { assert.NotEqual(t, []string{}, conf.Build.ExcludeDir) }, }, + { + name: "check full_bin", + args: []string{"--build.full_bin", "APP_ENV=dev APP_USER=air ./tmp/main"}, + check: func(t *testing.T, conf *Config) { + assert.Equal(t, "APP_ENV=dev APP_USER=air ./tmp/main", conf.Build.Bin) + }, + }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { From d0560b5e4e61888d8453e1910cb7caa618fd255b Mon Sep 17 00:00:00 2001 From: xiantang Date: Wed, 5 Mar 2025 23:29:18 +0800 Subject: [PATCH 3/4] fix: handle preprocess correctly in command line arguments --- main.go | 3 +-- runner/config.go | 19 +++++++--------- runner/config_test.go | 8 +++---- runner/engine.go | 4 ++-- runner/engine_test.go | 50 +++++++++++++++++++++---------------------- runner/flag_test.go | 17 +++++++++++++-- 6 files changed, 55 insertions(+), 46 deletions(-) diff --git a/main.go b/main.go index dd64104b..4e558bdb 100644 --- a/main.go +++ b/main.go @@ -89,12 +89,11 @@ func main() { signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP) var err error - cfg, err := runner.InitConfig(cfgPath) + cfg, err := runner.InitConfig(cfgPath, cmdArgs) if err != nil { log.Fatal(err) return } - cfg.WithArgs(cmdArgs) if !cfg.Log.Silent { printSplash() } diff --git a/runner/config.go b/runner/config.go index 021f034c..51d600b8 100644 --- a/runner/config.go +++ b/runner/config.go @@ -109,7 +109,7 @@ func (t sliceTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Va } // InitConfig initializes the configuration. -func InitConfig(path string) (cfg *Config, err error) { +func InitConfig(path string, cmdArgs map[string]TomlInfo) (cfg *Config, err error) { if path == "" { cfg, err = defaultPathConfig() if err != nil { @@ -135,7 +135,7 @@ func InitConfig(path string) (cfg *Config, err error) { return nil, err } - err = ret.preprocess() + err = ret.preprocess(cmdArgs) return ret, err } @@ -278,8 +278,12 @@ func readConfigOrDefault(path string) (*Config, error) { return cfg, nil } -func (c *Config) preprocess() error { +func (c *Config) preprocess(args map[string]TomlInfo) error { var err error + + if args != nil { + c.withArgs(args) + } cwd := os.Getenv(airWd) if cwd != "" { if err = os.Chdir(cwd); err != nil { @@ -385,7 +389,7 @@ func (c *Config) rel(path string) string { } // WithArgs returns a new config with the given arguments added to the configuration. -func (c *Config) WithArgs(args map[string]TomlInfo) { +func (c *Config) withArgs(args map[string]TomlInfo) { for _, value := range args { // Ignore values that match the default configuration. // This ensures user-specified configurations are not overwritten by default values. @@ -395,11 +399,4 @@ func (c *Config) WithArgs(args map[string]TomlInfo) { } } - if len(c.Build.FullBin) > 0 { - c.Build.Bin = c.Build.FullBin - return - } - // Fix windows CMD processor - // CMD will not recognize relative path like ./tmp/server - c.Build.Bin, _ = filepath.Abs(c.Build.Bin) } diff --git a/runner/config_test.go b/runner/config_test.go index 34618de2..fa583eb9 100644 --- a/runner/config_test.go +++ b/runner/config_test.go @@ -43,7 +43,7 @@ func TestBinCmdPath(t *testing.T) { var err error c := getWindowsConfig() - err = c.preprocess() + err = c.preprocess(nil) if err != nil { t.Fatal(err) } @@ -118,7 +118,7 @@ func TestReadConfByName(t *testing.T) { func TestConfPreprocess(t *testing.T) { t.Setenv(airWd, "_testdata/toml") df := defaultConfig() - err := df.preprocess() + err := df.preprocess(nil) if err != nil { t.Fatalf("preprocess error %v", err) } @@ -143,7 +143,7 @@ func TestConfigWithRuntimeArgs(t *testing.T) { t.Run("when using bin", func(t *testing.T) { df := defaultConfig() - if err := df.preprocess(); err != nil { + if err := df.preprocess(nil); err != nil { t.Fatalf("preprocess error %v", err) } @@ -155,7 +155,7 @@ func TestConfigWithRuntimeArgs(t *testing.T) { t.Run("when using full_bin", func(t *testing.T) { df := defaultConfig() df.Build.FullBin = "./tmp/main" - if err := df.preprocess(); err != nil { + if err := df.preprocess(nil); err != nil { t.Fatalf("preprocess error %v", err) } diff --git a/runner/engine.go b/runner/engine.go index ce2fa843..6d8f5e67 100644 --- a/runner/engine.go +++ b/runner/engine.go @@ -71,9 +71,9 @@ func NewEngineWithConfig(cfg *Config, debugMode bool) (*Engine, error) { } // NewEngine ... -func NewEngine(cfgPath string, debugMode bool) (*Engine, error) { +func NewEngine(cfgPath string, args map[string]TomlInfo, debugMode bool) (*Engine, error) { var err error - cfg, err := InitConfig(cfgPath) + cfg, err := InitConfig(cfgPath, args) if err != nil { return nil, err } diff --git a/runner/engine_test.go b/runner/engine_test.go index 2155743b..c8998cc7 100644 --- a/runner/engine_test.go +++ b/runner/engine_test.go @@ -21,7 +21,7 @@ import ( func TestNewEngine(t *testing.T) { _ = os.Unsetenv(airWd) - engine, err := NewEngine("", true) + engine, err := NewEngine("", nil, true) if err != nil { t.Fatalf("Should not be fail: %s.", err) } @@ -38,7 +38,7 @@ func TestNewEngine(t *testing.T) { func TestCheckRunEnv(t *testing.T) { _ = os.Unsetenv(airWd) - engine, err := NewEngine("", true) + engine, err := NewEngine("", nil, true) if err != nil { t.Fatalf("Should not be fail: %s.", err) } @@ -49,7 +49,7 @@ func TestCheckRunEnv(t *testing.T) { } func TestWatching(t *testing.T) { - engine, err := NewEngine("", true) + engine, err := NewEngine("", nil, true) if err != nil { t.Fatalf("Should not be fail: %s.", err) } @@ -65,12 +65,12 @@ func TestWatching(t *testing.T) { } func TestRegexes(t *testing.T) { - engine, err := NewEngine("", true) + engine, err := NewEngine("", nil, true) if err != nil { t.Fatalf("Should not be fail: %s.", err) } engine.config.Build.ExcludeRegex = []string{"foo\\.html$", "bar", "_test\\.go"} - err = engine.config.preprocess() + err = engine.config.preprocess(nil) if err != nil { t.Fatalf("Should not be fail: %s.", err) } @@ -123,7 +123,7 @@ func TestRunCommand(t *testing.T) { tmpDir := initTestEnv(t, port) // change dir to tmpDir chdir(t, tmpDir) - engine, err := NewEngine("", true) + engine, err := NewEngine("", nil, true) if err != nil { t.Fatalf("Should not be fail: %s.", err) } @@ -146,7 +146,7 @@ func TestRunPreCmd(t *testing.T) { tmpDir := initTestEnv(t, port) // change dir to tmpDir chdir(t, tmpDir) - engine, err := NewEngine("", true) + engine, err := NewEngine("", nil, true) if err != nil { t.Fatalf("Should not be fail: %s.", err) } @@ -171,7 +171,7 @@ func TestRunPostCmd(t *testing.T) { // change dir to tmpDir chdir(t, tmpDir) - engine, err := NewEngine("", true) + engine, err := NewEngine("", nil, true) if err != nil { t.Fatalf("Should not be fail: %s.", err) } @@ -190,7 +190,7 @@ func TestRunPostCmd(t *testing.T) { } func TestRunBin(t *testing.T) { - engine, err := NewEngine("", true) + engine, err := NewEngine("", nil, true) if err != nil { t.Fatalf("Should not be fail: %s.", err) } @@ -221,7 +221,7 @@ func TestRebuild(t *testing.T) { tmpDir := initTestEnv(t, port) // change dir to tmpDir chdir(t, tmpDir) - engine, err := NewEngine("", true) + engine, err := NewEngine("", nil, true) engine.config.Build.ExcludeUnchanged = true if err != nil { t.Fatalf("Should not be fail: %s.", err) @@ -310,14 +310,14 @@ func TestCtrlCWhenHaveKillDelay(t *testing.T) { tmpDir := initTestEnv(t, port) // change dir to tmpDir chdir(t, tmpDir) - engine, err := NewEngine("", true) + engine, err := NewEngine("", nil, true) if err != nil { t.Fatalf("Should not be fail: %s.", err) } engine.config.Build.KillDelay = c.Build.KillDelay engine.config.Build.Delay = 2000 engine.config.Build.SendInterrupt = true - if err := engine.config.preprocess(); err != nil { + if err := engine.config.preprocess(nil); err != nil { t.Fatalf("Should not be fail: %s.", err) } @@ -353,7 +353,7 @@ func TestCtrlCWhenREngineIsRunning(t *testing.T) { tmpDir := initTestEnv(t, port) // change dir to tmpDir chdir(t, tmpDir) - engine, err := NewEngine("", true) + engine, err := NewEngine("", nil, true) if err != nil { t.Fatalf("Should not be fail: %s.", err) } @@ -386,7 +386,7 @@ func TestCtrlCWithFailedBin(t *testing.T) { go func() { dir := initWithQuickExitGoCode(t) chdir(t, dir) - engine, err := NewEngine("", true) + engine, err := NewEngine("", nil, true) assert.NoError(t, err) engine.config.Build.Bin = "" sigs := make(chan os.Signal, 1) @@ -419,7 +419,7 @@ func TestFixCloseOfChannelAfterCtrlC(t *testing.T) { // fix https://github.com/air-verse/air/issues/294 dir := initWithBuildFailedCode(t) chdir(t, dir) - engine, err := NewEngine("", true) + engine, err := NewEngine("", nil, true) if err != nil { t.Fatalf("Should not be fail: %s.", err) } @@ -464,7 +464,7 @@ func TestFixCloseOfChannelAfterTwoFailedBuild(t *testing.T) { dir := initWithBuildFailedCode(t) // change dir to tmpDir chdir(t, dir) - engine, err := NewEngine("", true) + engine, err := NewEngine("", nil, true) if err != nil { t.Fatalf("Should not be fail: %s.", err) } @@ -530,7 +530,7 @@ func TestRun(t *testing.T) { tmpDir := initTestEnv(t, port) // change dir to tmpDir chdir(t, tmpDir) - engine, err := NewEngine("", true) + engine, err := NewEngine("", nil, true) if err != nil { t.Fatalf("Should not be fail: %s.", err) } @@ -716,7 +716,7 @@ func TestRebuildWhenRunCmdUsingDLV(t *testing.T) { tmpDir := initTestEnv(t, port) // change dir to tmpDir chdir(t, tmpDir) - engine, err := NewEngine("", true) + engine, err := NewEngine("", nil, true) if err != nil { t.Fatalf("Should not be fail: %s.", err) } @@ -725,7 +725,7 @@ func TestRebuildWhenRunCmdUsingDLV(t *testing.T) { dlvPort, f := GetPort() f() engine.config.Build.FullBin = fmt.Sprintf("dlv exec --accept-multiclient --log --headless --continue --listen :%d --api-version 2 ./tmp/main", dlvPort) - _ = engine.config.preprocess() + _ = engine.config.preprocess(nil) go func() { engine.Run() }() @@ -818,7 +818,7 @@ include_file = ["test/not_a_test.go"] if err := os.WriteFile(dftTOML, []byte(config), 0o644); err != nil { t.Fatal(err) } - engine, err := NewEngine(".air.toml", true) + engine, err := NewEngine(".air.toml", nil, true) if err != nil { t.Fatal(err) } @@ -881,7 +881,7 @@ func Test(t *testing.T) { } time.Sleep(time.Second * 2) - engine, err := NewEngine(".air.toml", false) + engine, err := NewEngine(".air.toml", nil, false) if err != nil { t.Fatal(err) } @@ -917,7 +917,7 @@ func TestCreateNewDir(t *testing.T) { tmpDir := initTestEnv(t, port) // change dir to tmpDir chdir(t, tmpDir) - engine, err := NewEngine("", true) + engine, err := NewEngine("", nil, true) if err != nil { t.Fatalf("Should not be fail: %s.", err) } @@ -967,7 +967,7 @@ include_file = ["main.sh"] t.Fatal(err) } - engine, err := NewEngine(dftTOML, false) + engine, err := NewEngine(dftTOML, nil, false) if err != nil { t.Fatal(err) } @@ -1026,7 +1026,7 @@ include_file = ["main.sh"] t.Fatal(err) } - engine, err := NewEngine(dftTOML, false) + engine, err := NewEngine(dftTOML, nil, false) if err != nil { t.Fatal(err) } @@ -1110,7 +1110,7 @@ func TestEngineExit(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - e, err := NewEngine("", true) + e, err := NewEngine("", nil, true) if err != nil { t.Fatal(err) } diff --git a/runner/flag_test.go b/runner/flag_test.go index 7ec6572b..8bd535bf 100644 --- a/runner/flag_test.go +++ b/runner/flag_test.go @@ -125,6 +125,20 @@ func TestConfigRuntimeArgs(t *testing.T) { assert.Equal(t, "APP_ENV=dev APP_USER=air ./tmp/main", conf.Build.Bin) }, }, + + { + name: "check exclude_regex patterns compiled", + args: []string{"--build.exclude_regex", "test_pattern\\.go"}, + check: func(t *testing.T, conf *Config) { + assert.Equal(t, []string{"test_pattern\\.go"}, conf.Build.ExcludeRegex) + patterns, err := conf.Build.RegexCompiled() + require.NoError(t, err) + require.NotNil(t, patterns) + require.Len(t, patterns, 1) + assert.True(t, patterns[0].MatchString("test_pattern.go"), "regex should match test_pattern.go") + assert.False(t, patterns[0].MatchString("other_file.go"), "regex shouldn't match other_file.go") + }, + }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { @@ -133,12 +147,11 @@ func TestConfigRuntimeArgs(t *testing.T) { flag := flag.NewFlagSet(t.Name(), flag.ExitOnError) cmdArgs := ParseConfigFlag(flag) _ = flag.Parse(tc.args) - cfg, err := InitConfig("") + cfg, err := InitConfig("", cmdArgs) if err != nil { log.Fatal(err) return } - cfg.WithArgs(cmdArgs) tc.check(t, cfg) }) } From c4b78696d7e7d2e9ec31af4dbbf8effedf36dcc9 Mon Sep 17 00:00:00 2001 From: xiantang Date: Wed, 5 Mar 2025 23:31:46 +0800 Subject: [PATCH 4/4] fix: update withArgs documentation to reflect its private status --- runner/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runner/config.go b/runner/config.go index 51d600b8..ebfceeb0 100644 --- a/runner/config.go +++ b/runner/config.go @@ -388,7 +388,7 @@ func (c *Config) rel(path string) string { return s } -// WithArgs returns a new config with the given arguments added to the configuration. +// withArgs returns a new config with the given arguments added to the configuration. func (c *Config) withArgs(args map[string]TomlInfo) { for _, value := range args { // Ignore values that match the default configuration.