{"action":"created","comment":{"url":"https://api.github.com/repos/drone/drone/pulls/comments/60183758","id":60183758,"diff_hunk":"@@ -0,0 +1,358 @@\n+package bitbucketserver\n+\n+// Requires the following to be set\n+// GIT_USERNAME -> a username on the stash server that has access to clone all repos\n+// GIT_USERPASSWORD -> password to the user that has access to clone all repos\n+// REMOTE_DRIVER=bitbucketserver\n+// REMOTE_CONFIG=https://{servername}?consumer_key={key added on the stash server for oath1}&open={not used yet}\n+// Configure application links in the bitbucket server --\n+// application url needs to be the base url to drone\n+// incoming auth needs to have the consumer key (same as the key in REMOTE_CONFIG)\n+// set the public key (public key from the private key added to /private_key.pem name matters)\n+// consumer call back is the base url to drone plus /authorize/\n+// Needs a pem private key added to / something like \"ADD private_key.pem /\" (name matters, needs to be /private_key.pem) to the Dockerfile\n+// After that you should be good to go\n+\n+\n+\n+import (\n+\t\"github.com/drone/drone/shared/envconfig\"\n+\t\"net/url\"\n+\tlog \"github.com/Sirupsen/logrus\"\n+\t\"net/http\"\n+\t\"github.com/drone/drone/model\"\n+\t\"fmt\"\n+\t\"io/ioutil\"\n+\t\"strconv\"\n+\t\"encoding/json\"\n+\n+)\n+\n+type BitbucketServer struct {\n+\tURL string\n+\tConsumerKey string\n+\tGitUserName string\n+\tGitPassword string\n+\tOpen bool\n+}\n+\n+func Load(env envconfig.Env) *BitbucketServer{\n+\n+\t//Read\n+\tconfig := env.String(\"REMOTE_CONFIG\", \"\")\n+\tgitUserName := env.String(\"GIT_USERNAME\", \"\")\n+\tgitUserPassword := env.String(\"GIT_USERPASSWORD\",\"\")\n+\n+\turl_, err := url.Parse(config)\n+\tif err != nil {\n+\t\tlog.Fatalln(\"unable to parse remote dsn. %s\", err)\n+\t}\n+\tparams := url_.Query()\n+\turl_.Path = \"\"\n+\turl_.RawQuery = \"\"\n+\n+\tbitbucketserver := BitbucketServer{}\n+\tbitbucketserver.URL = url_.String()\n+\tbitbucketserver.GitUserName = gitUserName\n+\tbitbucketserver.GitPassword = gitUserPassword\n+\tbitbucketserver.ConsumerKey = params.Get(\"consumer_key\")\n+\tbitbucketserver.Open, _ = strconv.ParseBool(params.Get(\"open\"))\n+\n+\treturn &bitbucketserver\n+}\n+\n+func (bs *BitbucketServer) Login(res http.ResponseWriter, req *http.Request) (*model.User, bool, error){\n+\tlog.Info(\"Starting to login for bitbucketServer\")\n+\n+\tc := NewClient(bs.ConsumerKey, bs.URL)\n+\n+\tlog.Info(\"getting the requestToken\")\n+\trequestToken, url, err := c.GetRequestTokenAndUrl(\"oob\")\n+\tif err != nil {\n+\t\tlog.Fatal(err)\n+\t}\n+\n+\tvar code = req.FormValue(\"oauth_verifier\")\n+\tif len(code) == 0 {\n+\t\tlog.Info(\"redirecting to %s\", url)\n+\t\thttp.Redirect(res, req, url, http.StatusSeeOther)\n+\t\treturn nil, false, nil\n+\t}\n+\n+\tvar request_oauth_token = req.FormValue(\"oauth_token\")\n+\trequestToken.Token = request_oauth_token\n+\taccessToken, err := c.AuthorizeToken(requestToken, code)\n+\tif err !=nil {\n+\t\tlog.Error(err)\n+\t}\n+\n+\tclient, err := c.MakeHttpClient(accessToken)\n+\tif err != nil {\n+\t\tlog.Fatal(err)\n+\t}\n+\n+\tresponse, err := client.Get(bs.URL + \"/plugins/servlet/applinks/whoami\")\n+\tif err != nil {\n+\t\tlog.Fatal(err)\n+\t}\n+\tdefer response.Body.Close()\n+\tbits, err := ioutil.ReadAll(response.Body)\n+\tuserName := string(bits)\n+\n+\tresponse1, err := client.Get(bs.URL + \"/rest/api/1.0/users/\" +userName)\n+\tcontents, err := ioutil.ReadAll(response1.Body)\n+\tdefer response1.Body.Close()\n+\tvar mUser User\n+\tjson.Unmarshal(contents, &mUser)\n+\n+\tuser := model.User{}\n+\tuser.Login = userName\n+\tuser.Email = mUser.EmailAddress\n+\tuser.Token = accessToken.Token\n+\n+\tuser.Avatar = avatarLink(mUser.EmailAddress)\n+\n+\n+\treturn &user, bs.Open, nil\n+}\n+\n+func (bs *BitbucketServer) Auth(token, secret string) (string, error) {\n+\tlog.Info(\"Staring to auth for bitbucketServer. %s\", token)\n+\tif len(token) == 0 {\n+\t\treturn \"\", fmt.Errorf(\"Hasn't logged in yet\")\n+\t}\n+\treturn token, nil;\n+}\n+\n+func (bs *BitbucketServer) Repo(u *model.User, owner, name string) (*model.Repo, error){\n+\tlog.Info(\"Staring repo for bitbucketServer with user \" + u.Login + \" \" + owner + \" \" + name )\n+\n+\tclient := NewClientWithToken(bs.ConsumerKey, bs.URL, u.Token)\n+\n+\turl := bs.URL + \"/rest/api/1.0/projects/\" + owner + \"/repos/\" + name\n+\tlog.Info(\"Trying to get \" + url)\n+\tresponse, err := client.Get(url)\n+\tif err != nil {\n+\t\tlog.Fatal(err)\n+\t}\n+\tdefer response.Body.Close()\n+\tcontents, err := ioutil.ReadAll(response.Body)\n+\tbsRepo := BSRepo{}\n+\tjson.Unmarshal(contents, &bsRepo)\n+\n+\tcloneLink := \"\"\n+\trepoLink := \"\"\n+\n+\tfor _, item := range bsRepo.Links.Clone {\n+\t\tif item.Name == \"http\" {\n+\t\t\tcloneLink = item.Href\n+\t\t}\n+\t}\n+\tfor _, item := range bsRepo.Links.Self {\n+\t\tif item.Href != \"\" {\n+\t\t\trepoLink = item.Href\n+\t\t}\n+\t}\n+\t//TODO: get the real allow tag+ infomration\n+\trepo := &model.Repo{}\n+\trepo.Clone = cloneLink\n+\trepo.Link = repoLink\n+\trepo.Name=bsRepo.Slug\n+\trepo.Owner=bsRepo.Project.Key\n+\trepo.AllowTag=false\n+\trepo.AllowDeploy=false\n+\trepo.AllowPull=false\n+\trepo.AllowPush=true\n+\trepo.FullName = bsRepo.Project.Key +\"/\" +bsRepo.Slug\n+\trepo.Branch = \"master\"\n+\trepo.Kind = model.RepoGit\n+\n+\n+\treturn repo, nil;\n+}\n+\n+\n+func (bs *BitbucketServer) Repos(u *model.User) ([]*model.RepoLite, error){\n+\tlog.Info(\"Staring repos for bitbucketServer \" + u.Login)\n+\tvar repos = []*model.RepoLite{}\n+\n+\tclient := NewClientWithToken(bs.ConsumerKey, bs.URL, u.Token)\n+\n+\tresponse, err := client.Get(bs.URL + \"/rest/api/1.0/repos?limit=10000\")\n+\tif err != nil {\n+\t\tlog.Fatal(err)\n+\t}\n+\tdefer response.Body.Close()\n+\tcontents, err := ioutil.ReadAll(response.Body)\n+\tvar repoResponse Repos\n+\tjson.Unmarshal(contents, &repoResponse)\n+\n+\tfor _, repo := range repoResponse.Values {\n+\t\trepos = append(repos, &model.RepoLite{\n+\t\t\tName:     repo.Slug,\n+\t\t\tFullName: repo.Project.Key + \"/\" +  repo.Slug,\n+\t\t\tOwner: repo.Project.Key,\n+\t\t})\n+\t}\n+\n+\n+\treturn repos, nil;\n+}\n+\n+func (bs *BitbucketServer) Perm(u *model.User, owner, repo string) (*model.Perm, error){\n+\n+\t//TODO: find the real permissions\n+\tlog.Info(\"Staring perm for bitbucketServer\")\n+\tperms := new(model.Perm)\n+\tperms.Pull = true\n+\tperms.Admin = true\n+\tperms.Push = true\n+\treturn perms , nil\n+}\n+\n+func (bs *BitbucketServer) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error){\n+\tlog.Info(fmt.Sprintf(\"Staring file for bitbucketServer login: %s repo: %s buildevent: %s string: %s\",u.Login, r.Name, b.Event, f))\n+\n+\tclient := NewClientWithToken(bs.ConsumerKey, bs.URL, u.Token)\n+\tfileURL := fmt.Sprintf(\"%s/projects/%s/repos/%s/browse/%s?raw\", bs.URL,r.Owner,r.Name,f)\n+\tlog.Info(fileURL)\n+\tresponse, err := client.Get(fileURL)\n+\tif err != nil {\n+\t\tlog.Fatal(err)\n+\t}\n+\tif response.StatusCode == 404 {\n+\t\treturn nil,nil\n+\t}\n+\tdefer response.Body.Close()\n+\tresponseBytes, err := ioutil.ReadAll(response.Body)\n+\tif err != nil {\n+\t\tlog.Fatal(err)\n+\t}\n+\n+\n+\treturn  responseBytes, nil;\n+}\n+\n+func (bs *BitbucketServer) Status(u *model.User, r *model.Repo, b *model.Build, link string) error{\n+\tlog.Info(\"Staring status for bitbucketServer\")\n+\treturn nil;\n+}\n+\n+func (bs *BitbucketServer) Netrc(user *model.User, r *model.Repo) (*model.Netrc, error){\n+\tlog.Info(\"Starting the Netrc lookup\")\n+\tu, err := url.Parse(bs.URL)\n+\tif err != nil {\n+\t\tpanic(err)\n+\t}\n+\treturn &model.Netrc{\n+\t\tMachine:  u.Host,\n+\t\tLogin:    bs.GitUserName,\n+\t\tPassword: bs.GitPassword,\n+\t}, nil\n+}\n+\n+func (bs *BitbucketServer) Activate(u *model.User, r *model.Repo, k *model.Key, link string) error{\n+\tlog.Info(fmt.Sprintf(\"Staring activate for bitbucketServer user: %s repo: %s key: %s link: %s\",u.Login,r.Name,k,link))\n+\tclient := NewClientWithToken(bs.ConsumerKey, bs.URL, u.Token)\n+\thook, err := bs.CreateHook(client, r.Owner,r.Name, \"com.atlassian.stash.plugin.stash-web-post-receive-hooks-plugin:postReceiveHook\",link)\n+\tif err !=nil {\n+\t\treturn err\n+\t}\n+\tlog.Info(hook)\n+\treturn nil;\n+}\n+\n+func (bs *BitbucketServer) Deactivate(u *model.User, r *model.Repo, link string) error{\n+\tlog.Info(fmt.Sprintf(\"Staring deactivating for bitbucketServer user: %s repo: %s link: %s\",u.Login,r.Name,link))\n+\tclient := NewClientWithToken(bs.ConsumerKey, bs.URL, u.Token)\n+\terr := bs.DeleteHook(client, r.Owner,r.Name, \"com.atlassian.stash.plugin.stash-web-post-receive-hooks-plugin:postReceiveHook\",link)\n+\tif err !=nil {\n+\t\treturn err\n+\t}\n+\treturn nil;\n+}\n+\n+func (bs *BitbucketServer) Hook(r *http.Request) (*model.Repo, *model.Build, error){\n+\tlog.Info(\"Staring hook for bitbucketServer\")\n+\tdefer r.Body.Close()\n+\tcontents, err := ioutil.ReadAll(r.Body)\n+\tif err != nil {\n+\t\tlog.Info(err)\n+\t}\n+\n+\tvar hookPost postHook\n+\tjson.Unmarshal(contents, &hookPost)\n+\n+\n+\n+\tbuildModel := &model.Build{}\n+\tbuildModel.Event = model.EventPush\n+\tbuildModel.Ref = hookPost.RefChanges[0].RefID\n+\tbuildModel.Author = hookPost.Changesets.Values[0].ToCommit.Author.EmailAddress\n+\tbuildModel.Commit = hookPost.RefChanges[0].ToHash\n+\tbuildModel.Avatar = avatarLink(hookPost.Changesets.Values[0].ToCommit.Author.EmailAddress)\n+\n+\t//All you really need is the name and owner. That's what creates the lookup key, so it needs to match the repo info. Just an FYI\n+\trepo := &model.Repo{}\n+\trepo.Name=hookPost.Repository.Slug\n+\trepo.Owner = hookPost.Repository.Project.Key\n+\trepo.AllowTag=false\n+\trepo.AllowDeploy=false\n+\trepo.AllowPull=false\n+\trepo.AllowPush=true","path":"remote/bitbucketserver/bitbucketserver.go","position":302,"original_position":302,"commit_id":"df45c847630e4d54e4d0168364e460d31cf18153","original_commit_id":"df45c847630e4d54e4d0168364e460d31cf18153","user":{"login":"bradrydzewski","id":817538,"avatar_url":"https://avatars.githubusercontent.com/u/817538?v=3","gravatar_id":"","url":"https://api.github.com/users/bradrydzewski","html_url":"https://github.com/bradrydzewski","followers_url":"https://api.github.com/users/bradrydzewski/followers","following_url":"https://api.github.com/users/bradrydzewski/following{/other_user}","gists_url":"https://api.github.com/users/bradrydzewski/gists{/gist_id}","starred_url":"https://api.github.com/users/bradrydzewski/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bradrydzewski/subscriptions","organizations_url":"https://api.github.com/users/bradrydzewski/orgs","repos_url":"https://api.github.com/users/bradrydzewski/repos","events_url":"https://api.github.com/users/bradrydzewski/events{/privacy}","received_events_url":"https://api.github.com/users/bradrydzewski/received_events","type":"User","site_admin":false},"body":"no need to set these values to false because the zero value of a boolean in Go is false. So you can remove these lines :)","created_at":"2016-04-19T07:28:56Z","updated_at":"2016-04-19T07:28:56Z","html_url":"https://github.com/drone/drone/pull/1581#discussion_r60183758","pull_request_url":"https://api.github.com/repos/drone/drone/pulls/1581","_links":{"self":{"href":"https://api.github.com/repos/drone/drone/pulls/comments/60183758"},"html":{"href":"https://github.com/drone/drone/pull/1581#discussion_r60183758"},"pull_request":{"href":"https://api.github.com/repos/drone/drone/pulls/1581"}}},"pull_request":{"url":"https://api.github.com/repos/drone/drone/pulls/1581","id":66963508,"html_url":"https://github.com/drone/drone/pull/1581","diff_url":"https://github.com/drone/drone/pull/1581.diff","patch_url":"https://github.com/drone/drone/pull/1581.patch","issue_url":"https://api.github.com/repos/drone/drone/issues/1581","number":1581,"state":"open","locked":false,"title":"Stash/Bitbucket Server initial support","user":{"login":"josmo","id":387992,"avatar_url":"https://avatars.githubusercontent.com/u/387992?v=3","gravatar_id":"","url":"https://api.github.com/users/josmo","html_url":"https://github.com/josmo","followers_url":"https://api.github.com/users/josmo/followers","following_url":"https://api.github.com/users/josmo/following{/other_user}","gists_url":"https://api.github.com/users/josmo/gists{/gist_id}","starred_url":"https://api.github.com/users/josmo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/josmo/subscriptions","organizations_url":"https://api.github.com/users/josmo/orgs","repos_url":"https://api.github.com/users/josmo/repos","events_url":"https://api.github.com/users/josmo/events{/privacy}","received_events_url":"https://api.github.com/users/josmo/received_events","type":"User","site_admin":false},"body":"This is the first go to get Stash/Bitbucket support working(continuation of https://github.com/drone/drone/pull/567).  The \"happy path\" works and there's a bit of clean up (First time I've written anything in golang) but if folks want to try it out or give some feedback on the dos and don'ts I'll update it.   ","created_at":"2016-04-19T05:48:16Z","updated_at":"2016-04-19T07:28:56Z","closed_at":null,"merged_at":null,"merge_commit_sha":null,"assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/drone/drone/pulls/1581/commits","review_comments_url":"https://api.github.com/repos/drone/drone/pulls/1581/comments","review_comment_url":"https://api.github.com/repos/drone/drone/pulls/comments{/number}","comments_url":"https://api.github.com/repos/drone/drone/issues/1581/comments","statuses_url":"https://api.github.com/repos/drone/drone/statuses/df45c847630e4d54e4d0168364e460d31cf18153","head":{"label":"josmo:bitbucketserver","ref":"bitbucketserver","sha":"df45c847630e4d54e4d0168364e460d31cf18153","user":{"login":"josmo","id":387992,"avatar_url":"https://avatars.githubusercontent.com/u/387992?v=3","gravatar_id":"","url":"https://api.github.com/users/josmo","html_url":"https://github.com/josmo","followers_url":"https://api.github.com/users/josmo/followers","following_url":"https://api.github.com/users/josmo/following{/other_user}","gists_url":"https://api.github.com/users/josmo/gists{/gist_id}","starred_url":"https://api.github.com/users/josmo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/josmo/subscriptions","organizations_url":"https://api.github.com/users/josmo/orgs","repos_url":"https://api.github.com/users/josmo/repos","events_url":"https://api.github.com/users/josmo/events{/privacy}","received_events_url":"https://api.github.com/users/josmo/received_events","type":"User","site_admin":false},"repo":{"id":55722876,"name":"drone","full_name":"josmo/drone","owner":{"login":"josmo","id":387992,"avatar_url":"https://avatars.githubusercontent.com/u/387992?v=3","gravatar_id":"","url":"https://api.github.com/users/josmo","html_url":"https://github.com/josmo","followers_url":"https://api.github.com/users/josmo/followers","following_url":"https://api.github.com/users/josmo/following{/other_user}","gists_url":"https://api.github.com/users/josmo/gists{/gist_id}","starred_url":"https://api.github.com/users/josmo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/josmo/subscriptions","organizations_url":"https://api.github.com/users/josmo/orgs","repos_url":"https://api.github.com/users/josmo/repos","events_url":"https://api.github.com/users/josmo/events{/privacy}","received_events_url":"https://api.github.com/users/josmo/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/josmo/drone","description":"Drone is a Continuous Delivery platform built on Docker, written in Go","fork":true,"url":"https://api.github.com/repos/josmo/drone","forks_url":"https://api.github.com/repos/josmo/drone/forks","keys_url":"https://api.github.com/repos/josmo/drone/keys{/key_id}","collaborators_url":"https://api.github.com/repos/josmo/drone/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/josmo/drone/teams","hooks_url":"https://api.github.com/repos/josmo/drone/hooks","issue_events_url":"https://api.github.com/repos/josmo/drone/issues/events{/number}","events_url":"https://api.github.com/repos/josmo/drone/events","assignees_url":"https://api.github.com/repos/josmo/drone/assignees{/user}","branches_url":"https://api.github.com/repos/josmo/drone/branches{/branch}","tags_url":"https://api.github.com/repos/josmo/drone/tags","blobs_url":"https://api.github.com/repos/josmo/drone/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/josmo/drone/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/josmo/drone/git/refs{/sha}","trees_url":"https://api.github.com/repos/josmo/drone/git/trees{/sha}","statuses_url":"https://api.github.com/repos/josmo/drone/statuses/{sha}","languages_url":"https://api.github.com/repos/josmo/drone/languages","stargazers_url":"https://api.github.com/repos/josmo/drone/stargazers","contributors_url":"https://api.github.com/repos/josmo/drone/contributors","subscribers_url":"https://api.github.com/repos/josmo/drone/subscribers","subscription_url":"https://api.github.com/repos/josmo/drone/subscription","commits_url":"https://api.github.com/repos/josmo/drone/commits{/sha}","git_commits_url":"https://api.github.com/repos/josmo/drone/git/commits{/sha}","comments_url":"https://api.github.com/repos/josmo/drone/comments{/number}","issue_comment_url":"https://api.github.com/repos/josmo/drone/issues/comments{/number}","contents_url":"https://api.github.com/repos/josmo/drone/contents/{+path}","compare_url":"https://api.github.com/repos/josmo/drone/compare/{base}...{head}","merges_url":"https://api.github.com/repos/josmo/drone/merges","archive_url":"https://api.github.com/repos/josmo/drone/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/josmo/drone/downloads","issues_url":"https://api.github.com/repos/josmo/drone/issues{/number}","pulls_url":"https://api.github.com/repos/josmo/drone/pulls{/number}","milestones_url":"https://api.github.com/repos/josmo/drone/milestones{/number}","notifications_url":"https://api.github.com/repos/josmo/drone/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/josmo/drone/labels{/name}","releases_url":"https://api.github.com/repos/josmo/drone/releases{/id}","deployments_url":"https://api.github.com/repos/josmo/drone/deployments","created_at":"2016-04-07T19:41:16Z","updated_at":"2016-04-07T19:41:17Z","pushed_at":"2016-04-19T05:34:09Z","git_url":"git://github.com/josmo/drone.git","ssh_url":"git@github.com:josmo/drone.git","clone_url":"https://github.com/josmo/drone.git","svn_url":"https://github.com/josmo/drone","homepage":"","size":19251,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"drone:master","ref":"master","sha":"b354b93658718c5c097309a7c98b5c7f5902048a","user":{"login":"drone","id":2181346,"avatar_url":"https://avatars.githubusercontent.com/u/2181346?v=3","gravatar_id":"","url":"https://api.github.com/users/drone","html_url":"https://github.com/drone","followers_url":"https://api.github.com/users/drone/followers","following_url":"https://api.github.com/users/drone/following{/other_user}","gists_url":"https://api.github.com/users/drone/gists{/gist_id}","starred_url":"https://api.github.com/users/drone/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/drone/subscriptions","organizations_url":"https://api.github.com/users/drone/orgs","repos_url":"https://api.github.com/users/drone/repos","events_url":"https://api.github.com/users/drone/events{/privacy}","received_events_url":"https://api.github.com/users/drone/received_events","type":"Organization","site_admin":false},"repo":{"id":16607898,"name":"drone","full_name":"drone/drone","owner":{"login":"drone","id":2181346,"avatar_url":"https://avatars.githubusercontent.com/u/2181346?v=3","gravatar_id":"","url":"https://api.github.com/users/drone","html_url":"https://github.com/drone","followers_url":"https://api.github.com/users/drone/followers","following_url":"https://api.github.com/users/drone/following{/other_user}","gists_url":"https://api.github.com/users/drone/gists{/gist_id}","starred_url":"https://api.github.com/users/drone/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/drone/subscriptions","organizations_url":"https://api.github.com/users/drone/orgs","repos_url":"https://api.github.com/users/drone/repos","events_url":"https://api.github.com/users/drone/events{/privacy}","received_events_url":"https://api.github.com/users/drone/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/drone/drone","description":"Drone is a Continuous Delivery platform built on Docker, written in Go","fork":false,"url":"https://api.github.com/repos/drone/drone","forks_url":"https://api.github.com/repos/drone/drone/forks","keys_url":"https://api.github.com/repos/drone/drone/keys{/key_id}","collaborators_url":"https://api.github.com/repos/drone/drone/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/drone/drone/teams","hooks_url":"https://api.github.com/repos/drone/drone/hooks","issue_events_url":"https://api.github.com/repos/drone/drone/issues/events{/number}","events_url":"https://api.github.com/repos/drone/drone/events","assignees_url":"https://api.github.com/repos/drone/drone/assignees{/user}","branches_url":"https://api.github.com/repos/drone/drone/branches{/branch}","tags_url":"https://api.github.com/repos/drone/drone/tags","blobs_url":"https://api.github.com/repos/drone/drone/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/drone/drone/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/drone/drone/git/refs{/sha}","trees_url":"https://api.github.com/repos/drone/drone/git/trees{/sha}","statuses_url":"https://api.github.com/repos/drone/drone/statuses/{sha}","languages_url":"https://api.github.com/repos/drone/drone/languages","stargazers_url":"https://api.github.com/repos/drone/drone/stargazers","contributors_url":"https://api.github.com/repos/drone/drone/contributors","subscribers_url":"https://api.github.com/repos/drone/drone/subscribers","subscription_url":"https://api.github.com/repos/drone/drone/subscription","commits_url":"https://api.github.com/repos/drone/drone/commits{/sha}","git_commits_url":"https://api.github.com/repos/drone/drone/git/commits{/sha}","comments_url":"https://api.github.com/repos/drone/drone/comments{/number}","issue_comment_url":"https://api.github.com/repos/drone/drone/issues/comments{/number}","contents_url":"https://api.github.com/repos/drone/drone/contents/{+path}","compare_url":"https://api.github.com/repos/drone/drone/compare/{base}...{head}","merges_url":"https://api.github.com/repos/drone/drone/merges","archive_url":"https://api.github.com/repos/drone/drone/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/drone/drone/downloads","issues_url":"https://api.github.com/repos/drone/drone/issues{/number}","pulls_url":"https://api.github.com/repos/drone/drone/pulls{/number}","milestones_url":"https://api.github.com/repos/drone/drone/milestones{/number}","notifications_url":"https://api.github.com/repos/drone/drone/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/drone/drone/labels{/name}","releases_url":"https://api.github.com/repos/drone/drone/releases{/id}","deployments_url":"https://api.github.com/repos/drone/drone/deployments","created_at":"2014-02-07T07:54:44Z","updated_at":"2016-04-19T05:46:45Z","pushed_at":"2016-04-19T05:48:16Z","git_url":"git://github.com/drone/drone.git","ssh_url":"git@github.com:drone/drone.git","clone_url":"https://github.com/drone/drone.git","svn_url":"https://github.com/drone/drone","homepage":"","size":29139,"stargazers_count":6666,"watchers_count":6666,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":762,"mirror_url":null,"open_issues_count":136,"forks":762,"open_issues":136,"watchers":6666,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/drone/drone/pulls/1581"},"html":{"href":"https://github.com/drone/drone/pull/1581"},"issue":{"href":"https://api.github.com/repos/drone/drone/issues/1581"},"comments":{"href":"https://api.github.com/repos/drone/drone/issues/1581/comments"},"review_comments":{"href":"https://api.github.com/repos/drone/drone/pulls/1581/comments"},"review_comment":{"href":"https://api.github.com/repos/drone/drone/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/drone/drone/pulls/1581/commits"},"statuses":{"href":"https://api.github.com/repos/drone/drone/statuses/df45c847630e4d54e4d0168364e460d31cf18153"}}}}