Skip to content

Commit bb7fe28

Browse files
xiantangjingdi.zhu
and
jingdi.zhu
authored
feat:support command line arguments when user not want use config file (#180)
Co-authored-by: jingdi.zhu <[email protected]>
1 parent 7b308c5 commit bb7fe28

13 files changed

+337
-44
lines changed

README.md

+9
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ NOTE: This tool has nothing to do with hot-deploy for production.
2424
* Allow watching new directories after Air started
2525
* Better building process
2626

27+
### ✨ beta feature
28+
Support air config fields as arguments:
29+
30+
if you just want to config build command and run command, you can use like following command without config file:
31+
32+
`air --build.cmd "go build -o bin/api cmd/run.go" --build.bin "./api"`
33+
34+
35+
2736
## Installation
2837

2938
### Prefer install.sh

go.mod

+1-2
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@ require (
77
github.com/fsnotify/fsnotify v1.4.9
88
github.com/imdario/mergo v0.3.12
99
github.com/pelletier/go-toml v1.8.1
10+
github.com/stretchr/testify v1.7.1
1011
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
1112
)
1213

1314
require github.com/creack/pty v1.1.11
1415

15-
require github.com/stretchr/testify v1.7.1
16-
1716
require (
1817
github.com/davecgh/go-spew v1.1.1 // indirect
1918
github.com/mattn/go-colorable v0.1.8 // indirect

main.go

+15-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ var (
1515
cfgPath string
1616
debugMode bool
1717
showVersion bool
18+
cmdArgs map[string]runner.TomlInfo
1819
runArgs []string
1920
)
2021

@@ -29,11 +30,17 @@ func helpMessage() {
2930
}
3031

3132
func init() {
33+
parseFlag(os.Args[1:])
34+
}
35+
36+
func parseFlag(args []string) {
3237
flag.Usage = helpMessage
3338
flag.StringVar(&cfgPath, "c", "", "config path")
3439
flag.BoolVar(&debugMode, "d", false, "debug mode")
3540
flag.BoolVar(&showVersion, "v", false, "show version")
36-
flag.Parse()
41+
cmd := flag.CommandLine
42+
cmdArgs = runner.ParseConfigFlag(cmd)
43+
flag.CommandLine.Parse(args)
3744
}
3845

3946
func main() {
@@ -51,12 +58,17 @@ func main() {
5158
if debugMode {
5259
fmt.Println("[debug] mode")
5360
}
54-
5561
sigs := make(chan os.Signal, 1)
5662
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
5763

5864
var err error
59-
r, err := runner.NewEngine(cfgPath, debugMode)
65+
cfg, err := runner.InitConfig(cfgPath)
66+
if err != nil {
67+
log.Fatal(err)
68+
return
69+
}
70+
cfg.WithArgs(cmdArgs)
71+
r, err := runner.NewEngineWithConfig(cfg, debugMode)
6072
if err != nil {
6173
log.Fatal(err)
6274
return

runner/cmdarg_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package runner

runner/config.go

+30-17
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"log"
77
"os"
88
"path/filepath"
9+
"reflect"
910
"regexp"
1011
"runtime"
1112
"time"
@@ -20,7 +21,8 @@ const (
2021
airWd = "air_wd"
2122
)
2223

23-
type config struct {
24+
// Config is the main configuration structure for Air.
25+
type Config struct {
2426
Root string `toml:"root"`
2527
TmpDir string `toml:"tmp_dir"`
2628
TestDataDir string `toml:"testdata_dir"`
@@ -85,7 +87,8 @@ type cfgScreen struct {
8587
ClearOnRebuild bool `toml:"clear_on_rebuild"`
8688
}
8789

88-
func initConfig(path string) (cfg *config, err error) {
90+
// InitConfig initializes the configuration.
91+
func InitConfig(path string) (cfg *Config, err error) {
8992
if path == "" {
9093
cfg, err = defaultPathConfig()
9194
if err != nil {
@@ -140,7 +143,7 @@ func writeDefaultConfig() {
140143
fmt.Printf("%s file created to the current directory with the default settings\n", dftTOML)
141144
}
142145

143-
func defaultPathConfig() (*config, error) {
146+
func defaultPathConfig() (*Config, error) {
144147
// when path is blank, first find `.air.toml`, `.air.conf` in `air_wd` and current working directory, if not found, use defaults
145148
for _, name := range []string{dftTOML, dftConf} {
146149
cfg, err := readConfByName(name)
@@ -156,7 +159,7 @@ func defaultPathConfig() (*config, error) {
156159
return &dftCfg, nil
157160
}
158161

159-
func readConfByName(name string) (*config, error) {
162+
func readConfByName(name string) (*Config, error) {
160163
var path string
161164
if wd := os.Getenv(airWd); wd != "" {
162165
path = filepath.Join(wd, name)
@@ -171,7 +174,7 @@ func readConfByName(name string) (*config, error) {
171174
return cfg, err
172175
}
173176

174-
func defaultConfig() config {
177+
func defaultConfig() Config {
175178
build := cfgBuild{
176179
Cmd: "go build -o ./tmp/main .",
177180
Bin: "./tmp/main",
@@ -201,7 +204,7 @@ func defaultConfig() config {
201204
misc := cfgMisc{
202205
CleanOnExit: false,
203206
}
204-
return config{
207+
return Config{
205208
Root: ".",
206209
TmpDir: "tmp",
207210
TestDataDir: "testdata",
@@ -212,21 +215,21 @@ func defaultConfig() config {
212215
}
213216
}
214217

215-
func readConfig(path string) (*config, error) {
218+
func readConfig(path string) (*Config, error) {
216219
data, err := os.ReadFile(path)
217220
if err != nil {
218221
return nil, err
219222
}
220223

221-
cfg := new(config)
224+
cfg := new(Config)
222225
if err = toml.Unmarshal(data, cfg); err != nil {
223226
return nil, err
224227
}
225228

226229
return cfg, nil
227230
}
228231

229-
func readConfigOrDefault(path string) (*config, error) {
232+
func readConfigOrDefault(path string) (*Config, error) {
230233
dftCfg := defaultConfig()
231234
cfg, err := readConfig(path)
232235
if err != nil {
@@ -236,7 +239,7 @@ func readConfigOrDefault(path string) (*config, error) {
236239
return cfg, nil
237240
}
238241

239-
func (c *config) preprocess() error {
242+
func (c *Config) preprocess() error {
240243
var err error
241244
cwd := os.Getenv(airWd)
242245
if cwd != "" {
@@ -278,7 +281,7 @@ func (c *config) preprocess() error {
278281
return err
279282
}
280283

281-
func (c *config) colorInfo() map[string]string {
284+
func (c *Config) colorInfo() map[string]string {
282285
return map[string]string{
283286
"main": c.Color.Main,
284287
"build": c.Color.Build,
@@ -287,30 +290,40 @@ func (c *config) colorInfo() map[string]string {
287290
}
288291
}
289292

290-
func (c *config) buildLogPath() string {
293+
func (c *Config) buildLogPath() string {
291294
return filepath.Join(c.tmpPath(), c.Build.Log)
292295
}
293296

294-
func (c *config) buildDelay() time.Duration {
297+
func (c *Config) buildDelay() time.Duration {
295298
return time.Duration(c.Build.Delay) * time.Millisecond
296299
}
297300

298-
func (c *config) binPath() string {
301+
func (c *Config) binPath() string {
299302
return filepath.Join(c.Root, c.Build.Bin)
300303
}
301304

302-
func (c *config) tmpPath() string {
305+
func (c *Config) tmpPath() string {
303306
return filepath.Join(c.Root, c.TmpDir)
304307
}
305308

306-
func (c *config) TestDataPath() string {
309+
func (c *Config) testDataPath() string {
307310
return filepath.Join(c.Root, c.TestDataDir)
308311
}
309312

310-
func (c *config) rel(path string) string {
313+
func (c *Config) rel(path string) string {
311314
s, err := filepath.Rel(c.Root, path)
312315
if err != nil {
313316
return ""
314317
}
315318
return s
316319
}
320+
321+
// WithArgs returns a new config with the given arguments added to the configuration.
322+
func (c *Config) WithArgs(args map[string]TomlInfo) {
323+
for _, value := range args {
324+
if value.Value != nil && *value.Value != "" {
325+
v := reflect.ValueOf(c)
326+
setValue2Struct(v, value.fieldPath, *value.Value)
327+
}
328+
}
329+
}

runner/config_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const (
1212
cmd = "go build -o ./tmp/main ."
1313
)
1414

15-
func getWindowsConfig() config {
15+
func getWindowsConfig() Config {
1616
build := cfgBuild{
1717
Cmd: "go build -o ./tmp/main .",
1818
Bin: "./tmp/main",
@@ -28,7 +28,7 @@ func getWindowsConfig() config {
2828
build.Cmd = cmd
2929
}
3030

31-
return config{
31+
return Config{
3232
Root: ".",
3333
TmpDir: "tmp",
3434
TestDataDir: "testdata",
@@ -110,7 +110,7 @@ func TestReadConfByName(t *testing.T) {
110110
_ = os.Unsetenv(airWd)
111111
config, _ := readConfByName(dftTOML)
112112
if config != nil {
113-
t.Fatalf("expect config is nil,but get a not nil config")
113+
t.Fatalf("expect Config is nil,but get a not nil Config")
114114
}
115115
}
116116

runner/engine.go

+13-9
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515

1616
// Engine ...
1717
type Engine struct {
18-
config *config
18+
config *Config
1919
logger *logger
2020
watcher *fsnotify.Watcher
2121
debugMode bool
@@ -37,14 +37,8 @@ type Engine struct {
3737
ll sync.Mutex // lock for logger
3838
}
3939

40-
// NewEngine ...
41-
func NewEngine(cfgPath string, debugMode bool) (*Engine, error) {
42-
var err error
43-
cfg, err := initConfig(cfgPath)
44-
if err != nil {
45-
return nil, err
46-
}
47-
40+
// NewEngineWithConfig ...
41+
func NewEngineWithConfig(cfg *Config, debugMode bool) (*Engine, error) {
4842
logger := newLogger(cfg)
4943
watcher, err := fsnotify.NewWatcher()
5044
if err != nil {
@@ -70,6 +64,16 @@ func NewEngine(cfgPath string, debugMode bool) (*Engine, error) {
7064
return &e, nil
7165
}
7266

67+
// NewEngine ...
68+
func NewEngine(cfgPath string, debugMode bool) (*Engine, error) {
69+
var err error
70+
cfg, err := InitConfig(cfgPath)
71+
if err != nil {
72+
return nil, err
73+
}
74+
return NewEngineWithConfig(cfg, debugMode)
75+
}
76+
7377
// Run run run
7478
func (e *Engine) Run() {
7579
if len(os.Args) > 1 && os.Args[1] == "init" {

runner/engine_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func TestNewEngine(t *testing.T) {
2525
t.Fatal("logger should not be nil")
2626
}
2727
if engine.config == nil {
28-
t.Fatal("config should not be nil")
28+
t.Fatal("Config should not be nil")
2929
}
3030
if engine.watcher == nil {
3131
t.Fatal("watcher should not be nil")
@@ -200,7 +200,7 @@ func TestCtrlCWhenHaveKillDelay(t *testing.T) {
200200
// fix https://github.com/cosmtrek/air/issues/278
201201
// generate a random port
202202
data := []byte("[build]\n kill_delay = \"2s\"")
203-
c := config{}
203+
c := Config{}
204204
if err := toml.Unmarshal(data, &c); err != nil {
205205
t.Fatalf("Should not be fail: %s.", err)
206206
}

runner/flag.go

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package runner
2+
3+
import (
4+
"flag"
5+
)
6+
7+
// ParseConfigFlag parse toml information for flag
8+
func ParseConfigFlag(f *flag.FlagSet) map[string]TomlInfo {
9+
c := Config{}
10+
m := flatConfig(c)
11+
for k, v := range m {
12+
f.StringVar(v.Value, k, "", "")
13+
}
14+
return m
15+
}

0 commit comments

Comments
 (0)