Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions conf/block_posix.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// +build !windows
//go:build !windows
// +build !windows

package conf

import (
"fmt"
"os"
"syscall"
)

Expand All @@ -12,7 +14,7 @@ func (b *Block) addDaemon(command string, options []string) error {
b.Daemons = []Daemon{}
}
d := Daemon{
Command: command,
Command: os.ExpandEnv(command),
RestartSignal: syscall.SIGHUP,
}
for _, v := range options {
Expand Down
6 changes: 4 additions & 2 deletions conf/block_windows.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// +build windows
//go:build windows
// +build windows

package conf

import (
"fmt"
"os"
"syscall"
)

Expand All @@ -12,7 +14,7 @@ func (b *Block) addDaemon(command string, options []string) error {
b.Daemons = []Daemon{}
}
d := Daemon{
Command: command,
Command: os.ExpandEnv(command),
RestartSignal: syscall.SIGHUP,
}
for _, v := range options {
Expand Down
7 changes: 5 additions & 2 deletions conf/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ type Block struct {
Exclude []string
NoCommonFilter bool
InDir string
// todo: cumulative env with envEndIdx
// envEndIdx int
Env []string

Daemons []Daemon
Preps []Prep
Expand All @@ -34,7 +37,7 @@ func (b *Block) addPrep(command string, options []string) error {
b.Preps = []Prep{}
}

var onchange = false
onchange := false
for _, v := range options {
switch v {
case "+onchange":
Expand All @@ -44,7 +47,7 @@ func (b *Block) addPrep(command string, options []string) error {
}
}

prep := Prep{command, onchange}
prep := Prep{os.ExpandEnv(command), onchange}

b.Preps = append(b.Preps, prep)
return nil
Expand Down
50 changes: 50 additions & 0 deletions conf/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package conf

import (
"bufio"
"errors"
"fmt"
"os"
"regexp"
"strings"
)

var validEnv = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z_0-9]*$`)

func processEnvFile(envFile string) ([]string, error) {
_, err := os.Stat(envFile)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil, fmt.Errorf(" not found")
}
return nil, err
}
f, err := os.Open(envFile)
if err != nil {
return nil, err
}
defer f.Close()

var envOut []string
lineNo := 0
s := bufio.NewScanner(f)
for s.Scan() {
lineNo++
line := strings.TrimSpace(s.Text())
if line == "" || line[0] == '#' {
continue
}
p := strings.SplitN(line, `=`, 2)
if !validEnv.MatchString(p[0]) {
return nil, fmt.Errorf("%d: invalid environment variable %q", lineNo, p[0])
}
if len(p) == 2 && p[1] == "" {
line = p[0]
}
envOut = append(envOut, line)
}
if s.Err() != nil {
return nil, fmt.Errorf("scan error: %v", err)
}
return envOut, nil
}
22 changes: 18 additions & 4 deletions conf/lex.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ import (
"unicode/utf8"
)

const spaces = " \t"
const whitespace = spaces + "\n"
const wordRunes = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ_"
const quotes = `'"`
const (
spaces = " \t"
whitespace = spaces + "\n"
wordRunes = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ_"
quotes = `'"`
)

// Characters we don't allow in bare strings
const bareStringDisallowed = "{}#\n" + whitespace + quotes
Expand All @@ -38,6 +40,8 @@ const (
itemSpace
itemVarName
itemEquals
itemEnvVar
itemEnvFile
)

func (i itemType) String() string {
Expand Down Expand Up @@ -70,6 +74,10 @@ func (i itemType) String() string {
return "space"
case itemVarName:
return "var"
case itemEnvVar:
return "env"
case itemEnvFile:
return "envfile"
default:
panic("unreachable")
}
Expand Down Expand Up @@ -432,6 +440,12 @@ func lexInside(l *lexer) stateFn {
case "prep":
l.emit(itemPrep)
return lexOptions
case "envfile":
l.emit(itemEnvFile)
return lexOptions
// case "env":
// l.emit(itemEnvVar)
// return lexOptions
default:
l.errorf("unknown directive: %s", l.current())
return nil
Expand Down
10 changes: 10 additions & 0 deletions conf/lex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,16 @@ var lexTests = []struct {
{itemBareString, "b"},
},
},
{
"one {\nenvfile: foo\n}", []itm{
{itemBareString, "one"},
{itemLeftParen, "{"},
{itemEnvFile, "envfile"},
{itemColon, ":"},
{itemBareString, "foo\n"},
{itemRightParen, "}"},
},
},
}

func TestLex(t *testing.T) {
Expand Down
18 changes: 18 additions & 0 deletions conf/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package conf

import (
"fmt"
"os"
"path"
"path/filepath"
"runtime"
Expand Down Expand Up @@ -248,11 +249,28 @@ Loop:
dir = strings.Replace(
dir, confVarName, p.config.variables[confVarName], -1,
)
// Expand any OS environment variables that might be in the path
dir = os.ExpandEnv(dir)
dir, err := filepath.Abs(dir)
if err != nil {
p.errorf("%s", err)
}
block.InDir = dir
case itemEnvFile:
options := p.collectValues(itemBareString)
if len(options) > 0 {
p.errorf("envfile takes no options")
}
p.mustNext(itemColon)
envFile := prepValue(p.mustNext(itemBareString, itemQuotedString))
cleanEnv := filepath.Clean(envFile)
envVars, err := processEnvFile(cleanEnv)
if err != nil {
p.errorf("envfile %s:%s", envFile, err)
}
block.Env = append(block.Env, envVars...)
// case itemEnvVar:
// options := p.collectValues(itemBareString, itemQuotedString, itemEquals)
case itemDaemon:
options := p.collectValues(itemBareString)
p.mustNext(itemColon)
Expand Down
55 changes: 51 additions & 4 deletions conf/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ var parseTests = []struct {
Blocks: []Block{
{
Include: []string{"foo"},
Preps: []Prep{Prep{Command: "command"}},
Preps: []Prep{{Command: "command"}},
},
},
},
Expand All @@ -171,7 +171,7 @@ var parseTests = []struct {
Blocks: []Block{
{
Include: []string{"foo"},
Preps: []Prep{Prep{Command: "command", Onchange: true}},
Preps: []Prep{{Command: "command", Onchange: true}},
},
},
},
Expand All @@ -183,7 +183,7 @@ var parseTests = []struct {
Blocks: []Block{
{
Include: []string{"foo"},
Preps: []Prep{Prep{Command: "command\n-one\n-two"}},
Preps: []Prep{{Command: "command\n-one\n-two"}},
},
},
},
Expand All @@ -195,7 +195,7 @@ var parseTests = []struct {
Blocks: []Block{
{
Include: []string{"foo", "bar"},
Preps: []Prep{Prep{Command: "command"}},
Preps: []Prep{{Command: "command"}},
},
},
},
Expand Down Expand Up @@ -299,6 +299,51 @@ var parseTests = []struct {
},
},
},
{
"",
"foo {\nenvfile:testdata/good.env\nprep: command\n}",
&Config{
Blocks: []Block{
{
Include: []string{"foo"},
Env: []string{
"FOO=BAR",
`BAR="FOO"`,
"BAZ",
"WUG",
},
Preps: []Prep{
{
Command: "command",
},
},
},
},
},
},
{
"",
"foo {\nenvfile:testdata/good.env\nenvfile:testdata/good2.env\nprep: command\n}",
&Config{
Blocks: []Block{
{
Include: []string{"foo"},
Env: []string{
"FOO=BAR",
`BAR="FOO"`,
"BAZ",
"WUG",
"ALSO=GOOD",
},
Preps: []Prep{
{
Command: "command",
},
},
},
},
},
},
}

var parseCmpOptions = []cmp.Option{
Expand Down Expand Up @@ -335,6 +380,8 @@ var parseErrorTests = []struct {
{"@foo=bar\n@foo=bar {}", "test:2: variable @foo shadows previous declaration"},
{"{indir +foo: bar\n}", "test:1: indir takes no options"},
{"{indir: bar\nindir: voing\n}", "test:2: indir can only be used once per block"},
{"{envfile: bar\n}", "test:1: envfile bar: not found"},
{"{envfile: testdata/bad_var.env\n}", "test:1: envfile testdata/bad_var.env:1: invalid environment variable \"!NOPE\""},
}

func TestErrorsParse(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions conf/testdata/bad_var.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!NOPE=FOO
5 changes: 5 additions & 0 deletions conf/testdata/good.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FOO=BAR
BAR="FOO"
#COMMENTED=not here
BAZ=
WUG
1 change: 1 addition & 0 deletions conf/testdata/good2.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALSO=GOOD
5 changes: 3 additions & 2 deletions daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const (
type daemon struct {
conf conf.Daemon
indir string

env []string
ex *shell.Executor
log termlog.Stream
shell string
Expand Down Expand Up @@ -77,7 +77,7 @@ func (d *daemon) Restart() {
d.Lock()
defer d.Unlock()
if d.ex == nil {
ex, err := shell.NewExecutor(d.shell, d.conf.Command, d.indir)
ex, err := shell.NewExecutor(d.shell, d.conf.Command, d.indir, d.env)
if err != nil {
d.log.Shout("Could not create executor: %s", err)
}
Expand Down Expand Up @@ -138,6 +138,7 @@ func NewDaemonPen(block conf.Block, vars map[string]string, log termlog.TermLog)
log: log.Stream(niceHeader("daemon: ", dmn.Command)),
shell: sh,
indir: indir,
env: block.Env,
}
}
return &DaemonPen{daemons: d}, nil
Expand Down
24 changes: 16 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
module github.com/cortesi/modd

go 1.14
go 1.19

require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/cortesi/moddwatch v0.0.0-20210323234936-df014e95c743
github.com/cortesi/termlog v0.0.0-20210222042314-a1eec763abec
github.com/fatih/color v1.13.0 // indirect
github.com/google/go-cmp v0.5.9
gopkg.in/alecthomas/kingpin.v2 v2.2.6
mvdan.cc/sh/v3 v3.6.0
)

require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/bmatcuk/doublestar v1.3.4 // indirect
github.com/fatih/color v1.14.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/rjeczalik/notify v0.9.3 // indirect
github.com/stretchr/testify v1.8.1 // indirect
golang.org/x/crypto v0.4.0 // indirect
golang.org/x/net v0.4.0 // indirect
gopkg.in/alecthomas/kingpin.v2 v2.2.6
golang.org/x/crypto v0.6.0 // indirect
golang.org/x/net v0.6.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/term v0.5.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
mvdan.cc/sh/v3 v3.6.0
)
Loading