Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port to Windows #216

Merged
merged 8 commits into from Oct 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
60 changes: 52 additions & 8 deletions .circleci/config.yml
@@ -1,8 +1,12 @@
# Golang CircleCI 2.0 configuration file
# Check https://circleci.com/docs/2.0/language-go/ for more details
version: 2
version: 2.1

orbs:
win: circleci/windows@2.2.0

jobs:
test:
test_linux:
docker:
- image: circleci/golang:1
environment:
Expand Down Expand Up @@ -48,7 +52,46 @@ jobs:
- store_test_results:
path: /tmp/test-results

build:
test_windows:
executor: win/default

environment:
GOFLAGS: -mod=vendor
TEST_RESULTS: ~/test-results
BAUR_TEST_POSTGRESQL_URL: "postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable"

working_directory: ~/baur

steps:
- checkout

- run:
name: Preparing Test Environment
command: |
choco install golang --version=1.15.2 -y
choco install postgresql12 --params '/Password:postgres' -y
New-Item -ItemType Directory -Force -Path $Env:TEST_RESULTS

- run:
name: Run Tests
command: |
$go111module = $Env:GO111MODULE
$Env:GO111MODULE="off"
go get github.com/jstemmer/go-junit-report
$Env:GO111MODULE=$go111module
git config --global user.email "circleci-baurtest@example.com"
git config --global user.name "baur"
$testResultDir = Resolve-Path $Env:TEST_RESULTS
$testOutputPath = Join-Path $testResultDir "go-test.out"
$testReportPath = Join-Path $testResultDir "go-test-report.xml"
try { go test --tags=dbtest -v -timeout 5m .\... | Tee-Object -FilePath $testOutputPath } `
finally { Get-Content -Path $testOutputPath | go-junit-report > $testReportPath; `
[System.Io.File]::ReadAllText($testReportPath) | Out-File -FilePath $testReportPath -Encoding utf8 }

- store_test_results:
path: ~\test-results

build_linux:
docker:
- image: circleci/golang:1

Expand All @@ -60,7 +103,7 @@ jobs:
name: Building baur
command: make baur

static_analysis:
static_analysis_linux:
docker:
- image: golangci/golangci-lint:v1.31.0

Expand All @@ -73,9 +116,10 @@ jobs:
command: golangci-lint run

workflows:
version: 2
version: 2.1
workflow:
jobs:
- build
- test
- static_analysis
- build_linux
- test_linux
- test_windows
- static_analysis_linux
7 changes: 6 additions & 1 deletion Makefile
Expand Up @@ -42,6 +42,11 @@ dist/linux_amd64/baur:
$(info * creating $(@D)/baur-linux_amd64-$(VERSION).tar.xz.sha256)
@(cd $(@D) && sha256sum baur-linux_amd64-$(VERSION).tar.xz > baur-linux_amd64-$(VERSION).tar.xz.sha256)

.PHONY: dist/windows_amd64/baur.exe
dist/windows_amd64/baur.exe:
$(info * building $@)
@CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build \
$(BUILDFLAGS) -o "$@" cmd/baur/main.go

.PHONY: dirty_worktree_check
dirty_worktree_check:
Expand All @@ -51,7 +56,7 @@ dirty_worktree_check:
fi

.PHONY: release
release: clean dirty_worktree_check dist/linux_amd64/baur dist/darwin_amd64/baur
release: clean dirty_worktree_check dist/linux_amd64/baur dist/darwin_amd64/baur dist/windows_amd64/baur.exe
@echo
@echo next steps:
@echo - git tag v$(VERSION)
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Expand Up @@ -29,3 +29,5 @@ require (
google.golang.org/protobuf v1.25.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
)

