Skip to content

Commit b9f7bcf

Browse files
blaubaerrdsubhas
authored andcommitted
Optimizations for Windows (#74)
* Bring myke go run with regular Windows environment on Windows systems * Add environment variable expansion in the way golang does (very close to sh) that it behaves on every operating system and/or distribution in the same way to be more "cross platform" * Added appveyor config to also enable tests to run on an Windows environment. * * Fixed problem with case insensitve PATH variable under Windows is doubled set - and with a chance of 50% wrong. * Improved the output .. if a different Shell is used do not output "+ whatever" if a command starts.
1 parent 70b9f7a commit b9f7bcf

File tree

11 files changed

+109
-16
lines changed

11 files changed

+109
-16
lines changed

appveyor.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
version: "{build}"
2+
3+
os: Windows Server 2012 R2
4+
5+
clone_folder: c:\gopath\src\github.com\goeuro\myke
6+
7+
environment:
8+
GOPATH: C:\gopath
9+
10+
# Uncomment following line if you are willing to debug on the build machine.
11+
#init:
12+
#- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
13+
14+
install:
15+
- set PATH=%GOPATH%\bin;C:\go\bin;%WINDIR%;%WINDIR%\System32;C:\Program Files\Git\cmd;C:\Program Files\Git\usr\bin
16+
- go version
17+
- go env
18+
- sh c:\gopath\src\github.com\goeuro\myke\bin\init.sh
19+
20+
build_script:
21+
- gofmt -d -s -e . 2>&1 | tee -a fmt.out
22+
- test ! -s fmt.out
23+
- golint -set_exit_status .
24+
- go test -timeout 10s -v ./...
25+

core/execution.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,20 +89,26 @@ func (e *Execution) executeCmd(cmd string) error {
8989
return nil
9090
}
9191

92+
env := e.env()
9293
cmd, err := RenderTemplate(cmd, e.Project.Env, e.Query.Params)
9394
if err != nil {
9495
return err
9596
}
97+
cmd = os.Expand(cmd, func(key string) string {
98+
return env[key]
99+
})
96100

97-
shell := []string{"sh", "-exc"}
101+
e.beforeExecuteCmd(cmd, env)
102+
103+
shell := executionShell()
98104
if len(e.Task.Shell) > 0 {
99105
shell = strings.Split(strings.TrimSpace(e.Task.Shell), " ")
100106
}
101107
shell = append(shell, cmd)
102108

103109
proc := exec.Command(shell[0], shell[1:]...)
104110
proc.Dir = e.Project.Cwd
105-
proc.Env = e.envList()
111+
proc.Env = mapToSlice(env)
106112
proc.Stdin = os.Stdin
107113
proc.Stdout = os.Stdout
108114
proc.Stderr = os.Stderr
@@ -121,12 +127,3 @@ func (e *Execution) env() map[string]string {
121127
env["PATH"] = strings.Join([]string{env["PATH"], os.Getenv("PATH")}, pathSep)
122128
return env
123129
}
124-
125-
func (e *Execution) envList() []string {
126-
env := e.env()
127-
list := make([]string, len(env))
128-
for k, v := range env {
129-
list = append(list, k+"="+v)
130-
}
131-
return list
132-
}

core/execution_default.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// +build !windows
2+
3+
package core
4+
5+
func executionShell() []string {
6+
return []string{"sh", "-exc"}
7+
}
8+
9+
func (e *Execution) beforeExecuteCmd(cmd string, env map[string]string) error {
10+
return nil
11+
}

core/execution_windows.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// +build windows
2+
3+
package core
4+
5+
import (
6+
"os"
7+
"os/exec"
8+
"path/filepath"
9+
)
10+
11+
var comSpec string
12+
13+
func init() {
14+
comSpec = os.Getenv("ComSpec")
15+
if len(comSpec) > 0 {
16+
return
17+
}
18+
winDir := os.Getenv("windir")
19+
if len(winDir) > 0 {
20+
comSpec = filepath.Join(winDir, "system32", "cmd.exe")
21+
}
22+
comSpec = "C:\\windows\\system32\\cmd.exe"
23+
return
24+
}
25+
26+
func executionShell() []string {
27+
return []string{comSpec, "/C"}
28+
}
29+
30+
func (e *Execution) beforeExecuteCmd(cmd string, env map[string]string) error {
31+
if len(e.Task.Shell) > 0 {
32+
return nil
33+
}
34+
// This will cause the same output like the sh -x on unix like systems.
35+
proc := exec.Command(comSpec, "/C", "echo", "+", cmd)
36+
proc.Dir = e.Project.Cwd
37+
proc.Env = mapToSlice(env)
38+
proc.Stdout = os.Stdout
39+
proc.Stderr = os.Stderr
40+
return proc.Run()
41+
}

core/util.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ func OsEnv() map[string]string {
4141
env := make(map[string]string)
4242
for _, e := range os.Environ() {
4343
pair := strings.SplitN(e, "=", 2)
44-
if pair[0] != "PATH" {
44+
// ToUpper is important here because on Windows this is case insensitive
45+
if strings.ToUpper(pair[0]) != "PATH" {
4546
// PATH is handled as a special case, so lets skip it
4647
env[pair[0]] = pair[1]
4748
}
@@ -104,3 +105,13 @@ func retry(f func(attempt int) (retry bool, err error)) error {
104105
attempt++
105106
}
106107
}
108+
109+
func mapToSlice(in map[string]string) []string {
110+
list := make([]string, len(in))
111+
i := 0
112+
for k, v := range in {
113+
list[i] = k + "=" + v
114+
i++
115+
}
116+
return list
117+
}

examples/env/package_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ var tests = []TestTable{
1111
{Arg: `file_default_local`, Out: `envvar from myke.env.local is value_from_myke.env.local`},
1212
{Arg: `file_custom`, Out: `envvar from test.env is value_from_test.env`},
1313
{Arg: `file_custom_local`, Out: `envvar from test.env.local is value_from_test.env.local`},
14-
{Arg: `path`, Out: `PATH is [^:]+env/path_from_myke.env.local:[^:]+env/path_from_myke.env:[^:]+env/path_from_test.env.local:[^:]+env/path_from_test.env:[^:]+env/path_from_yml:[^:]+env/bin`},
14+
{Arg: `path`, Out: `PATH is [^$PLS$]+env$PS$path_from_myke.env.local$PLS$[^$PLS$]+env$PS$path_from_myke.env$PLS$[^$PLS$]+env$PS$path_from_test.env.local$PLS$[^$PLS$]+env$PS$path_from_test.env$PLS$[^$PLS$]+env$PS$path_from_yml$PLS$[^$PLS$]+env$PS$bin`},
1515
}
1616

1717
func Test(t *testing.T) {

examples/mixin/package_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ var tests = []TestTable{
99
{Arg: `task1`, Out: `parent says value_parent_1`},
1010
{Arg: `task2`, Out: `(?s)parent says value_child_2.*?child says value_child_2`},
1111
{Arg: `task3`, Out: `child says value_child_3`},
12-
{Arg: `path`, Out: `PATH is [^:]+mixin/path_child:[^:]+mixin/bin:[^:]+mixin/parent/path_parent:[^:]+mixin/parent/bin`},
12+
{Arg: `path`, Out: `PATH is [^$PLS$]+mixin$PS$path_child$PLS$[^$PLS$]+mixin$PS$bin$PLS$[^$PLS$]+mixin$PS$parent$PS$path_parent$PLS$[^$PLS$]+mixin$PS$parent$PS$bin`},
1313
}
1414

1515
func Test(t *testing.T) {

examples/package_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ var tests = []TestTable{
1313
{Arg: ``, Out: `(?m)^\s*retry\s*\|\s*\|\s*retry\s*$`},
1414
{Arg: ``, Out: `(?m)^\s*tags1\s*\|\s*tagA, tagB\s*\|\s*tag\s*$`},
1515
{Arg: ``, Out: `(?m)^\s*tags2\s*\|\s*tagB, tagC\s*\|\s*tag\s*$`},
16-
{Arg: ``, Out: `(?m)^\s*template\s*\|\s*\|\s*args, file\s*$`},
16+
{Arg: ``, Out: `(?m)^\s*template\s*\|\s*\|\s*args, envs, file\s*$`},
1717
}
1818

1919
func Test(t *testing.T) {

examples/template/myke.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ tasks:
88
args:
99
cmd: echo from={{.from|required}} to={{.to|default "something_to"}}
1010
desc: run as myke template/args[from=...,to=...]
11+
envs:
12+
cmd: echo PARAM1={{.PARAM2|required}} PARAM2={{.PARAM2|required}}
13+
desc: run as myke template/envs
1114
file:
1215
cmd: myke --template template.tpl
1316
desc: run as myke template/file

examples/template/package_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ var tests = []TestTable{
1010
{Arg: `args --from=a`, Out: `from=a to=something_to`},
1111
{Arg: `args --from=a --to=b`, Out: `from=a to=b`},
1212
{Arg: `args --from=a args --from=b`, Out: `(?s).*from=a to=something_to.*from=b to=something_to`},
13+
{Arg: `envs`, Out: `(?s).*PARAM1=value2 PARAM2=value2`},
1314
// Cannot invoke myke subcommand in a test
1415
// {Arg:`file`, Out:`(?s)I am a template.*PARAM1=value1.*PARAM2=value2`},
1516
}

examples/util/test_util.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package util
44

55
import (
66
"bytes"
7+
"fmt"
78
"github.com/goeuro/myke/cmd"
89
"github.com/stretchr/testify/assert"
910
"io"
@@ -36,7 +37,10 @@ func runTest(t *testing.T, tt TestTable) {
3637
return cmd.Exec(args)
3738
})
3839

39-
if tt.Err == (err != nil) && assert.Regexp(t, tt.Out, actual) {
40+
expectedOut := strings.Replace(tt.Out, "$PS$", fmt.Sprintf("\\%c", os.PathSeparator), -1)
41+
expectedOut = strings.Replace(expectedOut, "$PLS$", fmt.Sprintf("\\%c", os.PathListSeparator), -1)
42+
43+
if tt.Err == (err != nil) && assert.Regexp(t, expectedOut, actual) {
4044
t.Logf("myke(%s): passed", tt.Arg)
4145
} else {
4246
t.Errorf("myke(%s): failed %s", tt.Arg, err)

0 commit comments

Comments
 (0)