Skip to content

Commit

Permalink
feat:support command line arguments when user not want use config file (
Browse files Browse the repository at this point in the history
#180)

Co-authored-by: jingdi.zhu <[email protected]>
  • Loading branch information
xiantang and jingdi.zhu authored Jun 6, 2022
1 parent 7b308c5 commit bb7fe28
Show file tree
Hide file tree
Showing 13 changed files with 337 additions and 44 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ NOTE: This tool has nothing to do with hot-deploy for production.
* Allow watching new directories after Air started
* Better building process

### ✨ beta feature
Support air config fields as arguments:

if you just want to config build command and run command, you can use like following command without config file:

`air --build.cmd "go build -o bin/api cmd/run.go" --build.bin "./api"`



## Installation

### Prefer install.sh
Expand Down
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ require (
github.com/fsnotify/fsnotify v1.4.9
github.com/imdario/mergo v0.3.12
github.com/pelletier/go-toml v1.8.1
github.com/stretchr/testify v1.7.1
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
)

require github.com/creack/pty v1.1.11

require github.com/stretchr/testify v1.7.1

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
Expand Down
18 changes: 15 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var (
cfgPath string
debugMode bool
showVersion bool
cmdArgs map[string]runner.TomlInfo
runArgs []string
)

Expand All @@ -29,11 +30,17 @@ func helpMessage() {
}

func init() {
parseFlag(os.Args[1:])
}

func parseFlag(args []string) {
flag.Usage = helpMessage
flag.StringVar(&cfgPath, "c", "", "config path")
flag.BoolVar(&debugMode, "d", false, "debug mode")
flag.BoolVar(&showVersion, "v", false, "show version")
flag.Parse()
cmd := flag.CommandLine
cmdArgs = runner.ParseConfigFlag(cmd)
flag.CommandLine.Parse(args)
}

func main() {
Expand All @@ -51,12 +58,17 @@ func main() {
if debugMode {
fmt.Println("[debug] mode")
}

sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

var err error
r, err := runner.NewEngine(cfgPath, debugMode)
cfg, err := runner.InitConfig(cfgPath)
if err != nil {
log.Fatal(err)
return
}
cfg.WithArgs(cmdArgs)
r, err := runner.NewEngineWithConfig(cfg, debugMode)
if err != nil {
log.Fatal(err)
return
Expand Down
1 change: 1 addition & 0 deletions runner/cmdarg_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package runner
47 changes: 30 additions & 17 deletions runner/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"log"
"os"
"path/filepath"
"reflect"
"regexp"
"runtime"
"time"
Expand All @@ -20,7 +21,8 @@ const (
airWd = "air_wd"
)

type config struct {
// Config is the main configuration structure for Air.
type Config struct {
Root string `toml:"root"`
TmpDir string `toml:"tmp_dir"`
TestDataDir string `toml:"testdata_dir"`
Expand Down Expand Up @@ -85,7 +87,8 @@ type cfgScreen struct {
ClearOnRebuild bool `toml:"clear_on_rebuild"`
}

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

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

func readConfByName(name string) (*config, error) {
func readConfByName(name string) (*Config, error) {
var path string
if wd := os.Getenv(airWd); wd != "" {
path = filepath.Join(wd, name)
Expand All @@ -171,7 +174,7 @@ func readConfByName(name string) (*config, error) {
return cfg, err
}

func defaultConfig() config {
func defaultConfig() Config {
build := cfgBuild{
Cmd: "go build -o ./tmp/main .",
Bin: "./tmp/main",
Expand Down Expand Up @@ -201,7 +204,7 @@ func defaultConfig() config {
misc := cfgMisc{
CleanOnExit: false,
}
return config{
return Config{
Root: ".",
TmpDir: "tmp",
TestDataDir: "testdata",
Expand All @@ -212,21 +215,21 @@ func defaultConfig() config {
}
}

func readConfig(path string) (*config, error) {
func readConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}

cfg := new(config)
cfg := new(Config)
if err = toml.Unmarshal(data, cfg); err != nil {
return nil, err
}

return cfg, nil
}

func readConfigOrDefault(path string) (*config, error) {
func readConfigOrDefault(path string) (*Config, error) {
dftCfg := defaultConfig()
cfg, err := readConfig(path)
if err != nil {
Expand All @@ -236,7 +239,7 @@ func readConfigOrDefault(path string) (*config, error) {
return cfg, nil
}

func (c *config) preprocess() error {
func (c *Config) preprocess() error {
var err error
cwd := os.Getenv(airWd)
if cwd != "" {
Expand Down Expand Up @@ -278,7 +281,7 @@ func (c *config) preprocess() error {
return err
}

func (c *config) colorInfo() map[string]string {
func (c *Config) colorInfo() map[string]string {
return map[string]string{
"main": c.Color.Main,
"build": c.Color.Build,
Expand All @@ -287,30 +290,40 @@ func (c *config) colorInfo() map[string]string {
}
}

func (c *config) buildLogPath() string {
func (c *Config) buildLogPath() string {
return filepath.Join(c.tmpPath(), c.Build.Log)
}

func (c *config) buildDelay() time.Duration {
func (c *Config) buildDelay() time.Duration {
return time.Duration(c.Build.Delay) * time.Millisecond
}

func (c *config) binPath() string {
func (c *Config) binPath() string {
return filepath.Join(c.Root, c.Build.Bin)
}

func (c *config) tmpPath() string {
func (c *Config) tmpPath() string {
return filepath.Join(c.Root, c.TmpDir)
}

func (c *config) TestDataPath() string {
func (c *Config) testDataPath() string {
return filepath.Join(c.Root, c.TestDataDir)
}

func (c *config) rel(path string) string {
func (c *Config) rel(path string) string {
s, err := filepath.Rel(c.Root, path)
if err != nil {
return ""
}
return s
}

// 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 {
if value.Value != nil && *value.Value != "" {
v := reflect.ValueOf(c)
setValue2Struct(v, value.fieldPath, *value.Value)
}
}
}
6 changes: 3 additions & 3 deletions runner/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const (
cmd = "go build -o ./tmp/main ."
)

func getWindowsConfig() config {
func getWindowsConfig() Config {
build := cfgBuild{
Cmd: "go build -o ./tmp/main .",
Bin: "./tmp/main",
Expand All @@ -28,7 +28,7 @@ func getWindowsConfig() config {
build.Cmd = cmd
}

return config{
return Config{
Root: ".",
TmpDir: "tmp",
TestDataDir: "testdata",
Expand Down Expand Up @@ -110,7 +110,7 @@ func TestReadConfByName(t *testing.T) {
_ = os.Unsetenv(airWd)
config, _ := readConfByName(dftTOML)
if config != nil {
t.Fatalf("expect config is nil,but get a not nil config")
t.Fatalf("expect Config is nil,but get a not nil Config")
}
}

Expand Down
22 changes: 13 additions & 9 deletions runner/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

// Engine ...
type Engine struct {
config *config
config *Config
logger *logger
watcher *fsnotify.Watcher
debugMode bool
Expand All @@ -37,14 +37,8 @@ type Engine struct {
ll sync.Mutex // lock for logger
}

// NewEngine ...
func NewEngine(cfgPath string, debugMode bool) (*Engine, error) {
var err error
cfg, err := initConfig(cfgPath)
if err != nil {
return nil, err
}

// NewEngineWithConfig ...
func NewEngineWithConfig(cfg *Config, debugMode bool) (*Engine, error) {
logger := newLogger(cfg)
watcher, err := fsnotify.NewWatcher()
if err != nil {
Expand All @@ -70,6 +64,16 @@ func NewEngine(cfgPath string, debugMode bool) (*Engine, error) {
return &e, nil
}

// NewEngine ...
func NewEngine(cfgPath string, debugMode bool) (*Engine, error) {
var err error
cfg, err := InitConfig(cfgPath)
if err != nil {
return nil, err
}
return NewEngineWithConfig(cfg, debugMode)
}

// Run run run
func (e *Engine) Run() {
if len(os.Args) > 1 && os.Args[1] == "init" {
Expand Down
4 changes: 2 additions & 2 deletions runner/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestNewEngine(t *testing.T) {
t.Fatal("logger should not be nil")
}
if engine.config == nil {
t.Fatal("config should not be nil")
t.Fatal("Config should not be nil")
}
if engine.watcher == nil {
t.Fatal("watcher should not be nil")
Expand Down Expand Up @@ -200,7 +200,7 @@ func TestCtrlCWhenHaveKillDelay(t *testing.T) {
// fix https://github.com/cosmtrek/air/issues/278
// generate a random port
data := []byte("[build]\n kill_delay = \"2s\"")
c := config{}
c := Config{}
if err := toml.Unmarshal(data, &c); err != nil {
t.Fatalf("Should not be fail: %s.", err)
}
Expand Down
15 changes: 15 additions & 0 deletions runner/flag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package runner

import (
"flag"
)

// ParseConfigFlag parse toml information for flag
func ParseConfigFlag(f *flag.FlagSet) map[string]TomlInfo {
c := Config{}
m := flatConfig(c)
for k, v := range m {
f.StringVar(v.Value, k, "", "")
}
return m
}
Loading

0 comments on commit bb7fe28

Please sign in to comment.