Skip to content

Commit 9cd6f8f

Browse files
authored
Merge pull request #153 from phase2/develop
Release 2.1.3
2 parents 09ff3c7 + 9bca3aa commit 9cd6f8f

File tree

13 files changed

+280
-61
lines changed

13 files changed

+280
-61
lines changed

.travis.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ os: linux
44

55
language: go
66
go:
7-
- 1.9
7+
- "1.10"
88
- tip
99

1010
matrix:
@@ -13,14 +13,14 @@ matrix:
1313
- go: tip
1414

1515
install:
16-
- "go get -u github.com/golang/dep/..."
16+
- "curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh"
1717
- "go get -u github.com/alecthomas/gometalinter"
1818
- "gometalinter --install --update"
1919
- "dep ensure"
2020

2121
script:
2222
- "scripts/test-go-fmt.sh"
23-
- "gometalinter --vendor --config=gometalinter.json ./..."
23+
- "gometalinter --vendor --deadline=60s --config=gometalinter.json ./..."
2424
- "go run cmd/main.go"
2525

2626
notifications:

CONTRIBUTING.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ Here are a few conventions:
4949
return cmd.Failure(message)
5050
```
5151

52+
## Developer Testing Commands
53+
54+
You can use `rig dev:win` or `rig dev:fail` as no-op commands to observe the
55+
effects of a success or failure without external dependencies on the local
56+
environment or side effects from "real" commands doing their job.
57+
5258
## Development Environment Setup
5359

5460
### Developing with Docker

Dockerfile

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
FROM golang:1.9-alpine
1+
FROM golang:1.10-alpine
22

33
RUN apk add --no-cache \
44
ca-certificates \
5+
curl \
56
git \
67
gcc \
78
libffi-dev \
@@ -12,8 +13,8 @@ RUN apk add --no-cache \
1213
ruby-dev \
1314
tar
1415

15-
RUN go get -u github.com/golang/dep/... \
16-
&& go get -u github.com/alecthomas/gometalinter \
16+
RUN curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
17+
RUN go get -u github.com/alecthomas/gometalinter \
1718
&& go get -u github.com/goreleaser/goreleaser
1819

1920
RUN gometalinter --install --update

Gopkg.lock

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ func main() {
3636
Usage: "Disable all desktop notifications",
3737
EnvVar: "RIG_NOTIFY_QUIET",
3838
},
39+
cli.BoolFlag{
40+
Name: "power-user",
41+
Usage: "Switch power-user mode on for quieter help output.",
42+
EnvVar: "RIG_POWER_USER_MODE",
43+
},
3944
}
4045

4146
app.Before = func(c *cli.Context) error {
@@ -60,6 +65,7 @@ func main() {
6065
app.Commands = append(app.Commands, (&commands.Remove{}).Commands()...)
6166
app.Commands = append(app.Commands, (&commands.Project{}).Commands()...)
6267
app.Commands = append(app.Commands, (&commands.Doctor{}).Commands()...)
68+
app.Commands = append(app.Commands, (&commands.Dev{}).Commands()...)
6369

6470
app.Run(os.Args)
6571
}

commands/command.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,34 @@ func (cmd *BaseCommand) Before(c *cli.Context) error {
3939

4040
// Success encapsulates the functionality for reporting command success
4141
func (cmd *BaseCommand) Success(message string) error {
42-
// Handle success messaging.
42+
// Handle success messaging. If the spinner is running or not, this will
43+
// output accordingly and issue a notification.
4344
if message != "" {
4445
cmd.out.Info(message)
4546
util.NotifySuccess(cmd.context, message)
47+
} else {
48+
// If there is an active spinner wrap it up. This is not placed before the
49+
// logging above so commands can rely on cmd.Success to set the last spinner
50+
// status in lieu of an extraneous log entry.
51+
cmd.out.NoSpin()
4652
}
4753

48-
// If there is an active spinner wrap it up. This is not placed before the logging above so commands can rely on
49-
// cmd.Success to set the last spinner status in lieu of an extraneous log entry.
50-
cmd.out.NoSpin()
51-
5254
return nil
5355
}
5456

5557
// Failure encapsulates the functionality for reporting command failure
5658
func (cmd *BaseCommand) Failure(message string, errorName string, exitCode int) error {
57-
// Make sure any running spinner halts.
58-
cmd.out.NoSpin()
59+
// If the spinner is running, output something to get closure and shut it down.
60+
if cmd.out.Spinning {
61+
cmd.out.Error(message)
62+
}
63+
5964
// Handle error messaging.
6065
util.NotifyError(cmd.context, message)
61-
66+
// Print expanded troubleshooting guidance.
67+
if !cmd.context.GlobalBool("power-user") {
68+
util.PrintDebugHelp(message, errorName, exitCode)
69+
}
6270
return cli.NewExitError(fmt.Sprintf("ERROR: %s [%s] (%d)", message, errorName, exitCode), exitCode)
6371
}
6472

commands/dev.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package commands
2+
3+
import (
4+
"time"
5+
6+
"github.com/urfave/cli"
7+
)
8+
9+
// Dev is the command for setting docker config to talk to a Docker Machine
10+
type Dev struct {
11+
BaseCommand
12+
}
13+
14+
// Commands returns the operations supported by this command
15+
func (cmd *Dev) Commands() []cli.Command {
16+
return []cli.Command{
17+
{
18+
Name: "dev:win",
19+
Usage: "A no-op command that will always succeed.",
20+
Before: cmd.Before,
21+
Action: cmd.RunSucceed,
22+
Hidden: true,
23+
},
24+
{
25+
Name: "dev:fail",
26+
Usage: "A no-op command that will always fail.",
27+
Before: cmd.Before,
28+
Action: cmd.RunFail,
29+
Hidden: true,
30+
},
31+
}
32+
}
33+
34+
// RunSucceed executes the `rig dev:succeed` command
35+
func (cmd *Dev) RunSucceed(c *cli.Context) error {
36+
cmd.out.Spin("Think positive...")
37+
time.Sleep(3 * time.Second)
38+
cmd.out.Info("We've got it.")
39+
return cmd.Success("Positively successful!")
40+
}
41+
42+
// RunFail executes the `rig dev:fail` command
43+
func (cmd *Dev) RunFail(c *cli.Context) error {
44+
cmd.out.Spin("Abandon all hope...")
45+
time.Sleep(3 * time.Second)
46+
cmd.out.Warning("Hope slipping...")
47+
cmd.out.Spin("Is the sky painted black?")
48+
time.Sleep(3 * time.Second)
49+
return cmd.Failure("Hope abandoned :(", "ABANDON-HOPE", 418)
50+
}

commands/project_sync.go

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ func (cmd *ProjectSync) Commands() []cli.Command {
4949
Flags: []cli.Flag{
5050
cli.IntFlag{
5151
Name: "initial-sync-timeout",
52-
Value: 60,
53-
Usage: "Maximum amount of time in seconds to allow for detecting each of start of the Unison container and start of initial sync. (not needed on linux)",
52+
Value: 120,
53+
Usage: "Maximum amount of time in seconds to allow for detecting each of start of the Unison container and start of initial sync. If you encounter failures detecting initial sync increasing this value may help. Search for sync on http://docs.outrigger.sh/faq/troubleshooting/ (not needed on linux)",
5454
EnvVar: "RIG_PROJECT_SYNC_TIMEOUT",
5555
},
5656
// Arbitrary sleep length but anything less than 3 wasn't catching
@@ -119,20 +119,22 @@ func (cmd *ProjectSync) RunStart(ctx *cli.Context) error {
119119

120120
// StartUnisonSync will create and launch the volumes and containers on systems that need/support Unison
121121
func (cmd *ProjectSync) StartUnisonSync(ctx *cli.Context, volumeName string, config *ProjectConfig, workingDir string) error {
122+
cmd.out.Spin("Starting Outrigger Filesync (unison)...")
123+
122124
// Ensure the processes can handle a large number of watches
123125
if err := cmd.machine.SetSysctl("fs.inotify.max_user_watches", maxWatches); err != nil {
124126
cmd.Failure(fmt.Sprintf("Failure configuring file watches on Docker Machine: %v", err), "INOTIFY-WATCH-FAILURE", 12)
125127
}
126128

127-
cmd.out.Channel.Info.Printf("Starting sync volume: %s", volumeName)
129+
cmd.out.SpinWithVerbose("Starting sync volume: %s", volumeName)
128130
if err := util.Command("docker", "volume", "create", volumeName).Run(); err != nil {
129131
return cmd.Failure(fmt.Sprintf("Failed to create sync volume: %s", volumeName), "VOLUME-CREATE-FAILED", 13)
130132
}
131-
132-
cmd.out.Info("Starting Unison container")
133+
cmd.out.Info("Sync volume '%s' created", volumeName)
134+
cmd.out.SpinWithVerbose(fmt.Sprintf("Starting sync container: %s (same name)", volumeName))
133135
unisonMinorVersion := cmd.GetUnisonMinorVersion()
134136

135-
cmd.out.Channel.Verbose.Printf("Local Unison version for compatibilty: %s", unisonMinorVersion)
137+
cmd.out.Verbose("Local Unison version for compatibilty: %s", unisonMinorVersion)
136138
util.Command("docker", "container", "stop", volumeName).Run()
137139
containerArgs := []string{
138140
"container", "run", "--detach", "--rm",
@@ -151,8 +153,8 @@ func (cmd *ProjectSync) StartUnisonSync(ctx *cli.Context, volumeName string, con
151153
if err != nil {
152154
return cmd.Failure(err.Error(), "SYNC-INIT-FAILED", 13)
153155
}
154-
155-
cmd.out.Info("Initializing sync")
156+
cmd.out.Info("Sync container '%s' started", volumeName)
157+
cmd.out.SpinWithVerbose("Initializing file sync...")
156158

157159
// Determine the location of the local Unison log file.
158160
var logFile = fmt.Sprintf("%s.log", volumeName)
@@ -179,7 +181,7 @@ func (cmd *ProjectSync) StartUnisonSync(ctx *cli.Context, volumeName string, con
179181
unisonArgs = append(unisonArgs, "-ignore", ignore)
180182
}
181183
}
182-
cmd.out.Verbose("Unison Args: %s", strings.Join(unisonArgs[:], " "))
184+
183185
/* #nosec */
184186
command := exec.Command("unison", unisonArgs...)
185187
command.Dir = workingDir
@@ -192,12 +194,14 @@ func (cmd *ProjectSync) StartUnisonSync(ctx *cli.Context, volumeName string, con
192194
return cmd.Failure(err.Error(), "UNISON-SYNC-FAILED", 13)
193195
}
194196

197+
cmd.out.Info("Watch unison process activities in the sync log: %s", logFile)
198+
195199
return cmd.Success("Unison sync started successfully")
196200
}
197201

198202
// SetupBindVolume will create minimal Docker Volumes for systems that have native container/volume support
199203
func (cmd *ProjectSync) SetupBindVolume(volumeName string, workingDir string) error {
200-
cmd.out.Info("Starting local bind volume: %s", volumeName)
204+
cmd.out.SpinWithVerbose("Starting local bind volume: %s", volumeName)
201205
util.Command("docker", "volume", "rm", volumeName).Run()
202206

203207
volumeArgs := []string{
@@ -220,6 +224,7 @@ func (cmd *ProjectSync) RunStop(ctx *cli.Context) error {
220224
if runtime.GOOS == "linux" {
221225
return cmd.Success("No Unison container to stop, using local bind volume")
222226
}
227+
cmd.out.Spin(fmt.Sprintf("Stopping Unison container"))
223228

224229
cmd.Config = NewProjectConfig()
225230
if cmd.Config.NotEmpty() {
@@ -238,7 +243,7 @@ func (cmd *ProjectSync) RunStop(ctx *cli.Context) error {
238243
return cmd.Failure(err.Error(), "SYNC-CONTAINER-FAILURE", 13)
239244
}
240245

241-
return cmd.Success(fmt.Sprintf("Unison container %s stopped", volumeName))
246+
return cmd.Success(fmt.Sprintf("Unison container '%s' stopped", volumeName))
242247
}
243248

244249
// GetVolumeName will find the volume name through a variety of fall backs
@@ -283,7 +288,7 @@ func (cmd *ProjectSync) LoadComposeFile() (*ComposeFile, error) {
283288
// when compiled without -cgo this executable will not use the native mac dns resolution
284289
// which is how we have configured dnsdock to provide names for containers.
285290
func (cmd *ProjectSync) WaitForUnisonContainer(containerName string, timeoutSeconds int) (string, error) {
286-
cmd.out.Info("Waiting for container to start")
291+
cmd.out.SpinWithVerbose("Sync container '%s' started , waiting for unison server process...", containerName)
287292

288293
var timeoutLoopSleep = time.Duration(100) * time.Millisecond
289294
// * 10 here because we loop once every 100 ms and we want to get to seconds
@@ -303,7 +308,7 @@ func (cmd *ProjectSync) WaitForUnisonContainer(containerName string, timeoutSeco
303308
return ip, nil
304309
}
305310

306-
cmd.out.Info("Failure: %v", err)
311+
cmd.out.SpinWithVerbose("Failure: %v", err)
307312
time.Sleep(timeoutLoopSleep)
308313
}
309314

@@ -313,12 +318,12 @@ func (cmd *ProjectSync) WaitForUnisonContainer(containerName string, timeoutSeco
313318
// WaitForSyncInit will wait for the local unison process to finish initializing
314319
// when the log file exists and has stopped growing in size
315320
func (cmd *ProjectSync) WaitForSyncInit(logFile string, workingDir string, timeoutSeconds int, syncWaitSeconds int) error {
316-
cmd.out.Info("Waiting for initial sync detection")
321+
cmd.out.SpinWithVerbose("Waiting for initial sync detection...")
317322

318323
// The use of os.Stat below is not subject to our working directory configuration,
319324
// so to ensure we can stat the log file we convert it to an absolute path.
320325
if logFilePath, err := util.AbsJoin(workingDir, logFile); err != nil {
321-
cmd.out.Info(err.Error())
326+
cmd.out.Error(err.Error())
322327
} else {
323328
// Create a temp file to cause a sync action
324329
var tempFile = ".rig-check-sync-start"
@@ -333,32 +338,29 @@ func (cmd *ProjectSync) WaitForSyncInit(logFile string, workingDir string, timeo
333338
var timeoutLoops = timeoutSeconds * 10
334339
var statSleep = time.Duration(syncWaitSeconds) * time.Second
335340
for i := 1; i <= timeoutLoops; i++ {
336-
if i%10 == 0 {
337-
os.Stdout.WriteString(".")
338-
}
339-
340341
statInfo, err := os.Stat(logFilePath)
341342
if err == nil {
342-
os.Stdout.WriteString(" initial sync detected\n")
343+
cmd.out.Info("Initial sync detected")
343344

344-
cmd.out.Info("Waiting for initial sync to finish")
345+
cmd.out.SpinWithVerbose("Waiting for initial sync to finish")
345346
// Initialize at -2 to force at least one loop
346347
var lastSize = int64(-2)
347348
for lastSize != statInfo.Size() {
348-
os.Stdout.WriteString(".")
349349
time.Sleep(statSleep)
350350
lastSize = statInfo.Size()
351351
if statInfo, err = os.Stat(logFilePath); err != nil {
352-
cmd.out.Info(err.Error())
352+
cmd.out.Error(err.Error())
353353
lastSize = -1
354354
}
355355
}
356-
os.Stdout.WriteString(" done\n")
356+
357357
// Remove the temp file, waiting until after sync so spurious
358358
// failure message doesn't show in log
359359
if err := util.RemoveFile(tempFile, workingDir); err != nil {
360360
cmd.out.Warning("Could not remove the temporary file: %s: %s", tempFile, err.Error())
361361
}
362+
363+
cmd.out.Info("File sync completed")
362364
return nil
363365
}
364366

@@ -373,6 +375,7 @@ func (cmd *ProjectSync) WaitForSyncInit(logFile string, workingDir string, timeo
373375
}
374376
}
375377

378+
cmd.out.Error("Initial sync detection failed, this could indicate a need to increase the initial-sync-timeout. See rig project sync --help")
376379
return fmt.Errorf("Failed to detect start of initial sync")
377380
}
378381

0 commit comments

Comments
 (0)