replace golang.org/x/sys => golang.org/x/sys v0.0.0-20190830141801-acfa387b8d69
29 changes: 2 additions & 27 deletions go.sum
Expand Up @@ -436,33 +436,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1cHUZgO1Ebq5r2hIjfo=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200926100807-9d91bd62050c h1:38q6VNPWR010vN82/SB121GujZNIfAUb4YttE2rhGuc=
golang.org/x/sys v0.0.0-20200926100807-9d91bd62050c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190830141801-acfa387b8d69 h1:Wdn4Yb8d5VrsO3jWgaeSZss09x1VLVBMePDh4VW/xSQ=
golang.org/x/sys v0.0.0-20190830141801-acfa387b8d69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
Expand Down
3 changes: 2 additions & 1 deletion internal/command/diff_inputs_test.go
Expand Up @@ -5,6 +5,7 @@ package command
import (
"encoding/csv"
"fmt"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -392,7 +393,7 @@ func TestDifferencesOutputWithCorrectState(t *testing.T) {
executeWithoutError(t, diffInputsCmd)

expectedOutput := [][]string{
{"D", "app_one/diff_test.txt", originalDigest.String(), newDigest.String()},
{"D", filepath.FromSlash("app_one/diff_test.txt"), originalDigest.String(), newDigest.String()},
{"-", "string:run_one", "sha384:95e52b4c9863a13d596d34df980988cb78bea9ec3381ba981e1656a84cc1c7456f6830bca0e8931be5f0f48593cb5d06", ""},
{"+", "string:run_two", "", "sha384:f3d5e46502641c5591563a0d3157f19a9739616f07bdb4bbc0285cb0a12bd511c026db94f12c719378a20d0ffe85090e"},
}
Expand Down
2 changes: 1 addition & 1 deletion internal/command/testdata/var_in_include/tasks.toml
Expand Up @@ -2,4 +2,4 @@
include_id = "build"
includes = ["$ROOT/inputs.toml#input"]
name = "build"
command = ["ls", "$APPNAME.txt"]
command = ["more", "$APPNAME.txt"]
18 changes: 16 additions & 2 deletions internal/command/upgrade_configs_test.go
Expand Up @@ -3,6 +3,7 @@ package command
import (
"os"
"path/filepath"
"runtime"
"strings"
"testing"

Expand All @@ -21,9 +22,18 @@ func TestUpgrade(t *testing.T) {

initTest(t)

tempdir := t.TempDir()
// When running on Windows we need to change the working directory back to the original
// working directory so the temporary directory that is used can be deleted
var originalDir string
var err error
if runtime.GOOS == "windows" {
originalDir, err = os.Getwd()
assert.NoError(t, err)
}

gitDir := filepath.Join(tempdir, "git")
tempDir := t.TempDir()

gitDir := filepath.Join(tempDir, "git")

require.NoError(t, os.Chdir("/"))
gittest.Clone(t, gitDir, gitURL, commit)
Expand Down Expand Up @@ -57,4 +67,8 @@ func TestUpgrade(t *testing.T) {
assert.Contains(t, taskIDs, "myredis.build")
assert.Contains(t, taskIDs, "random.build")
assert.Contains(t, taskIDs, "unixtime.build")

if runtime.GOOS == "windows" {
require.NoError(t, os.Chdir(originalDir))
}
}
2 changes: 2 additions & 0 deletions internal/digest/sha384/sha384_test.go
Expand Up @@ -126,6 +126,8 @@ func TestHashingNonExistingFileFails(t *testing.T) {
if err != nil {
t.Fatal("creating tempfile failed:", err.Error())
}
// The file must be closed before it can be deleted on Windows
file.Close()
os.Remove(file.Name())

sha := sha384.New()
Expand Down
39 changes: 34 additions & 5 deletions internal/exec/exec_test.go
@@ -1,13 +1,30 @@
package exec

import (
"fmt"
"runtime"
"testing"
)

func TestEchoStdout(t *testing.T) {
const echoStr = "hello world!"

res, err := Command("echo", "-n", echoStr).Run()
// Windows returns StrOutput with surrounding quotation marks
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The windows echo command prints the string quoted?
Or all output that is returned from running commands via the stdlib exec package is quoted?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have investigated this further and the results are quite interesting.
I created a simple app that runs a few different commands with a couple of different inputs, I then ran this on Windows and Linux.
Here are the results:
Running on Windows
image
Running on Linux
image

Summary:

  • cmd adds quotation marks around the string if it contains spaces
  • PowerShell wraps the output onto a new line at the space if it contains spaces
  • more on Linux outputs headers and footers, Windows doesn't

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so the echo command is probably also a shell builtin command like for bash and the implementation of it in cmd and PowerShell differ.
When commands are invoked without a shell, the output that is returned by the exec package should not be quoted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is my understanding.
Windows doesn't have an echo executable so it must be invoked from a shell. However it does have a more executable and when it is invoked the file contents are not quoted.

var expected string
if runtime.GOOS == "windows" {
expected = fmt.Sprintf("\"%s\"", echoStr)
} else {
expected = echoStr
}

var res *Result
var err error
if runtime.GOOS == "windows" {
res, err = Command("cmd", "/C", "echo", echoStr).Run()
} else {
res, err = Command("echo", "-n", echoStr).Run()
}

if err != nil {
t.Fatal(err)
}
Expand All @@ -16,13 +33,19 @@ func TestEchoStdout(t *testing.T) {
t.Fatalf("cmd exited with code %d, expected 0", res.ExitCode)
}

if res.StrOutput() != echoStr {
t.Errorf("expected output '%s', got '%s'", echoStr, res.StrOutput())
if res.StrOutput() != expected {
t.Errorf("expected output '%s', got '%s'", expected, res.StrOutput())
}
}

func TestCommandFails(t *testing.T) {
res, err := Command("false").Run()
var res *Result
var err error
if runtime.GOOS == "windows" {
res, err = Command("cmd", "/C", "exit", "1").Run()
} else {
res, err = Command("false").Run()
}
if err != nil {
t.Fatal(err)
}
Expand All @@ -37,7 +60,13 @@ func TestCommandFails(t *testing.T) {
}

func TestExpectSuccess(t *testing.T) {
res, err := Command("false").ExpectSuccess().Run()
var res *Result
var err error
if runtime.GOOS == "windows" {
res, err = Command("cmd", "/C", "exit", "1").ExpectSuccess().Run()
} else {
res, err = Command("false").ExpectSuccess().Run()
}
if err == nil {
t.Fatal("Command did not return an error")
}
Expand Down
5 changes: 5 additions & 0 deletions internal/fs/fileglob_test.go
Expand Up @@ -172,6 +172,11 @@ func Test_Resolve(t *testing.T) {
for _, tc := range testcases {
tempdir := t.TempDir()

// The path separators in the test cases are Unix style "/", they need to be converted to "\" when running on Windows
for i := range tc.expectedMatches {
tc.expectedMatches[i] = filepath.FromSlash(tc.expectedMatches[i])
}

if len(tc.dir) != 0 {
err := os.MkdirAll(filepath.Join(tempdir, tc.dir), os.ModePerm)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions internal/resolve/gosource/gosource_test.go
Expand Up @@ -56,6 +56,8 @@ func TestResolve(t *testing.T) {

for i := range testCfg.ExpectedResults {
testCfg.ExpectedResults[i] = strings.Replace(testCfg.ExpectedResults[i], "$WORKDIR", cwd, -1)
// The path separators in the test config are Unix style "/", they need to be converted to "\" when running on Windows
testCfg.ExpectedResults[i] = filepath.FromSlash(testCfg.ExpectedResults[i])
}

resolvedFiles, err := NewResolver(t.Logf).Resolve(
Expand Down