feat: add build-specific platform overrides (#889)

* feat: add build-specific platform overrides

* test(engine): align default config expectation with init overrides

* test(config): add integration coverage for windows build overrides

* test(config): make args_bin override assertion resilient

* test(config): expand coverage for platform override helpers

* docs(readme): fix broken markdown fences in config sections

* refactor(config): reuse shared build command fields

* test(engine): normalize windows override slices in config assertion
This commit is contained in:
xiantang
2026-04-05 16:44:48 +08:00
committed by GitHub
parent 47d11f6a71
commit c1e77778ac
8 changed files with 555 additions and 61 deletions
+18
View File
@@ -251,6 +251,24 @@ Air can automatically load environment variables from `.env` files before both b
env_files = [".env.development", ".env"]
```
### Platform-specific build overrides
You can override build settings per OS with `[build.windows]`, `[build.darwin]`, and `[build.linux]`. These blocks override the base `[build]` values when running on the matching platform. Only the fields below are supported in platform blocks: `pre_cmd`, `cmd`, `post_cmd`, `bin`, `entrypoint`, `full_bin`, `args_bin`.
```toml
[build]
cmd = "go build -o ./tmp/main ."
bin = "./tmp/main"
[build.windows]
cmd = "go build -o ./tmp/main.exe ."
bin = "tmp\\main.exe"
entrypoint = ["tmp\\main.exe"]
```
Running `air init` adds a platform block for the current OS when its defaults differ from the base configuration.
### Docker Compose
```yaml
+6
View File
@@ -63,6 +63,12 @@ rerun = false
# Delay after each execution
rerun_delay = 500
# Platform-specific build overrides
[build.windows]
cmd = "go build -o ./tmp/main.exe ."
bin = "tmp\\main.exe"
entrypoint = ["tmp\\main.exe"]
[log]
# Show log time
time = false
+199 -41
View File
@@ -81,32 +81,43 @@ func (e entrypoint) args() []string {
return e[1:]
}
type cfgBuildOverrides struct {
CfgBuildCommon `toml:",inline"`
}
type CfgBuildCommon struct {
PreCmd []string `toml:"pre_cmd" usage:"Array of commands to run before each build"`
Cmd string `toml:"cmd" usage:"Just plain old shell command. You could use 'make' as well"`
PostCmd []string `toml:"post_cmd" usage:"Array of commands to run after ^C"`
Bin string `toml:"bin" usage:"Binary file yields from 'cmd', will be deprecated soon, recommend using entrypoint."`
Entrypoint entrypoint `toml:"entrypoint" usage:"Binary file plus optional arguments relative to root, prefer [\"./tmp/main\", \"arg\"] form"`
FullBin string `toml:"full_bin" usage:"Customize binary, can setup environment variables when run your app"`
ArgsBin []string `toml:"args_bin" usage:"Add additional arguments when running binary (bin/full_bin)."`
}
type cfgBuild struct {
PreCmd []string `toml:"pre_cmd" usage:"Array of commands to run before each build"`
Cmd string `toml:"cmd" usage:"Just plain old shell command. You could use 'make' as well"`
PostCmd []string `toml:"post_cmd" usage:"Array of commands to run after ^C"`
Bin string `toml:"bin" usage:"Binary file yields from 'cmd', will be deprecated soon, recommend using entrypoint."`
Entrypoint entrypoint `toml:"entrypoint" usage:"Binary file plus optional arguments relative to root, prefer [\"./tmp/main\", \"arg\"] form"`
FullBin string `toml:"full_bin" usage:"Customize binary, can setup environment variables when run your app"`
ArgsBin []string `toml:"args_bin" usage:"Add additional arguments when running binary (bin/full_bin)."`
Log string `toml:"log" usage:"This log file is placed in your tmp_dir"`
IncludeExt []string `toml:"include_ext" usage:"Watch these filename extensions"`
ExcludeDir []string `toml:"exclude_dir" usage:"Ignore these filename extensions or directories"`
IncludeDir []string `toml:"include_dir" usage:"Watch these directories if you specified"`
ExcludeFile []string `toml:"exclude_file" usage:"Exclude files"`
IncludeFile []string `toml:"include_file" usage:"Watch these files"`
ExcludeRegex []string `toml:"exclude_regex" usage:"Exclude specific regular expressions"`
ExcludeUnchanged bool `toml:"exclude_unchanged" usage:"Exclude unchanged files"`
IgnoreDangerousRootDir bool `toml:"ignore_dangerous_root_dir" usage:"Ignore dangerous root directory that could cause excessive file watching"`
FollowSymlink bool `toml:"follow_symlink" usage:"Follow symlink for directories"`
Poll bool `toml:"poll" usage:"Poll files for changes instead of using fsnotify"`
PollInterval int `toml:"poll_interval" usage:"Poll interval (defaults to the minimum interval of 500ms)"`
Delay int `toml:"delay" usage:"It's not necessary to trigger build each time file changes if it's too frequent"`
StopOnError bool `toml:"stop_on_error" usage:"Stop running old binary when build errors occur"`
SendInterrupt bool `toml:"send_interrupt" usage:"Send Interrupt signal before killing process (ignored on Windows; uses TASKKILL)"`
KillDelay time.Duration `toml:"kill_delay" usage:"Delay after sending Interrupt signal"`
Rerun bool `toml:"rerun" usage:"Rerun binary or not"`
RerunDelay int `toml:"rerun_delay" usage:"Delay after each execution"`
CfgBuildCommon `toml:",inline"`
Log string `toml:"log" usage:"This log file is placed in your tmp_dir"`
IncludeExt []string `toml:"include_ext" usage:"Watch these filename extensions"`
ExcludeDir []string `toml:"exclude_dir" usage:"Ignore these filename extensions or directories"`
IncludeDir []string `toml:"include_dir" usage:"Watch these directories if you specified"`
ExcludeFile []string `toml:"exclude_file" usage:"Exclude files"`
IncludeFile []string `toml:"include_file" usage:"Watch these files"`
ExcludeRegex []string `toml:"exclude_regex" usage:"Exclude specific regular expressions"`
ExcludeUnchanged bool `toml:"exclude_unchanged" usage:"Exclude unchanged files"`
IgnoreDangerousRootDir bool `toml:"ignore_dangerous_root_dir" usage:"Ignore dangerous root directory that could cause excessive file watching"`
FollowSymlink bool `toml:"follow_symlink" usage:"Follow symlink for directories"`
Poll bool `toml:"poll" usage:"Poll files for changes instead of using fsnotify"`
PollInterval int `toml:"poll_interval" usage:"Poll interval (defaults to the minimum interval of 500ms)"`
Delay int `toml:"delay" usage:"It's not necessary to trigger build each time file changes if it's too frequent"`
StopOnError bool `toml:"stop_on_error" usage:"Stop running old binary when build errors occur"`
SendInterrupt bool `toml:"send_interrupt" usage:"Send Interrupt signal before killing process (windows does not support this feature)"`
KillDelay time.Duration `toml:"kill_delay" usage:"Delay after sending Interrupt signal"`
Rerun bool `toml:"rerun" usage:"Rerun binary or not"`
RerunDelay int `toml:"rerun_delay" usage:"Delay after each execution"`
Windows *cfgBuildOverrides `toml:"windows,omitempty"`
Darwin *cfgBuildOverrides `toml:"darwin,omitempty"`
Linux *cfgBuildOverrides `toml:"linux,omitempty"`
regexCompiled []*regexp.Regexp
includeDirAbs []string
extraIncludeDirs []string
@@ -198,7 +209,6 @@ func InitConfig(path string, cmdArgs map[string]TomlInfo) (cfg *Config, err erro
return nil, err
}
}
warnDeprecatedBin(cfg)
config := defaultConfig()
// get addr
ret := &config
@@ -213,6 +223,12 @@ func InitConfig(path string, cmdArgs map[string]TomlInfo) (cfg *Config, err erro
return nil, err
}
if err = applyPlatformOverrides(ret); err != nil {
return nil, err
}
warnDeprecatedBin(ret)
err = ret.preprocess(cmdArgs)
return ret, err
}
@@ -232,10 +248,9 @@ func writeDefaultConfig() (string, error) {
}
defer file.Close()
config := defaultConfig()
if len(config.Build.Entrypoint) == 0 && config.Build.Bin != "" {
config.Build.Entrypoint = entrypoint{config.Build.Bin}
}
config := defaultConfigBase()
setEntrypointFromBin(&config)
addPlatformOverridesForInit(&config, runtime.GOOS)
configFile, err := toml.Marshal(config)
if err != nil {
return "", fmt.Errorf("failed to marshal the default configuration: %w", err)
@@ -284,29 +299,27 @@ func readConfByName(name string) (*Config, error) {
return cfg, err
}
func defaultConfig() Config {
func defaultConfigBase() Config {
build := cfgBuild{
Cmd: "go build -o ./tmp/main .",
Bin: "./tmp/main",
Entrypoint: entrypoint{},
CfgBuildCommon: CfgBuildCommon{
Cmd: "go build -o ./tmp/main .",
Bin: "./tmp/main",
Entrypoint: entrypoint{},
PreCmd: []string{},
PostCmd: []string{},
ArgsBin: []string{},
},
Log: "build-errors.log",
IncludeExt: []string{"go", "tpl", "tmpl", "html"},
IncludeDir: []string{},
PreCmd: []string{},
PostCmd: []string{},
ExcludeFile: []string{},
IncludeFile: []string{},
ExcludeDir: []string{"assets", "tmp", "vendor", "testdata"},
ArgsBin: []string{},
ExcludeRegex: []string{"_test.go"},
Delay: 1000,
Rerun: false,
RerunDelay: 500,
}
if runtime.GOOS == PlatformWindows {
build.Bin = `tmp\main.exe`
build.Cmd = "go build -o ./tmp/main.exe ."
}
log := cfgLog{
AddTime: false,
MainOnly: false,
@@ -337,6 +350,151 @@ func defaultConfig() Config {
}
}
func defaultConfigForOS(goos string) Config {
cfg := defaultConfigBase()
applyPlatformDefaults(&cfg, goos)
return cfg
}
func defaultConfig() Config {
return defaultConfigForOS(runtime.GOOS)
}
func applyPlatformDefaults(cfg *Config, goos string) {
if cfg == nil {
return
}
if goos == PlatformWindows {
cfg.Build.Bin = `tmp\main.exe`
cfg.Build.Cmd = "go build -o ./tmp/main.exe ."
}
}
func applyPlatformOverrides(cfg *Config) error {
if cfg == nil {
return nil
}
override := platformBuildOverrides(&cfg.Build, runtime.GOOS)
if override == nil {
return nil
}
applyBuildOverrides(&cfg.Build, override)
return nil
}
func platformBuildOverrides(build *cfgBuild, goos string) *cfgBuildOverrides {
if build == nil {
return nil
}
switch goos {
case PlatformWindows:
return build.Windows
case "darwin":
return build.Darwin
case "linux":
return build.Linux
default:
return nil
}
}
func applyBuildOverrides(build *cfgBuild, override *cfgBuildOverrides) {
if build == nil || override == nil {
return
}
if override.PreCmd != nil {
build.PreCmd = override.PreCmd
}
if override.Cmd != "" {
build.Cmd = override.Cmd
}
if override.PostCmd != nil {
build.PostCmd = override.PostCmd
}
if override.Bin != "" {
build.Bin = override.Bin
}
if override.Entrypoint != nil {
build.Entrypoint = override.Entrypoint
}
if override.FullBin != "" {
build.FullBin = override.FullBin
}
if override.ArgsBin != nil {
build.ArgsBin = override.ArgsBin
}
}
func setEntrypointFromBin(cfg *Config) {
if cfg == nil {
return
}
if len(cfg.Build.Entrypoint) == 0 && cfg.Build.Bin != "" {
cfg.Build.Entrypoint = entrypoint{cfg.Build.Bin}
}
}
func addPlatformOverridesForInit(cfg *Config, goos string) {
if cfg == nil {
return
}
platformConfig := defaultConfigForOS(goos)
setEntrypointFromBin(&platformConfig)
override := buildOverridesFromDiff(cfg.Build, platformConfig.Build)
if override == nil {
return
}
switch goos {
case PlatformWindows:
cfg.Build.Windows = override
case "darwin":
cfg.Build.Darwin = override
case "linux":
cfg.Build.Linux = override
}
}
func buildOverridesFromDiff(base cfgBuild, target cfgBuild) *cfgBuildOverrides {
var override cfgBuildOverrides
changed := false
if !reflect.DeepEqual(base.PreCmd, target.PreCmd) {
override.PreCmd = target.PreCmd
changed = true
}
if base.Cmd != target.Cmd {
override.Cmd = target.Cmd
changed = true
}
if !reflect.DeepEqual(base.PostCmd, target.PostCmd) {
override.PostCmd = target.PostCmd
changed = true
}
if base.Bin != target.Bin {
override.Bin = target.Bin
changed = true
}
if !reflect.DeepEqual(base.Entrypoint, target.Entrypoint) {
override.Entrypoint = target.Entrypoint
changed = true
}
if base.FullBin != target.FullBin {
override.FullBin = target.FullBin
changed = true
}
if !reflect.DeepEqual(base.ArgsBin, target.ArgsBin) {
override.ArgsBin = target.ArgsBin
changed = true
}
if !changed {
return nil
}
return &override
}
func readConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
+297 -11
View File
@@ -19,9 +19,11 @@ const (
func getWindowsConfig() Config {
build := cfgBuild{
PreCmd: []string{"echo Hello Air"},
Cmd: "go build -o ./tmp/main .",
Bin: "./tmp/main",
CfgBuildCommon: CfgBuildCommon{
PreCmd: []string{"echo Hello Air"},
Cmd: "go build -o ./tmp/main .",
Bin: "./tmp/main",
},
Log: "build-errors.log",
IncludeExt: []string{"go", "tpl", "tmpl", "html"},
ExcludeDir: []string{"assets", "tmp", "vendor", "testdata"},
@@ -314,6 +316,164 @@ func TestKillDelay(t *testing.T) {
}
}
func TestApplyBuildOverrides(t *testing.T) {
base := defaultConfigBase()
override := &cfgBuildOverrides{
CfgBuildCommon: CfgBuildCommon{
PreCmd: []string{"echo override"},
Cmd: "go build -o ./tmp/custom .",
ArgsBin: []string{"custom"},
},
}
applyBuildOverrides(&base.Build, override)
if base.Build.Cmd != override.Cmd {
t.Fatalf("cmd mismatch: got %s want %s", base.Build.Cmd, override.Cmd)
}
if !reflect.DeepEqual(base.Build.PreCmd, override.PreCmd) {
t.Fatalf("pre_cmd mismatch: got %v want %v", base.Build.PreCmd, override.PreCmd)
}
if !reflect.DeepEqual(base.Build.ArgsBin, override.ArgsBin) {
t.Fatalf("args_bin mismatch: got %v want %v", base.Build.ArgsBin, override.ArgsBin)
}
if base.Build.Bin != "./tmp/main" {
t.Fatalf("bin should remain default, got %s", base.Build.Bin)
}
}
func TestAddPlatformOverridesForInit(t *testing.T) {
cfg := defaultConfigBase()
setEntrypointFromBin(&cfg)
addPlatformOverridesForInit(&cfg, PlatformWindows)
if cfg.Build.Windows == nil {
t.Fatal("expected windows overrides to be set")
}
if cfg.Build.Windows.Cmd != "go build -o ./tmp/main.exe ." {
t.Fatalf("windows cmd mismatch: got %s", cfg.Build.Windows.Cmd)
}
if cfg.Build.Windows.Bin != `tmp\main.exe` {
t.Fatalf("windows bin mismatch: got %s", cfg.Build.Windows.Bin)
}
if !reflect.DeepEqual(cfg.Build.Windows.Entrypoint, entrypoint{`tmp\main.exe`}) {
t.Fatalf("windows entrypoint mismatch: got %v", cfg.Build.Windows.Entrypoint)
}
}
func TestDefaultConfigForOS(t *testing.T) {
t.Parallel()
winCfg := defaultConfigForOS(PlatformWindows)
if winCfg.Build.Cmd != "go build -o ./tmp/main.exe ." {
t.Fatalf("windows cmd mismatch: got %q", winCfg.Build.Cmd)
}
if winCfg.Build.Bin != `tmp\main.exe` {
t.Fatalf("windows bin mismatch: got %q", winCfg.Build.Bin)
}
linuxCfg := defaultConfigForOS("linux")
if linuxCfg.Build.Cmd != "go build -o ./tmp/main ." {
t.Fatalf("linux cmd mismatch: got %q", linuxCfg.Build.Cmd)
}
if linuxCfg.Build.Bin != "./tmp/main" {
t.Fatalf("linux bin mismatch: got %q", linuxCfg.Build.Bin)
}
}
func TestPlatformBuildOverridesSelection(t *testing.T) {
t.Parallel()
win := &cfgBuildOverrides{CfgBuildCommon: CfgBuildCommon{Cmd: "win"}}
darwin := &cfgBuildOverrides{CfgBuildCommon: CfgBuildCommon{Cmd: "darwin"}}
linux := &cfgBuildOverrides{CfgBuildCommon: CfgBuildCommon{Cmd: "linux"}}
build := &cfgBuild{Windows: win, Darwin: darwin, Linux: linux}
if got := platformBuildOverrides(build, PlatformWindows); got != win {
t.Fatalf("windows override mismatch: got %v", got)
}
if got := platformBuildOverrides(build, "darwin"); got != darwin {
t.Fatalf("darwin override mismatch: got %v", got)
}
if got := platformBuildOverrides(build, "linux"); got != linux {
t.Fatalf("linux override mismatch: got %v", got)
}
if got := platformBuildOverrides(build, "freebsd"); got != nil {
t.Fatalf("unknown platform should return nil, got %v", got)
}
if got := platformBuildOverrides(nil, PlatformWindows); got != nil {
t.Fatalf("nil build should return nil, got %v", got)
}
}
func TestBuildOverridesFromDiff(t *testing.T) {
t.Parallel()
base := defaultConfigBase().Build
if got := buildOverridesFromDiff(base, base); got != nil {
t.Fatalf("expected nil override for identical configs, got %v", got)
}
target := base
target.PreCmd = []string{"echo pre"}
target.Cmd = "go build -o ./tmp/custom ."
target.PostCmd = []string{"echo post"}
target.Bin = "./tmp/custom"
target.Entrypoint = entrypoint{"./tmp/custom", "serve"}
target.FullBin = "APP_ENV=dev ./tmp/custom"
target.ArgsBin = []string{"--port", "8080"}
got := buildOverridesFromDiff(base, target)
if got == nil {
t.Fatal("expected non-nil override for changed configs")
}
if !reflect.DeepEqual(got.PreCmd, target.PreCmd) {
t.Fatalf("pre_cmd mismatch: got %v want %v", got.PreCmd, target.PreCmd)
}
if got.Cmd != target.Cmd {
t.Fatalf("cmd mismatch: got %q want %q", got.Cmd, target.Cmd)
}
if !reflect.DeepEqual(got.PostCmd, target.PostCmd) {
t.Fatalf("post_cmd mismatch: got %v want %v", got.PostCmd, target.PostCmd)
}
if got.Bin != target.Bin {
t.Fatalf("bin mismatch: got %q want %q", got.Bin, target.Bin)
}
if !reflect.DeepEqual(got.Entrypoint, target.Entrypoint) {
t.Fatalf("entrypoint mismatch: got %v want %v", got.Entrypoint, target.Entrypoint)
}
if got.FullBin != target.FullBin {
t.Fatalf("full_bin mismatch: got %q want %q", got.FullBin, target.FullBin)
}
if !reflect.DeepEqual(got.ArgsBin, target.ArgsBin) {
t.Fatalf("args_bin mismatch: got %v want %v", got.ArgsBin, target.ArgsBin)
}
}
func TestSetEntrypointFromBin(t *testing.T) {
t.Parallel()
cfg := defaultConfigBase()
setEntrypointFromBin(&cfg)
if !reflect.DeepEqual(cfg.Build.Entrypoint, entrypoint{"./tmp/main"}) {
t.Fatalf("entrypoint mismatch: got %v", cfg.Build.Entrypoint)
}
cfgWithEntry := defaultConfigBase()
cfgWithEntry.Build.Entrypoint = entrypoint{"./tmp/custom"}
setEntrypointFromBin(&cfgWithEntry)
if !reflect.DeepEqual(cfgWithEntry.Build.Entrypoint, entrypoint{"./tmp/custom"}) {
t.Fatalf("existing entrypoint should not be overwritten, got %v", cfgWithEntry.Build.Entrypoint)
}
cfgEmptyBin := defaultConfigBase()
cfgEmptyBin.Build.Bin = ""
setEntrypointFromBin(&cfgEmptyBin)
if len(cfgEmptyBin.Build.Entrypoint) != 0 {
t.Fatalf("entrypoint should remain empty when bin is empty, got %v", cfgEmptyBin.Build.Entrypoint)
}
}
func contains(sl []string, target string) bool {
for _, c := range sl {
if c == target {
@@ -360,6 +520,124 @@ cmd = "go build -o ./tmp/main ."
}
}
func TestInitConfigAppliesWindowsBuildOverride(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir()
cfgPath := filepath.Join(tmpDir, ".air.toml")
cfgContent := `
[build]
cmd = "base-cmd"
entrypoint = ["./tmp/base"]
args_bin = ["base-arg"]
[build.windows]
cmd = "windows-cmd"
entrypoint = ["tmp\\main.exe"]
args_bin = ["win-arg"]
`
if err := os.WriteFile(cfgPath, []byte(cfgContent), 0o644); err != nil {
t.Fatalf("failed to write config: %v", err)
}
cfg, err := InitConfig(cfgPath, nil)
if err != nil {
t.Fatalf("InitConfig error: %v", err)
}
if runtime.GOOS == PlatformWindows {
if cfg.Build.Cmd != "windows-cmd" {
t.Fatalf("expected windows cmd, got %q", cfg.Build.Cmd)
}
if !contains(cfg.Build.ArgsBin, "win-arg") {
t.Fatalf("expected windows args_bin to contain %q, got %v", "win-arg", cfg.Build.ArgsBin)
}
if !strings.HasSuffix(cfg.Build.Entrypoint.binary(), filepath.Join("tmp", "main.exe")) {
t.Fatalf("expected windows entrypoint suffix, got %q", cfg.Build.Entrypoint.binary())
}
return
}
if cfg.Build.Cmd != "base-cmd" {
t.Fatalf("expected base cmd on non-windows, got %q", cfg.Build.Cmd)
}
if !contains(cfg.Build.ArgsBin, "base-arg") {
t.Fatalf("expected base args_bin to contain %q on non-windows, got %v", "base-arg", cfg.Build.ArgsBin)
}
if !strings.HasSuffix(cfg.Build.Entrypoint.binary(), filepath.Join("tmp", "base")) {
t.Fatalf("expected base entrypoint suffix, got %q", cfg.Build.Entrypoint.binary())
}
}
func TestInitConfigAppliesCurrentPlatformBuildOverride(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir()
cfgPath := filepath.Join(tmpDir, ".air.toml")
cfgContent := `
[build]
cmd = "base-cmd"
entrypoint = ["./tmp/base"]
args_bin = ["base-arg"]
[build.windows]
cmd = "windows-cmd"
entrypoint = ["tmp\\main.exe"]
args_bin = ["win-arg"]
[build.darwin]
cmd = "darwin-cmd"
entrypoint = ["./tmp/darwin"]
args_bin = ["darwin-arg"]
[build.linux]
cmd = "linux-cmd"
entrypoint = ["./tmp/linux"]
args_bin = ["linux-arg"]
`
if err := os.WriteFile(cfgPath, []byte(cfgContent), 0o644); err != nil {
t.Fatalf("failed to write config: %v", err)
}
cfg, err := InitConfig(cfgPath, nil)
if err != nil {
t.Fatalf("InitConfig error: %v", err)
}
switch runtime.GOOS {
case PlatformWindows:
if cfg.Build.Cmd != "windows-cmd" {
t.Fatalf("expected windows cmd, got %q", cfg.Build.Cmd)
}
if !contains(cfg.Build.ArgsBin, "win-arg") {
t.Fatalf("expected windows args_bin to contain %q, got %v", "win-arg", cfg.Build.ArgsBin)
}
if !strings.HasSuffix(cfg.Build.Entrypoint.binary(), filepath.Join("tmp", "main.exe")) {
t.Fatalf("expected windows entrypoint suffix, got %q", cfg.Build.Entrypoint.binary())
}
case "darwin":
if cfg.Build.Cmd != "darwin-cmd" {
t.Fatalf("expected darwin cmd, got %q", cfg.Build.Cmd)
}
if !contains(cfg.Build.ArgsBin, "darwin-arg") {
t.Fatalf("expected darwin args_bin to contain %q, got %v", "darwin-arg", cfg.Build.ArgsBin)
}
if !strings.HasSuffix(cfg.Build.Entrypoint.binary(), filepath.Join("tmp", "darwin")) {
t.Fatalf("expected darwin entrypoint suffix, got %q", cfg.Build.Entrypoint.binary())
}
default:
if cfg.Build.Cmd != "linux-cmd" {
t.Fatalf("expected linux cmd, got %q", cfg.Build.Cmd)
}
if !contains(cfg.Build.ArgsBin, "linux-arg") {
t.Fatalf("expected linux args_bin to contain %q, got %v", "linux-arg", cfg.Build.ArgsBin)
}
if !strings.HasSuffix(cfg.Build.Entrypoint.binary(), filepath.Join("tmp", "linux")) {
t.Fatalf("expected linux entrypoint suffix, got %q", cfg.Build.Entrypoint.binary())
}
}
}
func TestTmpDirAdjustsDefaults(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir()
@@ -419,8 +697,10 @@ func TestTmpDirAdjustsDefaultsWindows(t *testing.T) {
cfg := &Config{
TmpDir: ".tmp",
Build: cfgBuild{
Cmd: "go build -o ./tmp/main.exe .",
Bin: `tmp\main.exe`,
CfgBuildCommon: CfgBuildCommon{
Cmd: "go build -o ./tmp/main.exe .",
Bin: `tmp\main.exe`,
},
ExcludeDir: []string{"assets", "tmp", "vendor", "testdata"},
},
}
@@ -485,8 +765,10 @@ func TestTmpDirAdjustsDefaultsWithAbsolutePath(t *testing.T) {
cfg := &Config{
TmpDir: "/tmp/air-build",
Build: cfgBuild{
Cmd: "go build -o ./tmp/main .",
Bin: "./tmp/main",
CfgBuildCommon: CfgBuildCommon{
Cmd: "go build -o ./tmp/main .",
Bin: "./tmp/main",
},
ExcludeDir: []string{"assets", "tmp", "vendor", "testdata"},
},
}
@@ -512,8 +794,10 @@ func TestTmpDirAdjustsDefaultsWithWindowsAbsolutePath(t *testing.T) {
cfg := &Config{
TmpDir: `C:\tmp\air-build`,
Build: cfgBuild{
Cmd: "go build -o ./tmp/main.exe .",
Bin: `tmp\main.exe`,
CfgBuildCommon: CfgBuildCommon{
Cmd: "go build -o ./tmp/main.exe .",
Bin: `tmp\main.exe`,
},
ExcludeDir: []string{"assets", "tmp", "vendor", "testdata"},
},
}
@@ -539,8 +823,10 @@ func TestTmpDirDoesNotOverrideExplicitExcludeDir(t *testing.T) {
cfg := &Config{
TmpDir: ".tmp",
Build: cfgBuild{
Cmd: "go build -o ./tmp/main .",
Bin: "./tmp/main",
CfgBuildCommon: CfgBuildCommon{
Cmd: "go build -o ./tmp/main .",
Bin: "./tmp/main",
},
ExcludeDir: []string{"tmp", "node_modules"},
},
}
+13 -3
View File
@@ -931,9 +931,19 @@ func TestWriteDefaultConfig(t *testing.T) {
if err != nil {
t.Fatal(err)
}
expect := defaultConfig()
if len(expect.Build.Entrypoint) == 0 && expect.Build.Bin != "" {
expect.Build.Entrypoint = entrypoint{expect.Build.Bin}
expect := defaultConfigBase()
setEntrypointFromBin(&expect)
addPlatformOverridesForInit(&expect, runtime.GOOS)
if expect.Build.Windows != nil {
if expect.Build.Windows.PreCmd == nil {
expect.Build.Windows.PreCmd = []string{}
}
if expect.Build.Windows.PostCmd == nil {
expect.Build.Windows.PostCmd = []string{}
}
if expect.Build.Windows.ArgsBin == nil {
expect.Build.Windows.ArgsBin = []string{}
}
}
assert.Equal(t, expect, *actual)
+4
View File
@@ -453,6 +453,10 @@ func setTage2Map(root string, t reflect.Type, v reflect.Value, m map[string]Toml
tomlVal := field.Tag.Get("toml")
if field.Type.Kind() == reflect.Struct {
if field.Anonymous {
setTage2Map(root, field.Type, fieldValue, m, fieldPath)
continue
}
path := fieldPath + field.Name + "."
setTage2Map(root+tomlVal+".", field.Type, fieldValue, m, path)
continue
+15 -5
View File
@@ -190,7 +190,9 @@ func TestAdaptToVariousPlatforms(t *testing.T) {
t.Parallel()
config := &Config{
Build: cfgBuild{
Bin: "tmp\\main.exe -dev",
CfgBuildCommon: CfgBuildCommon{
Bin: "tmp\\main.exe -dev",
},
},
}
adaptToVariousPlatforms(config)
@@ -412,8 +414,10 @@ func TestIsIncludeExtWildcard(t *testing.T) {
config: &Config{
Root: tmpDir,
Build: cfgBuild{
CfgBuildCommon: CfgBuildCommon{
Entrypoint: entrypoint{binPath},
},
IncludeExt: []string{"*"},
Entrypoint: entrypoint{binPath},
},
},
}
@@ -432,8 +436,10 @@ func TestIsIncludeExtWildcardWithSpaces(t *testing.T) {
e := Engine{
config: &Config{
Build: cfgBuild{
CfgBuildCommon: CfgBuildCommon{
Entrypoint: entrypoint{"/tmp/main"},
},
IncludeExt: []string{" * "},
Entrypoint: entrypoint{"/tmp/main"},
},
},
}
@@ -450,7 +456,9 @@ func TestIsBinPath(t *testing.T) {
config: &Config{
Root: tmpDir,
Build: cfgBuild{
Entrypoint: entrypoint{binPath},
CfgBuildCommon: CfgBuildCommon{
Entrypoint: entrypoint{binPath},
},
},
},
}
@@ -467,7 +475,9 @@ func TestIsBinPathEmptyBinPath(t *testing.T) {
e := Engine{
config: &Config{
Build: cfgBuild{
Entrypoint: entrypoint{}, // empty entrypoint
CfgBuildCommon: CfgBuildCommon{
Entrypoint: entrypoint{}, // empty entrypoint
},
},
},
}
+3 -1
View File
@@ -34,7 +34,9 @@ func TestAdaptToVariousPlatformsFullBinWindows(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
config := &Config{
Build: cfgBuild{
FullBin: tt.fullBin,
CfgBuildCommon: CfgBuildCommon{
FullBin: tt.fullBin,
},
},
}
adaptToVariousPlatforms(config)