{"action":"created","comment":{"url":"https://api.github.com/repos/opentable/sous/pulls/comments/132057730","pull_request_review_id":55099340,"id":132057730,"diff_hunk":"@@ -1,323 +1,164 @@\n package shell\n \n import (\n-\t\"bytes\"\n-\t\"encoding/json\"\n-\t\"fmt\"\n-\t\"io\"\n \t\"os\"\n-\t\"strings\"\n \n-\t\"github.com/opentable/sous/util/whitespace\"\n-\n-\t\"golang.org/x/tools/godoc/vfs\"\n-\t\"golang.org/x/tools/godoc/vfs/mapfs\"\n+\t\"github.com/opentable/sous/util/spies\"\n+\t\"github.com/stretchr/testify/mock\"\n )\n \n type (\n \t// TestShell is a test instance for Sh\n \tTestShell struct {\n-\t\tSh\n-\t\tCmdsF   cmdGenerator\n-\t\tFS      vfs.FileSystem\n-\t\tHistory []*DummyCommand\n-\t}\n-\n-\tcmdGenerator func(name string, args []interface{}) *DummyResult\n-\n-\t// DummyCommand is a test wrapper for Command\n-\tDummyCommand struct {\n-\t\tsiStr *string\n-\t\tCommand\n-\t\tDummyResult\n+\t\t*spies.Spy\n+\t\t/*\n+\t\t\tSh\n+\t\t\tCmdsF   cmdGenerator\n+\t\t\tFS      vfs.FileSystem\n+\t\t\tHistory []*DummyCommand\n+\t\t*/\n \t}\n \n-\t// DummyResult describes the dummy results of a dummy command\n-\tDummyResult struct {\n-\t\tSO, SE []byte\n-\t\tErr    error\n-\t\tStatus int\n+\tTestShellController struct {\n+\t\t*spies.Spy\n \t}\n )\n \n-func blissfulSuccess(n string, as []interface{}) *DummyResult {\n-\treturn nil\n+//Clone implements Shell on TestShell\n+func (s *TestShell) Clone() Shell {\n+\ts.Called()\n+\treturn s\n+}\n+\n+//Dir implements Shell on TestShell\n+func (s *TestShell) Dir() string {\n+\tres := s.Called()\n+\treturn res.String(0)\n+}\n+\n+//Abs implements Shell on TestShell\n+func (s *TestShell) Abs(path string) string {\n+\tres := s.Called(path)\n+\treturn res.String(0)\n+}\n+\n+//ConsoleEcho implements Shell on TestShell\n+func (s *TestShell) ConsoleEcho(line string) {\n+\ts.Called(line)\n+}\n+\n+//LongRunning implements Shell on TestShell\n+func (s *TestShell) LongRunning(is bool) {\n+\ts.Called(is)\n+}\n+\n+//Dir implements Shell on TestShell\n+func (s *TestShell) CD(dir string) error {\n+\tres := s.Called(dir)\n+\treturn res.Error(0)\n }\n \n // NewTestShell builds a test shell, notionally at path, with a set of files available\n // to it built from the files map. Note that relative paths in the map are\n // considered wrt the path\n-func NewTestShell(path string, files map[string]string) (*TestShell, error) {\n-\tts := TestShell{\n-\t\tCmdsF: blissfulSuccess,\n-\t\tSh: Sh{\n-\t\t\tCwd: path,\n-\t\t\tEnv: os.Environ(),\n-\t\t},\n-\t}\n-\tfs := make(map[string]string)\n-\tfor n, c := range files {\n-\t\tfs[strings.TrimPrefix(ts.Abs(n), \"/\")] = c\n-\t}\n-\tts.FS = mapfs.New(fs)\n-\treturn &ts, nil\n+func NewTestShell() (*TestShell, *TestShellController) {\n+\tspy := spies.NewSpy()\n+\treturn &TestShell{Spy: spy}, &TestShellController{Spy: spy}\n }\n \n // Cmd creates a new Command based on this shell.\n func (s *TestShell) Cmd(name string, args ...interface{}) Cmd {\n-\tsargs := make([]string, len(args))\n-\tfor i, a := range args {\n-\t\tsargs[i] = fmt.Sprint(a)\n-\t}\n-\tr := s.CmdsF(name, args)\n-\tif r == nil {\n-\t\tr = &DummyResult{\n-\t\t\tSO:     []byte{},\n-\t\t\tSE:     []byte{},\n-\t\t\tErr:    nil,\n-\t\t\tStatus: 0,\n-\t\t}\n-\t}\n-\tdc := &DummyCommand{\n-\t\tDummyResult: *r,\n-\t\tCommand: Command{\n-\t\t\tDir:  s.Dir(), // is therefore the live shell...\n-\t\t\tName: name,\n-\t\t\tArgs: sargs,\n-\t\t},\n-\t}\n-\ts.History = append(s.History, dc)\n-\treturn dc\n+\tres := s.Called(name, args)\n+\tcmd, _ := NewTestCommand()\n+\treturn res.GetOr(0, cmd).(Cmd)\n }\n \n // List returns all files (including dotfiles) inside Dir.\n func (s *TestShell) List() ([]os.FileInfo, error) {\n-\tios, err := s.FS.ReadDir(s.Dir())\n-\tif err != nil {\n-\t\treturn nil, err\n-\t}\n-\tfor i := range ios {\n-\t\tif ios[i].Name() == \"__exists__\" {\n-\t\t\tios[i] = ios[len(ios)-1]\n-\t\t\treturn ios[:len(ios)-1], nil\n-\t\t}\n-\t}\n-\n-\treturn ios, err\n+\tres := s.Called()\n+\treturn res.GetOr(0, []os.FileInfo{}).([]os.FileInfo), res.Error(1)\n }\n \n // Exists returns true if the path definitely exists. It swallows\n // any errors and returns false, in the case that e.g. permissions\n // prevent the check from working correctly.\n func (s *TestShell) Exists(path string) bool {\n-\t_, err := s.Stat(path)\n-\treturn err == nil\n+\tres := s.Called(path)\n+\treturn res.Bool(0)\n }\n \n // Stat calls os.Stat on the path provided, relative to the current\n // shell's working directory.\n func (s *TestShell) Stat(path string) (os.FileInfo, error) {\n-\treturn s.FS.Stat(s.Abs(path))\n-}\n-\n-// SetStdin sets the stdin on the command\n-func (c *DummyCommand) SetStdin(in io.Reader) {\n-\tc.Stdin = in\n-}\n-\n-// StdinString returns the Stdin provided for this command as a string\n-func (c *DummyCommand) StdinString() string {\n-\tif c.Stdin == nil {\n-\t\treturn \"\"\n-\t}\n-\tif c.siStr != nil {\n-\t\treturn *c.siStr\n-\t}\n-\tbf := &bytes.Buffer{}\n-\tio.Copy(bf, c.Stdin)\n-\ts := bf.String()\n-\tc.siStr = &s\n-\treturn *c.siStr\n-}\n-\n-// Stdout returns the stdout stream as a string. It returns an error for the\n-// same reasons as Succeed.\n-func (c *DummyCommand) Stdout() (string, error) {\n-\tr, err := c.SucceedResult()\n-\tif err != nil {\n-\t\treturn \"\", err\n-\t}\n-\treturn r.Stdout.String(), nil\n-}\n-\n-// Stderr is returns the stderr stream as a string. It returns an error for the\n-// same reasons as Result.\n-func (c *DummyCommand) Stderr() (string, error) {\n-\tr, err := c.Result()\n-\tif err != nil {\n-\t\treturn \"\", err\n-\t}\n-\treturn r.Stderr.String(), nil\n-}\n-\n-// Lines returns Stdout split by newline. Leading and trailing empty lines are\n-// removed, and each line is trimmed of whitespace.\n-func (c *DummyCommand) Lines() ([]string, error) {\n-\tstdout, err := c.Stdout()\n-\tif err != nil {\n-\t\treturn nil, err\n-\t}\n-\trawLines := strings.Split(stdout, \"\\n\")\n-\tlines := []string{}\n-\tfor _, l := range rawLines {\n-\t\ttrimmed := whitespace.Trim(l)\n-\t\tif len(trimmed) == 0 {\n-\t\t\tcontinue\n-\t\t}\n-\t\tlines = append(lines, trimmed)\n-\t}\n-\treturn lines, nil\n-}\n-\n-// Table is similar to Lines, except lines are further split by whitespace.\n-func (c *DummyCommand) Table() ([][]string, error) {\n-\tr, err := c.SucceedResult()\n-\tif err != nil {\n-\t\treturn nil, err\n-\t}\n-\treturn r.Stdout.Table(), nil\n-}\n-\n-// JSON tries to parse the stdout from the command as JSON, populating the\n-// value you pass. (This value should be a pointer.)\n-func (c *DummyCommand) JSON(v interface{}) error {\n-\tr, err := c.Result()\n-\tif err != nil {\n-\t\treturn err\n-\t}\n-\tdecoder := json.NewDecoder(r.Stdout.Reader())\n-\treturn decoder.Decode(v)\n-}\n-\n-// ExitCode only returns an error if there were io issues starting the command,\n-// it does not return an error for a command which fails and returns an error\n-// code, which is unlike most of Sh's methods. If it returns an error, then it\n-// also returns -1 for the exit code.\n-func (c *DummyCommand) ExitCode() (int, error) {\n-\tr, err := c.Result()\n-\tif err != nil {\n-\t\treturn -1, err\n-\t}\n-\treturn r.ExitCode, nil\n-}\n-\n-// Result attempts to simulate the running of a command by filling the\n-// appropriate buffers from its Test fields and forwarding its Test exit codes\n-func (c *DummyCommand) Result() (*Result, error) {\n-\t//command := exec.Command(c.Name, c.Args...)\n-\toutbuf := &bytes.Buffer{}\n-\terrbuf := &bytes.Buffer{}\n-\tcombinedbuf := &bytes.Buffer{}\n-\toutWriters := []io.Writer{outbuf, combinedbuf}\n-\terrWriters := []io.Writer{errbuf, combinedbuf}\n-\tif c.Command.TeeOut != nil {\n-\t\toutWriters = append(outWriters, c.TeeOut)\n-\t}\n-\tif c.Command.TeeErr != nil {\n-\t\terrWriters = append(errWriters, c.TeeErr)\n-\t}\n-\n-\toutWr := io.MultiWriter(outWriters...)\n-\terrWr := io.MultiWriter(errWriters...)\n-\n-\toutWr.Write(c.DummyResult.SO)\n-\terrWr.Write(c.DummyResult.SE)\n-\n-\treturn &Result{\n-\t\tCommand:  &c.Command, // XXX thus the live command...\n-\t\tStdout:   &Output{outbuf},\n-\t\tStderr:   &Output{errbuf},\n-\t\tCombined: &Output{combinedbuf},\n-\t\tErr:      c.DummyResult.Err,\n-\t\tExitCode: c.DummyResult.Status,\n-\t}, nil\n-}\n-\n-// SucceedResult is similar to Result, except that it also returns an error if\n-// the command itself fails (returns a non-zero exit code).\n-func (c *DummyCommand) SucceedResult() (*Result, error) {\n-\tr, err := c.Result()\n-\tif err != nil {\n-\t\treturn r, err\n-\t}\n-\tif r.Err != nil {\n-\t\treturn r, newError(r.Err, r)\n-\t}\n-\treturn r, nil\n-}\n-\n-// Succeed returns an error if the command fails for any reason (fails to start\n-// or finishes with a non-zero exist code).\n-func (c *DummyCommand) Succeed() error {\n-\t_, err := c.SucceedResult()\n-\treturn err\n-}\n-\n-// Fail returns an error if the command succeeds to execute, or if it fails to\n-// start. It returns nil only if the command starts successfully and then exits\n-// with a non-zero exit code.\n-func (c *DummyCommand) Fail() error {\n-\t_, err := c.FailResult()\n-\treturn err\n-}\n-\n-// FailResult returns an error when the command fails to be invoked at all, or\n-// when the command is successfully invoked, and then runs successfully. It\n-// does not return an error when the command is invoked successfully and then\n-// fails.\n-func (c *DummyCommand) FailResult() (*Result, error) {\n-\tr, err := c.Result()\n-\tif err != nil {\n-\t\treturn r, err\n-\t}\n-\tif r.Err == nil {\n-\t\treturn r, fmt.Errorf(\"command %q succeeded, expected failure\", c)\n-\t}\n-\treturn r, nil\n-}\n-\n-func (c *DummyCommand) String() string {\n-\targs := strings.Join(c.Command.Args, \" \")\n-\t// using shell comment token\n-\treturn fmt.Sprintf(\"%s %s # the dummy version\", c.Command.Name, args)\n+\tres := s.Called(path)\n+\treturn res.GetOr(0, nil).(os.FileInfo), res.Error(1)\n }\n \n // Run (...) is a shortcut for shell.Cmd(...).Succeed()\n func (s *TestShell) Run(name string, args ...interface{}) error {\n+\ts.Called(name, args)\n \treturn s.Cmd(name, args...).Succeed()\n }\n \n // Stdout (...) is a shortcut for shell.Cmd(...).Stdout()\n func (s *TestShell) Stdout(name string, args ...interface{}) (string, error) {\n+\ts.Called(name, args)\n \treturn s.Cmd(name, args...).Stdout()\n }\n \n // Stderr (...) is a shortcut for shell.Cmd(...).Stderr()\n func (s *TestShell) Stderr(name string, args ...interface{}) (string, error) {\n+\ts.Called(name, args)\n \treturn s.Cmd(name, args...).Stderr()\n }\n \n // ExitCode (...) is a shortcut for shell.Cmd(...).ExitCode()\n func (s *TestShell) ExitCode(name string, args ...interface{}) (int, error) {\n+\ts.Called(name, args)\n \treturn s.Cmd(name, args...).ExitCode()\n }\n \n // Lines (...) is a shortcut for shell.Cmd(...).Lines()\n func (s *TestShell) Lines(name string, args ...interface{}) ([]string, error) {\n+\ts.Called(name, args)\n \treturn s.Cmd(name, args...).Lines()\n }\n \n // JSON (x, ...) is a shortcut for shell.Cmd(...).JSON(x)\n func (s *TestShell) JSON(v interface{}, name string, args ...interface{}) error {\n+\ts.Called(name, args)\n \treturn s.Cmd(name, args...).JSON(v)\n }\n+\n+func cmdWithArgs(parts ...string) func(string, mock.Arguments) bool {\n+\treturn func(method string, args mock.Arguments) bool {\n+\t\tif method != \"Cmd\" {\n+\t\t\treturn false\n+\t\t}\n+\t\tif args.String(0) != parts[0] {\n+\t\t\treturn false\n+\t\t}\n+\n+\t\tai := args.Get(1).([]interface{})\n+\t\tfor n, part := range parts[1:] {\n+\t\t\tif n >= len(ai) {\n+\t\t\t\treturn false\n+\t\t\t}\n+\n+\t\t\tif ai[n] != part {\n+\t\t\t\treturn false\n+\t\t\t}\n+\t\t}\n+\t\treturn true\n+\t}\n+}\n+\n+func (ctl *TestShellController) CmdFor(parts ...string) (*TestCommand, *TestCommandController) {\n+\tcmd, c := NewTestCommand()\n+\tctl.Match(cmdWithArgs(parts...), cmd)\n+\treturn cmd, c\n+}\n+\n+func (ctl *TestShellController) CmdsLike(parts ...string) []spies.Call {","path":"util/shell/test_shell.go","position":415,"original_position":415,"commit_id":"cc8c07cbfa8d27f80de84542b16a18db76889c08","original_commit_id":"cc8c07cbfa8d27f80de84542b16a18db76889c08","user":{"login":"houndci-bot","id":6697940,"avatar_url":"https://avatars0.githubusercontent.com/u/6697940?v=4","gravatar_id":"","url":"https://api.github.com/users/houndci-bot","html_url":"https://github.com/houndci-bot","followers_url":"https://api.github.com/users/houndci-bot/followers","following_url":"https://api.github.com/users/houndci-bot/following{/other_user}","gists_url":"https://api.github.com/users/houndci-bot/gists{/gist_id}","starred_url":"https://api.github.com/users/houndci-bot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/houndci-bot/subscriptions","organizations_url":"https://api.github.com/users/houndci-bot/orgs","repos_url":"https://api.github.com/users/houndci-bot/repos","events_url":"https://api.github.com/users/houndci-bot/events{/privacy}","received_events_url":"https://api.github.com/users/houndci-bot/received_events","type":"User","site_admin":false},"body":"exported method TestShellController.CmdsLike should have comment or be unexported","created_at":"2017-08-08T23:23:23Z","updated_at":"2017-08-08T23:23:24Z","html_url":"https://github.com/opentable/sous/pull/444#discussion_r132057730","pull_request_url":"https://api.github.com/repos/opentable/sous/pulls/444","_links":{"self":{"href":"https://api.github.com/repos/opentable/sous/pulls/comments/132057730"},"html":{"href":"https://github.com/opentable/sous/pull/444#discussion_r132057730"},"pull_request":{"href":"https://api.github.com/repos/opentable/sous/pulls/444"}}},"pull_request":{"url":"https://api.github.com/repos/opentable/sous/pulls/444","id":134780056,"html_url":"https://github.com/opentable/sous/pull/444","diff_url":"https://github.com/opentable/sous/pull/444.diff","patch_url":"https://github.com/opentable/sous/pull/444.patch","issue_url":"https://api.github.com/repos/opentable/sous/issues/444","number":444,"state":"open","locked":false,"title":"Split build can now produce multiple deploy artifacts - on a per-offset basis.","user":{"login":"nyarly","id":127548,"avatar_url":"https://avatars2.githubusercontent.com/u/127548?v=4","gravatar_id":"","url":"https://api.github.com/users/nyarly","html_url":"https://github.com/nyarly","followers_url":"https://api.github.com/users/nyarly/followers","following_url":"https://api.github.com/users/nyarly/following{/other_user}","gists_url":"https://api.github.com/users/nyarly/gists{/gist_id}","starred_url":"https://api.github.com/users/nyarly/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nyarly/subscriptions","organizations_url":"https://api.github.com/users/nyarly/orgs","repos_url":"https://api.github.com/users/nyarly/repos","events_url":"https://api.github.com/users/nyarly/events{/privacy}","received_events_url":"https://api.github.com/users/nyarly/received_events","type":"User","site_admin":false},"body":null,"created_at":"2017-08-08T23:23:15Z","updated_at":"2017-08-08T23:23:24Z","closed_at":null,"merged_at":null,"merge_commit_sha":"0343b6277fb7122ec75c1f452e5d5d73edccf005","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/opentable/sous/pulls/444/commits","review_comments_url":"https://api.github.com/repos/opentable/sous/pulls/444/comments","review_comment_url":"https://api.github.com/repos/opentable/sous/pulls/comments{/number}","comments_url":"https://api.github.com/repos/opentable/sous/issues/444/comments","statuses_url":"https://api.github.com/repos/opentable/sous/statuses/cc8c07cbfa8d27f80de84542b16a18db76889c08","head":{"label":"opentable:multiple_targets","ref":"multiple_targets","sha":"cc8c07cbfa8d27f80de84542b16a18db76889c08","user":{"login":"opentable","id":2653541,"avatar_url":"https://avatars0.githubusercontent.com/u/2653541?v=4","gravatar_id":"","url":"https://api.github.com/users/opentable","html_url":"https://github.com/opentable","followers_url":"https://api.github.com/users/opentable/followers","following_url":"https://api.github.com/users/opentable/following{/other_user}","gists_url":"https://api.github.com/users/opentable/gists{/gist_id}","starred_url":"https://api.github.com/users/opentable/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/opentable/subscriptions","organizations_url":"https://api.github.com/users/opentable/orgs","repos_url":"https://api.github.com/users/opentable/repos","events_url":"https://api.github.com/users/opentable/events{/privacy}","received_events_url":"https://api.github.com/users/opentable/received_events","type":"Organization","site_admin":false},"repo":{"id":43084873,"name":"sous","full_name":"opentable/sous","owner":{"login":"opentable","id":2653541,"avatar_url":"https://avatars0.githubusercontent.com/u/2653541?v=4","gravatar_id":"","url":"https://api.github.com/users/opentable","html_url":"https://github.com/opentable","followers_url":"https://api.github.com/users/opentable/followers","following_url":"https://api.github.com/users/opentable/following{/other_user}","gists_url":"https://api.github.com/users/opentable/gists{/gist_id}","starred_url":"https://api.github.com/users/opentable/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/opentable/subscriptions","organizations_url":"https://api.github.com/users/opentable/orgs","repos_url":"https://api.github.com/users/opentable/repos","events_url":"https://api.github.com/users/opentable/events{/privacy}","received_events_url":"https://api.github.com/users/opentable/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/opentable/sous","description":"Tools for building, testing, deploying Docker containers","fork":false,"url":"https://api.github.com/repos/opentable/sous","forks_url":"https://api.github.com/repos/opentable/sous/forks","keys_url":"https://api.github.com/repos/opentable/sous/keys{/key_id}","collaborators_url":"https://api.github.com/repos/opentable/sous/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/opentable/sous/teams","hooks_url":"https://api.github.com/repos/opentable/sous/hooks","issue_events_url":"https://api.github.com/repos/opentable/sous/issues/events{/number}","events_url":"https://api.github.com/repos/opentable/sous/events","assignees_url":"https://api.github.com/repos/opentable/sous/assignees{/user}","branches_url":"https://api.github.com/repos/opentable/sous/branches{/branch}","tags_url":"https://api.github.com/repos/opentable/sous/tags","blobs_url":"https://api.github.com/repos/opentable/sous/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/opentable/sous/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/opentable/sous/git/refs{/sha}","trees_url":"https://api.github.com/repos/opentable/sous/git/trees{/sha}","statuses_url":"https://api.github.com/repos/opentable/sous/statuses/{sha}","languages_url":"https://api.github.com/repos/opentable/sous/languages","stargazers_url":"https://api.github.com/repos/opentable/sous/stargazers","contributors_url":"https://api.github.com/repos/opentable/sous/contributors","subscribers_url":"https://api.github.com/repos/opentable/sous/subscribers","subscription_url":"https://api.github.com/repos/opentable/sous/subscription","commits_url":"https://api.github.com/repos/opentable/sous/commits{/sha}","git_commits_url":"https://api.github.com/repos/opentable/sous/git/commits{/sha}","comments_url":"https://api.github.com/repos/opentable/sous/comments{/number}","issue_comment_url":"https://api.github.com/repos/opentable/sous/issues/comments{/number}","contents_url":"https://api.github.com/repos/opentable/sous/contents/{+path}","compare_url":"https://api.github.com/repos/opentable/sous/compare/{base}...{head}","merges_url":"https://api.github.com/repos/opentable/sous/merges","archive_url":"https://api.github.com/repos/opentable/sous/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/opentable/sous/downloads","issues_url":"https://api.github.com/repos/opentable/sous/issues{/number}","pulls_url":"https://api.github.com/repos/opentable/sous/pulls{/number}","milestones_url":"https://api.github.com/repos/opentable/sous/milestones{/number}","notifications_url":"https://api.github.com/repos/opentable/sous/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/opentable/sous/labels{/name}","releases_url":"https://api.github.com/repos/opentable/sous/releases{/id}","deployments_url":"https://api.github.com/repos/opentable/sous/deployments","created_at":"2015-09-24T18:22:46Z","updated_at":"2017-07-01T07:43:05Z","pushed_at":"2017-08-08T23:23:16Z","git_url":"git://github.com/opentable/sous.git","ssh_url":"git@github.com:opentable/sous.git","clone_url":"https://github.com/opentable/sous.git","svn_url":"https://github.com/opentable/sous","homepage":"","size":13078,"stargazers_count":36,"watchers_count":36,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":11,"mirror_url":null,"open_issues_count":3,"forks":11,"open_issues":3,"watchers":36,"default_branch":"master"}},"base":{"label":"opentable:master","ref":"master","sha":"6dcbe911ca1b5bd1198f57044677ab7547025ac8","user":{"login":"opentable","id":2653541,"avatar_url":"https://avatars0.githubusercontent.com/u/2653541?v=4","gravatar_id":"","url":"https://api.github.com/users/opentable","html_url":"https://github.com/opentable","followers_url":"https://api.github.com/users/opentable/followers","following_url":"https://api.github.com/users/opentable/following{/other_user}","gists_url":"https://api.github.com/users/opentable/gists{/gist_id}","starred_url":"https://api.github.com/users/opentable/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/opentable/subscriptions","organizations_url":"https://api.github.com/users/opentable/orgs","repos_url":"https://api.github.com/users/opentable/repos","events_url":"https://api.github.com/users/opentable/events{/privacy}","received_events_url":"https://api.github.com/users/opentable/received_events","type":"Organization","site_admin":false},"repo":{"id":43084873,"name":"sous","full_name":"opentable/sous","owner":{"login":"opentable","id":2653541,"avatar_url":"https://avatars0.githubusercontent.com/u/2653541?v=4","gravatar_id":"","url":"https://api.github.com/users/opentable","html_url":"https://github.com/opentable","followers_url":"https://api.github.com/users/opentable/followers","following_url":"https://api.github.com/users/opentable/following{/other_user}","gists_url":"https://api.github.com/users/opentable/gists{/gist_id}","starred_url":"https://api.github.com/users/opentable/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/opentable/subscriptions","organizations_url":"https://api.github.com/users/opentable/orgs","repos_url":"https://api.github.com/users/opentable/repos","events_url":"https://api.github.com/users/opentable/events{/privacy}","received_events_url":"https://api.github.com/users/opentable/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/opentable/sous","description":"Tools for building, testing, deploying Docker containers","fork":false,"url":"https://api.github.com/repos/opentable/sous","forks_url":"https://api.github.com/repos/opentable/sous/forks","keys_url":"https://api.github.com/repos/opentable/sous/keys{/key_id}","collaborators_url":"https://api.github.com/repos/opentable/sous/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/opentable/sous/teams","hooks_url":"https://api.github.com/repos/opentable/sous/hooks","issue_events_url":"https://api.github.com/repos/opentable/sous/issues/events{/number}","events_url":"https://api.github.com/repos/opentable/sous/events","assignees_url":"https://api.github.com/repos/opentable/sous/assignees{/user}","branches_url":"https://api.github.com/repos/opentable/sous/branches{/branch}","tags_url":"https://api.github.com/repos/opentable/sous/tags","blobs_url":"https://api.github.com/repos/opentable/sous/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/opentable/sous/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/opentable/sous/git/refs{/sha}","trees_url":"https://api.github.com/repos/opentable/sous/git/trees{/sha}","statuses_url":"https://api.github.com/repos/opentable/sous/statuses/{sha}","languages_url":"https://api.github.com/repos/opentable/sous/languages","stargazers_url":"https://api.github.com/repos/opentable/sous/stargazers","contributors_url":"https://api.github.com/repos/opentable/sous/contributors","subscribers_url":"https://api.github.com/repos/opentable/sous/subscribers","subscription_url":"https://api.github.com/repos/opentable/sous/subscription","commits_url":"https://api.github.com/repos/opentable/sous/commits{/sha}","git_commits_url":"https://api.github.com/repos/opentable/sous/git/commits{/sha}","comments_url":"https://api.github.com/repos/opentable/sous/comments{/number}","issue_comment_url":"https://api.github.com/repos/opentable/sous/issues/comments{/number}","contents_url":"https://api.github.com/repos/opentable/sous/contents/{+path}","compare_url":"https://api.github.com/repos/opentable/sous/compare/{base}...{head}","merges_url":"https://api.github.com/repos/opentable/sous/merges","archive_url":"https://api.github.com/repos/opentable/sous/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/opentable/sous/downloads","issues_url":"https://api.github.com/repos/opentable/sous/issues{/number}","pulls_url":"https://api.github.com/repos/opentable/sous/pulls{/number}","milestones_url":"https://api.github.com/repos/opentable/sous/milestones{/number}","notifications_url":"https://api.github.com/repos/opentable/sous/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/opentable/sous/labels{/name}","releases_url":"https://api.github.com/repos/opentable/sous/releases{/id}","deployments_url":"https://api.github.com/repos/opentable/sous/deployments","created_at":"2015-09-24T18:22:46Z","updated_at":"2017-07-01T07:43:05Z","pushed_at":"2017-08-08T23:23:16Z","git_url":"git://github.com/opentable/sous.git","ssh_url":"git@github.com:opentable/sous.git","clone_url":"https://github.com/opentable/sous.git","svn_url":"https://github.com/opentable/sous","homepage":"","size":13078,"stargazers_count":36,"watchers_count":36,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":11,"mirror_url":null,"open_issues_count":3,"forks":11,"open_issues":3,"watchers":36,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/opentable/sous/pulls/444"},"html":{"href":"https://github.com/opentable/sous/pull/444"},"issue":{"href":"https://api.github.com/repos/opentable/sous/issues/444"},"comments":{"href":"https://api.github.com/repos/opentable/sous/issues/444/comments"},"review_comments":{"href":"https://api.github.com/repos/opentable/sous/pulls/444/comments"},"review_comment":{"href":"https://api.github.com/repos/opentable/sous/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/opentable/sous/pulls/444/commits"},"statuses":{"href":"https://api.github.com/repos/opentable/sous/statuses/cc8c07cbfa8d27f80de84542b16a18db76889c08"}}}}
{"actor":{"display_login":"houndci-bot"}}