Revert "feat: add build-specific platform overrides (#864)" (#865)

This reverts commit f5498179c6.
This commit is contained in:
xiantang
2026-01-19 21:34:27 +08:00
committed by GitHub
parent f5498179c6
commit 122d83caf7
4 changed files with 35 additions and 274 deletions
-32
View File
@@ -227,38 +227,6 @@ entrypoint = [
]
```
### 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.
### .env File
Air can automatically load environment variables from a `.env` file before both building and running.
```toml
# Load from .env file (default)
env_file = ".env"
# Load from a custom file
env_file = ".env.development"
# Disable env file loading
env_file = ""
```
### Docker Compose
```yaml
-6
View File
@@ -59,12 +59,6 @@ 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
+35 -193
View File
@@ -79,45 +79,32 @@ func (e entrypoint) args() []string {
return e[1:]
}
type cfgBuildOverrides struct {
PreCmd []string `toml:"pre_cmd,omitempty" usage:"Array of commands to run before each build"`
Cmd string `toml:"cmd,omitempty" usage:"Just plain old shell command. You could use 'make' as well"`
PostCmd []string `toml:"post_cmd,omitempty" usage:"Array of commands to run after ^C"`
Bin string `toml:"bin,omitempty" usage:"Binary file yields from 'cmd', will be deprecated soon, recommend using entrypoint."`
Entrypoint entrypoint `toml:"entrypoint,omitempty" usage:"Binary file plus optional arguments relative to root, prefer [\"./tmp/main\", \"arg\"] form"`
FullBin string `toml:"full_bin,omitempty" usage:"Customize binary, can setup environment variables when run your app"`
ArgsBin []string `toml:"args_bin,omitempty" 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 (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"`
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 (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"`
regexCompiled []*regexp.Regexp
includeDirAbs []string
extraIncludeDirs []string
@@ -209,6 +196,7 @@ func InitConfig(path string, cmdArgs map[string]TomlInfo) (cfg *Config, err erro
return nil, err
}
}
warnDeprecatedBin(cfg)
config := defaultConfig()
// get addr
ret := &config
@@ -223,12 +211,6 @@ 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
}
@@ -248,9 +230,10 @@ func writeDefaultConfig() (string, error) {
}
defer file.Close()
config := defaultConfigBase()
setEntrypointFromBin(&config)
addPlatformOverridesForInit(&config, runtime.GOOS)
config := defaultConfig()
if len(config.Build.Entrypoint) == 0 && config.Build.Bin != "" {
config.Build.Entrypoint = entrypoint{config.Build.Bin}
}
configFile, err := toml.Marshal(config)
if err != nil {
return "", fmt.Errorf("failed to marshal the default configuration: %w", err)
@@ -299,7 +282,7 @@ func readConfByName(name string) (*Config, error) {
return cfg, err
}
func defaultConfigBase() Config {
func defaultConfig() Config {
build := cfgBuild{
Cmd: "go build -o ./tmp/main .",
Bin: "./tmp/main",
@@ -318,6 +301,10 @@ func defaultConfigBase() Config {
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,
@@ -347,151 +334,6 @@ func defaultConfigBase() 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 {
-43
View File
@@ -302,49 +302,6 @@ func TestKillDelay(t *testing.T) {
}
}
func TestApplyBuildOverrides(t *testing.T) {
base := defaultConfigBase()
override := &cfgBuildOverrides{
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 contains(sl []string, target string) bool {
for _, c := range sl {
if c == target {