{"action":"created","comment":{"url":"https://api.github.com/repos/google/gapid/pulls/comments/190307704","pull_request_review_id":122656008,"id":190307704,"diff_hunk":"@@ -0,0 +1,244 @@\n+// Copyright (C) 2018 Google Inc.\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+//      http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n+\n+package remotessh\n+\n+import (\n+\t\"context\"\n+\t\"fmt\"\n+\t\"io\"\n+\t\"net\"\n+\t\"os\"\n+\t\"strings\"\n+\t\"sync\"\n+\n+\t\"github.com/google/gapid/core/log\"\n+\t\"github.com/google/gapid/core/os/device\"\n+\t\"github.com/google/gapid/core/os/shell\"\n+\t\"github.com/google/gapid/core/app/crash\"\n+\t\"golang.org/x/crypto/ssh\"\n+)\n+\n+// remoteProcess is the interface to a running process, as started by a Target.\n+type remoteProcess struct {\n+\tsession    *ssh.Session\n+\twg\t\t   sync.WaitGroup\n+}\n+\n+func (r *remoteProcess) Kill() error {\n+\treturn r.session.Signal(ssh.SIGSEGV)\n+}\n+\n+func (r *remoteProcess) Wait(ctx context.Context) error {\n+\tret := r.session.Wait()\n+\tr.wg.Wait()\n+\treturn ret\n+}\n+\n+var _ shell.Process = (*remoteProcess)(nil)\n+\n+type sshShellTarget struct{ b *binding }\n+\n+// Start starts the given command in the remote shell.\n+func (t sshShellTarget) Start(cmd shell.Cmd) (shell.Process, error) {\n+\tsession, err := t.b.connection.NewSession()\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\tp := &remoteProcess{\n+\t\tsession:    session,\n+\t\twg:    sync.WaitGroup{},\n+\t}\n+\n+\tif cmd.Stdin != nil {\n+\t\tstdin, err := session.StdinPipe()\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\t\tgo func() {\n+\t\t\tdefer stdin.Close()\n+\t\t\tio.Copy(stdin, cmd.Stdin)\n+\t\t}()\n+\t}\n+\n+\tif cmd.Stdout != nil {\n+\t\tstdout, err := session.StdoutPipe()\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\t\tp.wg.Add(1)\n+\t\tcrash.Go(func() {\n+\t\t\tb := make([]byte, 1024)\n+\t\t\tfor {\n+\t\t\t\tn, err := stdout.Read(b)\n+\t\t\t\tcmd.Stdout.Write(b[:n])\n+\t\t\t\tif err != nil {\n+\t\t\t\t\tp.wg.Done()\n+\t\t\t\t\tbreak\n+\t\t\t\t}\n+\t\t\t}\n+\t\t})\n+\t}\n+\n+\tif cmd.Stderr != nil {\n+\t\tstderr, err := session.StderrPipe()\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\t\tp.wg.Add(1)\n+\t\tcrash.Go(func() {\n+\t\t\tb := make([]byte, 1024)\n+\t\t\tfor {\n+\t\t\t\tn, err := stderr.Read(b)\n+\t\t\t\tcmd.Stderr.Write(b[:n])\n+\t\t\t\tif err != nil {\n+\t\t\t\t\tp.wg.Done()\n+\t\t\t\t\tbreak\n+\t\t\t\t}\n+\t\t\t}\n+\t\t})\n+\t}\n+\n+\tprefix := \"\"\n+\tif cmd.Dir != \"\" {\n+\t\tprefix += \"cd \" + cmd.Dir + \"; \"\n+\t}\n+\n+\tfor _, e := range cmd.Environment.Keys() {\n+\t\tif e != \"\" {\n+\t\t\tprefix = prefix + e + \"=\" + cmd.Environment.Get(e) + \" \"\n+\t\t}\n+\t}\n+\n+\tif err := session.Start(prefix + cmd.Name + \" \" + strings.Join(cmd.Args, \" \")); err != nil {\n+\t\treturn nil, err\n+\t}\n+\n+\treturn p, nil\n+}\n+\n+func (t sshShellTarget) String() string {\n+\tc := t.b.configuration\n+\treturn c.User + \"@\" + c.Host + \": \" + t.b.String()\n+}\n+\n+// Shell implements the Device interface returning commands that will error if run.\n+func (b binding) Shell(name string, args ...string) shell.Cmd {\n+\treturn shell.Command(name, args...).On(sshShellTarget{&b})\n+}\n+\n+func (b binding) destroyPosixDirectory(ctx context.Context, dir string) {\n+\t_, _ = b.Shell(\"rm\", \"-rf\", dir).Call(ctx)\n+}\n+\n+func (b binding) createPosixTempDirectory(ctx context.Context) (string, func(context.Context), error) {\n+\tdir, err := b.Shell(\"mktemp\", \"-d\").Call(ctx)\n+\tif err != nil {\n+\t\treturn \"\", nil, err\n+\t}\n+\treturn dir, func(ctx context.Context) { b.destroyPosixDirectory(ctx, dir) }, nil\n+}\n+\n+func (b binding) createWindowsTempDirectory(ctx context.Context) (string, func(ctx context.Context), error) {\n+\treturn \"\", nil, fmt.Errorf(\"Not yet supported, windows remote targets\")\n+}\n+\n+// MakeTempDir creates a temporary directory on the remote machine. It returns the\n+// full path, and a function that can be called to clean up the directory.\n+func (b binding) MakeTempDir(ctx context.Context) (string, func(ctx context.Context), error) {\n+\tswitch b.os {\n+\tcase device.Linux:\n+\t\tfallthrough\n+\tcase device.OSX:\n+\t\treturn b.createPosixTempDirectory(ctx)\n+\tcase device.Windows:\n+\t\treturn b.createWindowsTempDirectory(ctx)\n+\tdefault:\n+\t\tpanic(fmt.Errorf(\"Unsupported OS %v\", b.os))\n+\t}\n+}\n+\n+// WriteFile moves the contents of io.Reader into the given file on the remote machine.\n+// The file is given the mode as described by the unix filemode string.\n+func (b binding) WriteFile(ctx context.Context, contents io.Reader, mode string, destPath string) error {\n+\t_, err := b.Shell(\"cat\", \">\", destPath, \"; chmod \", mode, \" \", destPath).Read(contents).Call(ctx)\n+\treturn err\n+}\n+\n+// PushFile copies a file from a local path to the remote machine. Permissions are\n+// maintained across.\n+func (b binding) PushFile(ctx context.Context, source, dest string) error {\n+\tinfile, err := os.Open(source)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\tpermission, err := os.Stat(source)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\tperm := fmt.Sprintf(\"%4o\", permission.Mode().Perm())\n+\n+\treturn b.WriteFile(ctx, infile, perm, dest)\n+}\n+\n+// doTunnel tunnels a single connection through the SSH connection.\n+func (b binding) doTunnel(ctx context.Context, local net.Conn, remotePort int) error {\n+\tremote, err := b.connection.Dial(\"tcp\", fmt.Sprintf(\"localhost:%d\", remotePort))\n+\tif err != nil {\n+\t\tlocal.Close()\n+\t\treturn err\n+\t}\n+\n+\twg := sync.WaitGroup{}\n+\t\n+\tcopy := func(writer io.Writer, reader io.Reader) {\n+\t\t_, err := io.Copy(writer, reader)\n+\t\tif err != nil {\n+\t\t\tlog.E(ctx, \"Copy Error %s\", err)\n+\t\t}\n+\t\twg.Done()\n+\t}\n+\n+\twg.Add(2)\n+\tcrash.Go( func() { copy(local, remote)} )\n+\tcrash.Go( func() { copy(remote, local)} )\n+\t\n+\tcrash.Go( func() {\n+\t\tdefer local.Close()\n+\t\tdefer remote.Close()\n+\t\twg.Wait()\n+\t})\n+\treturn nil\n+}\n+\n+// ForwardPort forwards a local TCP port to the remote machine on the remote port.\n+// The local port that was opened is returned.\n+func (b binding) ForwardPort(ctx context.Context, remotePort int) (int, error) {\n+\tlistener, err := net.Listen(\"tcp\", \":0\")\n+\tif err != nil {\n+\t\treturn 0, err\n+\t}\n+\tcrash.Go( func() {\n+\t\tdefer listener.Close()\n+\t\tfor {\n+\t\t\tlocal, err := listener.Accept()\n+\t\t\tif err != nil {\n+\t\t\t\treturn\n+\t\t\t}\n+\t\t\tb.doTunnel(ctx, local, remotePort)","path":"core/os/device/remotessh/commands.go","position":225,"original_position":239,"commit_id":"d4467ba5102dfb5deb9827eb56257701b96d635d","original_commit_id":"bd0fe706f0c23acb8d0a94a98e770fef1fdca069","user":{"login":"AWoloszyn","id":9043212,"avatar_url":"https://avatars1.githubusercontent.com/u/9043212?v=4","gravatar_id":"","url":"https://api.github.com/users/AWoloszyn","html_url":"https://github.com/AWoloszyn","followers_url":"https://api.github.com/users/AWoloszyn/followers","following_url":"https://api.github.com/users/AWoloszyn/following{/other_user}","gists_url":"https://api.github.com/users/AWoloszyn/gists{/gist_id}","starred_url":"https://api.github.com/users/AWoloszyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AWoloszyn/subscriptions","organizations_url":"https://api.github.com/users/AWoloszyn/orgs","repos_url":"https://api.github.com/users/AWoloszyn/repos","events_url":"https://api.github.com/users/AWoloszyn/events{/privacy}","received_events_url":"https://api.github.com/users/AWoloszyn/received_events","type":"User","site_admin":false},"body":"Likely yes thanks. If we can't connect once, its not likely the next attempt will work.","created_at":"2018-05-23T16:05:50Z","updated_at":"2018-05-23T16:17:10Z","html_url":"https://github.com/google/gapid/pull/1898#discussion_r190307704","pull_request_url":"https://api.github.com/repos/google/gapid/pulls/1898","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/google/gapid/pulls/comments/190307704"},"html":{"href":"https://github.com/google/gapid/pull/1898#discussion_r190307704"},"pull_request":{"href":"https://api.github.com/repos/google/gapid/pulls/1898"}},"in_reply_to_id":190280560},"pull_request":{"url":"https://api.github.com/repos/google/gapid/pulls/1898","id":189701776,"html_url":"https://github.com/google/gapid/pull/1898","diff_url":"https://github.com/google/gapid/pull/1898.diff","patch_url":"https://github.com/google/gapid/pull/1898.patch","issue_url":"https://api.github.com/repos/google/gapid/issues/1898","number":1898,"state":"open","locked":false,"title":"Remote SSH connection support","user":{"login":"AWoloszyn","id":9043212,"avatar_url":"https://avatars1.githubusercontent.com/u/9043212?v=4","gravatar_id":"","url":"https://api.github.com/users/AWoloszyn","html_url":"https://github.com/AWoloszyn","followers_url":"https://api.github.com/users/AWoloszyn/followers","following_url":"https://api.github.com/users/AWoloszyn/following{/other_user}","gists_url":"https://api.github.com/users/AWoloszyn/gists{/gist_id}","starred_url":"https://api.github.com/users/AWoloszyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AWoloszyn/subscriptions","organizations_url":"https://api.github.com/users/AWoloszyn/orgs","repos_url":"https://api.github.com/users/AWoloszyn/repos","events_url":"https://api.github.com/users/AWoloszyn/events{/privacy}","received_events_url":"https://api.github.com/users/AWoloszyn/received_events","type":"User","site_admin":false},"body":"This is still somewhat in the experimental phase, it has no UI present yet, and can only be\r\nenabled by command-line flags, but it adds functional remote SSH support.\r\n\r\nThis depends on both https://github.com/google/gapid/pull/1896 https://github.com/google/gapid/pull/1897 to be useful.","created_at":"2018-05-22T15:22:52Z","updated_at":"2018-05-23T16:17:10Z","closed_at":null,"merged_at":null,"merge_commit_sha":"d17517aa86116ebfb782088b9fb7747eda7bd551","assignee":null,"assignees":[],"requested_reviewers":[{"login":"Qining","id":2983685,"avatar_url":"https://avatars0.githubusercontent.com/u/2983685?v=4","gravatar_id":"","url":"https://api.github.com/users/Qining","html_url":"https://github.com/Qining","followers_url":"https://api.github.com/users/Qining/followers","following_url":"https://api.github.com/users/Qining/following{/other_user}","gists_url":"https://api.github.com/users/Qining/gists{/gist_id}","starred_url":"https://api.github.com/users/Qining/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Qining/subscriptions","organizations_url":"https://api.github.com/users/Qining/orgs","repos_url":"https://api.github.com/users/Qining/repos","events_url":"https://api.github.com/users/Qining/events{/privacy}","received_events_url":"https://api.github.com/users/Qining/received_events","type":"User","site_admin":false},{"login":"pmuetschard","id":13356324,"avatar_url":"https://avatars3.githubusercontent.com/u/13356324?v=4","gravatar_id":"","url":"https://api.github.com/users/pmuetschard","html_url":"https://github.com/pmuetschard","followers_url":"https://api.github.com/users/pmuetschard/followers","following_url":"https://api.github.com/users/pmuetschard/following{/other_user}","gists_url":"https://api.github.com/users/pmuetschard/gists{/gist_id}","starred_url":"https://api.github.com/users/pmuetschard/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pmuetschard/subscriptions","organizations_url":"https://api.github.com/users/pmuetschard/orgs","repos_url":"https://api.github.com/users/pmuetschard/repos","events_url":"https://api.github.com/users/pmuetschard/events{/privacy}","received_events_url":"https://api.github.com/users/pmuetschard/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/google/gapid/pulls/1898/commits","review_comments_url":"https://api.github.com/repos/google/gapid/pulls/1898/comments","review_comment_url":"https://api.github.com/repos/google/gapid/pulls/comments{/number}","comments_url":"https://api.github.com/repos/google/gapid/issues/1898/comments","statuses_url":"https://api.github.com/repos/google/gapid/statuses/d4467ba5102dfb5deb9827eb56257701b96d635d","head":{"label":"AWoloszyn:remote-ssh","ref":"remote-ssh","sha":"d4467ba5102dfb5deb9827eb56257701b96d635d","user":{"login":"AWoloszyn","id":9043212,"avatar_url":"https://avatars1.githubusercontent.com/u/9043212?v=4","gravatar_id":"","url":"https://api.github.com/users/AWoloszyn","html_url":"https://github.com/AWoloszyn","followers_url":"https://api.github.com/users/AWoloszyn/followers","following_url":"https://api.github.com/users/AWoloszyn/following{/other_user}","gists_url":"https://api.github.com/users/AWoloszyn/gists{/gist_id}","starred_url":"https://api.github.com/users/AWoloszyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AWoloszyn/subscriptions","organizations_url":"https://api.github.com/users/AWoloszyn/orgs","repos_url":"https://api.github.com/users/AWoloszyn/repos","events_url":"https://api.github.com/users/AWoloszyn/events{/privacy}","received_events_url":"https://api.github.com/users/AWoloszyn/received_events","type":"User","site_admin":false},"repo":{"id":84079818,"name":"gapid","full_name":"AWoloszyn/gapid","owner":{"login":"AWoloszyn","id":9043212,"avatar_url":"https://avatars1.githubusercontent.com/u/9043212?v=4","gravatar_id":"","url":"https://api.github.com/users/AWoloszyn","html_url":"https://github.com/AWoloszyn","followers_url":"https://api.github.com/users/AWoloszyn/followers","following_url":"https://api.github.com/users/AWoloszyn/following{/other_user}","gists_url":"https://api.github.com/users/AWoloszyn/gists{/gist_id}","starred_url":"https://api.github.com/users/AWoloszyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AWoloszyn/subscriptions","organizations_url":"https://api.github.com/users/AWoloszyn/orgs","repos_url":"https://api.github.com/users/AWoloszyn/repos","events_url":"https://api.github.com/users/AWoloszyn/events{/privacy}","received_events_url":"https://api.github.com/users/AWoloszyn/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/AWoloszyn/gapid","description":"Graphics API Debugger","fork":true,"url":"https://api.github.com/repos/AWoloszyn/gapid","forks_url":"https://api.github.com/repos/AWoloszyn/gapid/forks","keys_url":"https://api.github.com/repos/AWoloszyn/gapid/keys{/key_id}","collaborators_url":"https://api.github.com/repos/AWoloszyn/gapid/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/AWoloszyn/gapid/teams","hooks_url":"https://api.github.com/repos/AWoloszyn/gapid/hooks","issue_events_url":"https://api.github.com/repos/AWoloszyn/gapid/issues/events{/number}","events_url":"https://api.github.com/repos/AWoloszyn/gapid/events","assignees_url":"https://api.github.com/repos/AWoloszyn/gapid/assignees{/user}","branches_url":"https://api.github.com/repos/AWoloszyn/gapid/branches{/branch}","tags_url":"https://api.github.com/repos/AWoloszyn/gapid/tags","blobs_url":"https://api.github.com/repos/AWoloszyn/gapid/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/AWoloszyn/gapid/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/AWoloszyn/gapid/git/refs{/sha}","trees_url":"https://api.github.com/repos/AWoloszyn/gapid/git/trees{/sha}","statuses_url":"https://api.github.com/repos/AWoloszyn/gapid/statuses/{sha}","languages_url":"https://api.github.com/repos/AWoloszyn/gapid/languages","stargazers_url":"https://api.github.com/repos/AWoloszyn/gapid/stargazers","contributors_url":"https://api.github.com/repos/AWoloszyn/gapid/contributors","subscribers_url":"https://api.github.com/repos/AWoloszyn/gapid/subscribers","subscription_url":"https://api.github.com/repos/AWoloszyn/gapid/subscription","commits_url":"https://api.github.com/repos/AWoloszyn/gapid/commits{/sha}","git_commits_url":"https://api.github.com/repos/AWoloszyn/gapid/git/commits{/sha}","comments_url":"https://api.github.com/repos/AWoloszyn/gapid/comments{/number}","issue_comment_url":"https://api.github.com/repos/AWoloszyn/gapid/issues/comments{/number}","contents_url":"https://api.github.com/repos/AWoloszyn/gapid/contents/{+path}","compare_url":"https://api.github.com/repos/AWoloszyn/gapid/compare/{base}...{head}","merges_url":"https://api.github.com/repos/AWoloszyn/gapid/merges","archive_url":"https://api.github.com/repos/AWoloszyn/gapid/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/AWoloszyn/gapid/downloads","issues_url":"https://api.github.com/repos/AWoloszyn/gapid/issues{/number}","pulls_url":"https://api.github.com/repos/AWoloszyn/gapid/pulls{/number}","milestones_url":"https://api.github.com/repos/AWoloszyn/gapid/milestones{/number}","notifications_url":"https://api.github.com/repos/AWoloszyn/gapid/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/AWoloszyn/gapid/labels{/name}","releases_url":"https://api.github.com/repos/AWoloszyn/gapid/releases{/id}","deployments_url":"https://api.github.com/repos/AWoloszyn/gapid/deployments","created_at":"2017-03-06T14:01:20Z","updated_at":"2017-03-06T14:01:26Z","pushed_at":"2018-05-23T15:25:41Z","git_url":"git://github.com/AWoloszyn/gapid.git","ssh_url":"git@github.com:AWoloszyn/gapid.git","clone_url":"https://github.com/AWoloszyn/gapid.git","svn_url":"https://github.com/AWoloszyn/gapid","homepage":"","size":59914,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_projects":true,"has_downloads":false,"has_wiki":false,"has_pages":true,"forks_count":1,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0"},"forks":1,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"google:master","ref":"master","sha":"2105d7dcf3804332beefb3d7acae505b386677f5","user":{"login":"google","id":1342004,"avatar_url":"https://avatars1.githubusercontent.com/u/1342004?v=4","gravatar_id":"","url":"https://api.github.com/users/google","html_url":"https://github.com/google","followers_url":"https://api.github.com/users/google/followers","following_url":"https://api.github.com/users/google/following{/other_user}","gists_url":"https://api.github.com/users/google/gists{/gist_id}","starred_url":"https://api.github.com/users/google/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/google/subscriptions","organizations_url":"https://api.github.com/users/google/orgs","repos_url":"https://api.github.com/users/google/repos","events_url":"https://api.github.com/users/google/events{/privacy}","received_events_url":"https://api.github.com/users/google/received_events","type":"Organization","site_admin":false},"repo":{"id":81937787,"name":"gapid","full_name":"google/gapid","owner":{"login":"google","id":1342004,"avatar_url":"https://avatars1.githubusercontent.com/u/1342004?v=4","gravatar_id":"","url":"https://api.github.com/users/google","html_url":"https://github.com/google","followers_url":"https://api.github.com/users/google/followers","following_url":"https://api.github.com/users/google/following{/other_user}","gists_url":"https://api.github.com/users/google/gists{/gist_id}","starred_url":"https://api.github.com/users/google/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/google/subscriptions","organizations_url":"https://api.github.com/users/google/orgs","repos_url":"https://api.github.com/users/google/repos","events_url":"https://api.github.com/users/google/events{/privacy}","received_events_url":"https://api.github.com/users/google/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/google/gapid","description":"Graphics API Debugger","fork":false,"url":"https://api.github.com/repos/google/gapid","forks_url":"https://api.github.com/repos/google/gapid/forks","keys_url":"https://api.github.com/repos/google/gapid/keys{/key_id}","collaborators_url":"https://api.github.com/repos/google/gapid/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/google/gapid/teams","hooks_url":"https://api.github.com/repos/google/gapid/hooks","issue_events_url":"https://api.github.com/repos/google/gapid/issues/events{/number}","events_url":"https://api.github.com/repos/google/gapid/events","assignees_url":"https://api.github.com/repos/google/gapid/assignees{/user}","branches_url":"https://api.github.com/repos/google/gapid/branches{/branch}","tags_url":"https://api.github.com/repos/google/gapid/tags","blobs_url":"https://api.github.com/repos/google/gapid/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/google/gapid/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/google/gapid/git/refs{/sha}","trees_url":"https://api.github.com/repos/google/gapid/git/trees{/sha}","statuses_url":"https://api.github.com/repos/google/gapid/statuses/{sha}","languages_url":"https://api.github.com/repos/google/gapid/languages","stargazers_url":"https://api.github.com/repos/google/gapid/stargazers","contributors_url":"https://api.github.com/repos/google/gapid/contributors","subscribers_url":"https://api.github.com/repos/google/gapid/subscribers","subscription_url":"https://api.github.com/repos/google/gapid/subscription","commits_url":"https://api.github.com/repos/google/gapid/commits{/sha}","git_commits_url":"https://api.github.com/repos/google/gapid/git/commits{/sha}","comments_url":"https://api.github.com/repos/google/gapid/comments{/number}","issue_comment_url":"https://api.github.com/repos/google/gapid/issues/comments{/number}","contents_url":"https://api.github.com/repos/google/gapid/contents/{+path}","compare_url":"https://api.github.com/repos/google/gapid/compare/{base}...{head}","merges_url":"https://api.github.com/repos/google/gapid/merges","archive_url":"https://api.github.com/repos/google/gapid/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/google/gapid/downloads","issues_url":"https://api.github.com/repos/google/gapid/issues{/number}","pulls_url":"https://api.github.com/repos/google/gapid/pulls{/number}","milestones_url":"https://api.github.com/repos/google/gapid/milestones{/number}","notifications_url":"https://api.github.com/repos/google/gapid/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/google/gapid/labels{/name}","releases_url":"https://api.github.com/repos/google/gapid/releases{/id}","deployments_url":"https://api.github.com/repos/google/gapid/deployments","created_at":"2017-02-14T11:18:43Z","updated_at":"2018-05-23T15:57:56Z","pushed_at":"2018-05-23T15:57:53Z","git_url":"git://github.com/google/gapid.git","ssh_url":"git@github.com:google/gapid.git","clone_url":"https://github.com/google/gapid.git","svn_url":"https://github.com/google/gapid","homepage":"","size":67487,"stargazers_count":753,"watchers_count":753,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":false,"has_wiki":false,"has_pages":true,"forks_count":124,"mirror_url":null,"archived":false,"open_issues_count":185,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0"},"forks":124,"open_issues":185,"watchers":753,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/google/gapid/pulls/1898"},"html":{"href":"https://github.com/google/gapid/pull/1898"},"issue":{"href":"https://api.github.com/repos/google/gapid/issues/1898"},"comments":{"href":"https://api.github.com/repos/google/gapid/issues/1898/comments"},"review_comments":{"href":"https://api.github.com/repos/google/gapid/pulls/1898/comments"},"review_comment":{"href":"https://api.github.com/repos/google/gapid/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/google/gapid/pulls/1898/commits"},"statuses":{"href":"https://api.github.com/repos/google/gapid/statuses/d4467ba5102dfb5deb9827eb56257701b96d635d"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"AWoloszyn"}}