Pass down context.Context (#371)

* pass context down to remote clients

* make tests work

* add ctx to Refresh() and use it

* bitbucketserver

* code format

* plugin interface: add todo context

* solve todo

* RM TODO by using context.WithTimeout

* refactor & fix

* Apply suggestions from code review

Co-authored-by: Anbraten <anton@ju60.de>

* go fmt

* Update server/remote/coding/coding.go

Co-authored-by: Anbraten <anton@ju60.de>

Co-authored-by: Anbraten <anton@ju60.de>
This commit is contained in:
6543 2021-09-28 12:56:59 +02:00 committed by GitHub
parent c0888de86b
commit e3499f610d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 815 additions and 791 deletions

View file

@ -333,9 +333,9 @@ func PostApproval(c *gin.Context) {
for _, item := range buildItems { for _, item := range buildItems {
uri := fmt.Sprintf("%s/%s/%d", server.Config.Server.Host, repo.FullName, build.Number) uri := fmt.Sprintf("%s/%s/%d", server.Config.Server.Host, repo.FullName, build.Number)
if len(buildItems) > 1 { if len(buildItems) > 1 {
err = remote_.Status(user, repo, build, uri, item.Proc) err = remote_.Status(c, user, repo, build, uri, item.Proc)
} else { } else {
err = remote_.Status(user, repo, build, uri, nil) err = remote_.Status(c, user, repo, build, uri, nil)
} }
if err != nil { if err != nil {
logrus.Errorf("error setting commit status for %s/%d: %v", repo.FullName, build.Number, err) logrus.Errorf("error setting commit status for %s/%d: %v", repo.FullName, build.Number, err)
@ -373,7 +373,7 @@ func PostDecline(c *gin.Context) {
} }
uri := fmt.Sprintf("%s/%s/%d", server.Config.Server.Host, repo.FullName, build.Number) uri := fmt.Sprintf("%s/%s/%d", server.Config.Server.Host, repo.FullName, build.Number)
err = remote_.Status(user, repo, build, uri, nil) err = remote_.Status(c, user, repo, build, uri, nil)
if err != nil { if err != nil {
logrus.Errorf("error setting commit status for %s/%d: %v", repo.FullName, build.Number, err) logrus.Errorf("error setting commit status for %s/%d: %v", repo.FullName, build.Number, err)
} }
@ -426,7 +426,7 @@ func PostBuild(c *gin.Context) {
// may be stale. Therefore, we should refresh prior to dispatching // may be stale. Therefore, we should refresh prior to dispatching
// the job. // the job.
if refresher, ok := remote_.(remote.Refresher); ok { if refresher, ok := remote_.(remote.Refresher); ok {
ok, _ := refresher.Refresh(user) ok, _ := refresher.Refresh(c, user)
if ok { if ok {
store.UpdateUser(c, user) store.UpdateUser(c, user)
} }

View file

@ -155,7 +155,7 @@ func PostHook(c *gin.Context) {
// may be stale. Therefore, we should refresh prior to dispatching // may be stale. Therefore, we should refresh prior to dispatching
// the build. // the build.
if refresher, ok := remote_.(remote.Refresher); ok { if refresher, ok := remote_.(remote.Refresher); ok {
ok, err := refresher.Refresh(user) ok, err := refresher.Refresh(c, user)
if err != nil { if err != nil {
logrus.Errorf("failed to refresh oauth2 token: %s", err) logrus.Errorf("failed to refresh oauth2 token: %s", err)
} else if ok { } else if ok {
@ -168,7 +168,7 @@ func PostHook(c *gin.Context) {
// fetch the build file from the remote // fetch the build file from the remote
configFetcher := shared.NewConfigFetcher(remote_, user, repo, build) configFetcher := shared.NewConfigFetcher(remote_, user, repo, build)
remoteYamlConfigs, err := configFetcher.Fetch() remoteYamlConfigs, err := configFetcher.Fetch(c)
if err != nil { if err != nil {
logrus.Errorf("error: %s: cannot find %s in %s: %s", repo.FullName, repo.Config, build.Ref, err) logrus.Errorf("error: %s: cannot find %s in %s: %s", repo.FullName, repo.Config, build.Ref, err)
c.AbortWithError(404, err) c.AbortWithError(404, err)
@ -278,9 +278,9 @@ func PostHook(c *gin.Context) {
for _, item := range buildItems { for _, item := range buildItems {
uri := fmt.Sprintf("%s/%s/%d", server.Config.Server.Host, repo.FullName, build.Number) uri := fmt.Sprintf("%s/%s/%d", server.Config.Server.Host, repo.FullName, build.Number)
if len(buildItems) > 1 { if len(buildItems) > 1 {
err = remote_.Status(user, repo, build, uri, item.Proc) err = remote_.Status(c, user, repo, build, uri, item.Proc)
} else { } else {
err = remote_.Status(user, repo, build, uri, nil) err = remote_.Status(c, user, repo, build, uri, nil)
} }
if err != nil { if err != nil {
logrus.Errorf("error setting commit status for %s/%d: %v", repo.FullName, build.Number, err) logrus.Errorf("error setting commit status for %s/%d: %v", repo.FullName, build.Number, err)

View file

@ -75,13 +75,13 @@ func PostRepo(c *gin.Context) {
sig, sig,
) )
err = r.Activate(user, repo, link) err = r.Activate(c, user, repo, link)
if err != nil { if err != nil {
c.String(500, err.Error()) c.String(500, err.Error())
return return
} }
from, err := r.Repo(user, repo.Owner, repo.Name) from, err := r.Repo(c, user, repo.Owner, repo.Name)
if err == nil { if err == nil {
repo.Update(from) repo.Update(from)
} }
@ -187,7 +187,10 @@ func DeleteRepo(c *gin.Context) {
} }
} }
r.Deactivate(user, repo, server.Config.Server.Host) if err := r.Deactivate(c, user, repo, server.Config.Server.Host); err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
return
}
c.JSON(200, repo) c.JSON(200, repo)
} }
@ -212,14 +215,14 @@ func RepairRepo(c *gin.Context) {
sig, sig,
) )
r.Deactivate(user, repo, host) _ = r.Deactivate(c, user, repo, host)
err = r.Activate(user, repo, link) err = r.Activate(c, user, repo, link)
if err != nil { if err != nil {
c.String(500, err.Error()) c.String(500, err.Error())
return return
} }
from, err := r.Repo(user, repo.Owner, repo.Name) from, err := r.Repo(c, user, repo.Owner, repo.Name)
if err == nil { if err == nil {
repo.Name = from.Name repo.Name = from.Name
repo.Owner = from.Owner repo.Owner = from.Owner
@ -255,7 +258,7 @@ func MoveRepo(c *gin.Context) {
return return
} }
from, err := r.Repo(user, owner, name) from, err := r.Repo(c, user, owner, name)
if err != nil { if err != nil {
c.AbortWithError(http.StatusInternalServerError, err) c.AbortWithError(http.StatusInternalServerError, err)
return return
@ -298,8 +301,9 @@ func MoveRepo(c *gin.Context) {
sig, sig,
) )
r.Deactivate(user, repo, host) // TODO: check if we should handle that error
err = r.Activate(user, repo, link) r.Deactivate(c, user, repo, host)
err = r.Activate(c, user, repo, link)
if err != nil { if err != nil {
c.String(500, err.Error()) c.String(500, err.Error())
return return

View file

@ -54,7 +54,7 @@ func GetFeed(c *gin.Context) {
Perms: store.FromContext(c), Perms: store.FromContext(c),
Match: shared.NamespaceFilter(config.OwnersWhitelist), Match: shared.NamespaceFilter(config.OwnersWhitelist),
} }
if err := sync.Sync(user); err != nil { if err := sync.Sync(c, user); err != nil {
logrus.Debugf("sync error: %s: %s", user.Login, err) logrus.Debugf("sync error: %s: %s", user.Login, err)
} else { } else {
logrus.Debugf("sync complete: %s", user.Login) logrus.Debugf("sync complete: %s", user.Login)
@ -100,7 +100,7 @@ func GetRepos(c *gin.Context) {
Match: shared.NamespaceFilter(config.OwnersWhitelist), Match: shared.NamespaceFilter(config.OwnersWhitelist),
} }
if err := sync.Sync(user); err != nil { if err := sync.Sync(c, user); err != nil {
logrus.Debugf("sync error: %s: %s", user.Login, err) logrus.Debugf("sync error: %s: %s", user.Login, err)
} else { } else {
logrus.Debugf("sync complete: %s", user.Login) logrus.Debugf("sync complete: %s", user.Login)

View file

@ -338,12 +338,12 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error {
} }
if !isMultiPipeline(procs) { if !isMultiPipeline(procs) {
s.updateRemoteStatus(repo, build, nil) s.updateRemoteStatus(c, repo, build, nil)
} }
} }
if isMultiPipeline(procs) { if isMultiPipeline(procs) {
s.updateRemoteStatus(repo, build, proc) s.updateRemoteStatus(c, repo, build, proc)
} }
if err := s.logger.Close(c, id); err != nil { if err := s.logger.Close(c, id); err != nil {
@ -416,17 +416,17 @@ func buildStatus(procs []*model.Proc) string {
return status return status
} }
func (s *RPC) updateRemoteStatus(repo *model.Repo, build *model.Build, proc *model.Proc) { func (s *RPC) updateRemoteStatus(ctx context.Context, repo *model.Repo, build *model.Build, proc *model.Proc) {
user, err := s.store.GetUser(repo.UserID) user, err := s.store.GetUser(repo.UserID)
if err == nil { if err == nil {
if refresher, ok := s.remote.(remote.Refresher); ok { if refresher, ok := s.remote.(remote.Refresher); ok {
ok, _ := refresher.Refresh(user) ok, _ := refresher.Refresh(ctx, user)
if ok { if ok {
s.store.UpdateUser(user) s.store.UpdateUser(user)
} }
} }
uri := fmt.Sprintf("%s/%s/%d", server.Config.Server.Host, repo.FullName, build.Number) uri := fmt.Sprintf("%s/%s/%d", server.Config.Server.Host, repo.FullName, build.Number)
err = s.remote.Status(user, repo, build, uri, proc) err = s.remote.Status(ctx, user, repo, build, uri, proc)
if err != nil { if err != nil {
logrus.Errorf("error setting commit status for %s/%d: %v", repo.FullName, build.Number, err) logrus.Errorf("error setting commit status for %s/%d: %v", repo.FullName, build.Number, err)
} }

View file

@ -9,13 +9,13 @@ type builtin struct {
globals []*model.Environ globals []*model.Environ
} }
// New returns a new local registry service. // Filesystem returns a new local registry service.
func Filesystem(params []string) model.EnvironService { func Filesystem(params []string) model.EnvironService {
var globals []*model.Environ var globals []*model.Environ
for _, item := range params { for _, item := range params {
kvpair := strings.SplitN(item, ":", 2) kvPair := strings.SplitN(item, ":", 2)
globals = append(globals, &model.Environ{Name: kvpair[0], Value: kvpair[1]}) globals = append(globals, &model.Environ{Name: kvPair[0], Value: kvPair[1]})
} }
return &builtin{globals} return &builtin{globals}
} }

View file

@ -2,6 +2,7 @@ package internal
import ( import (
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"io" "io"
"io/ioutil" "io/ioutil"
@ -11,7 +12,7 @@ import (
// Send makes an http request to the given endpoint, writing the input // Send makes an http request to the given endpoint, writing the input
// to the request body and unmarshaling the output from the response body. // to the request body and unmarshaling the output from the response body.
func Send(method, path string, in, out interface{}) error { func Send(ctx context.Context, method, path string, in, out interface{}) error {
uri, err := url.Parse(path) uri, err := url.Parse(path)
if err != nil { if err != nil {
return err return err
@ -29,7 +30,7 @@ func Send(method, path string, in, out interface{}) error {
} }
// creates a new http request to bitbucket. // creates a new http request to bitbucket.
req, err := http.NewRequest(method, uri.String(), buf) req, err := http.NewRequestWithContext(ctx, method, uri.String(), buf)
if err != nil { if err != nil {
return err return err
} }

View file

@ -1,6 +1,7 @@
package sender package sender
import ( import (
"context"
"fmt" "fmt"
"github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/model"
@ -22,7 +23,7 @@ func (p *plugin) SenderAllowed(user *model.User, repo *model.Repo, build *model.
"build": build, "build": build,
"config": conf, "config": conf,
} }
err := internal.Send("POST", path, &data, nil) err := internal.Send(context.TODO(), "POST", path, &data, nil)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -31,21 +32,21 @@ func (p *plugin) SenderAllowed(user *model.User, repo *model.Repo, build *model.
func (p *plugin) SenderCreate(repo *model.Repo, sender *model.Sender) error { func (p *plugin) SenderCreate(repo *model.Repo, sender *model.Sender) error {
path := fmt.Sprintf("%s/senders/%s/%s", p.endpoint, repo.Owner, repo.Name) path := fmt.Sprintf("%s/senders/%s/%s", p.endpoint, repo.Owner, repo.Name)
return internal.Send("POST", path, sender, nil) return internal.Send(context.TODO(), "POST", path, sender, nil)
} }
func (p *plugin) SenderUpdate(repo *model.Repo, sender *model.Sender) error { func (p *plugin) SenderUpdate(repo *model.Repo, sender *model.Sender) error {
path := fmt.Sprintf("%s/senders/%s/%s", p.endpoint, repo.Owner, repo.Name) path := fmt.Sprintf("%s/senders/%s/%s", p.endpoint, repo.Owner, repo.Name)
return internal.Send("PUT", path, sender, nil) return internal.Send(context.TODO(), "PUT", path, sender, nil)
} }
func (p *plugin) SenderDelete(repo *model.Repo, login string) error { func (p *plugin) SenderDelete(repo *model.Repo, login string) error {
path := fmt.Sprintf("%s/senders/%s/%s/%s", p.endpoint, repo.Owner, repo.Name, login) path := fmt.Sprintf("%s/senders/%s/%s/%s", p.endpoint, repo.Owner, repo.Name, login)
return internal.Send("DELETE", path, nil, nil) return internal.Send(context.TODO(), "DELETE", path, nil, nil)
} }
func (p *plugin) SenderList(repo *model.Repo) (out []*model.Sender, err error) { func (p *plugin) SenderList(repo *model.Repo) (out []*model.Sender, err error) {
path := fmt.Sprintf("%s/senders/%s/%s", p.endpoint, repo.Owner, repo.Name) path := fmt.Sprintf("%s/senders/%s/%s", p.endpoint, repo.Owner, repo.Name)
err = internal.Send("GET", path, nil, out) err = internal.Send(context.TODO(), "GET", path, nil, out)
return out, err return out, err
} }

View file

@ -134,7 +134,7 @@ type Queue interface {
// Push pushes a task to the tail of this queue. // Push pushes a task to the tail of this queue.
Push(c context.Context, task *Task) error Push(c context.Context, task *Task) error
// Push pushes a task to the tail of this queue. // PushAtOnce pushes a task to the tail of this queue.
PushAtOnce(c context.Context, tasks []*Task) error PushAtOnce(c context.Context, tasks []*Task) error
// Poll retrieves and removes a task head of this queue. // Poll retrieves and removes a task head of this queue.
@ -149,13 +149,13 @@ type Queue interface {
// Error signals the task is complete with errors. // Error signals the task is complete with errors.
Error(c context.Context, id string, err error) error Error(c context.Context, id string, err error) error
// Error signals the task is complete with errors. // ErrorAtOnce signals the task is complete with errors.
ErrorAtOnce(c context.Context, id []string, err error) error ErrorAtOnce(c context.Context, id []string, err error) error
// Evict removes a pending task from the queue. // Evict removes a pending task from the queue.
Evict(c context.Context, id string) error Evict(c context.Context, id string) error
// Evict removes a pending task from the queue. // EvictAtOnce removes a pending task from the queue.
EvictAtOnce(c context.Context, id []string) error EvictAtOnce(c context.Context, id []string) error
// Wait waits until the task is complete. // Wait waits until the task is complete.
@ -164,9 +164,9 @@ type Queue interface {
// Info returns internal queue information. // Info returns internal queue information.
Info(c context.Context) InfoT Info(c context.Context) InfoT
// Stops the queue from handing out new work items in Poll // Pause stops the queue from handing out new work items in Poll
Pause() Pause()
// Starts the queue again, Poll returns new items // Resume starts the queue again, Poll returns new items
Resume() Resume()
} }

View file

@ -15,6 +15,7 @@
package bitbucket package bitbucket
import ( import (
"context"
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
@ -53,7 +54,7 @@ func New(client, secret string) remote.Remote {
// Login authenticates an account with Bitbucket using the oauth2 protocol. The // Login authenticates an account with Bitbucket using the oauth2 protocol. The
// Bitbucket account details are returned when the user is successfully authenticated. // Bitbucket account details are returned when the user is successfully authenticated.
func (c *config) Login(w http.ResponseWriter, req *http.Request) (*model.User, error) { func (c *config) Login(ctx context.Context, w http.ResponseWriter, req *http.Request) (*model.User, error) {
config := c.newConfig(server.Config.Server.Host) config := c.newConfig(server.Config.Server.Host)
// get the OAuth errors // get the OAuth errors
@ -72,12 +73,12 @@ func (c *config) Login(w http.ResponseWriter, req *http.Request) (*model.User, e
return nil, nil return nil, nil
} }
token, err := config.Exchange(oauth2.NoContext, code) token, err := config.Exchange(ctx, code)
if err != nil { if err != nil {
return nil, err return nil, err
} }
client := internal.NewClient(c.API, config.Client(oauth2.NoContext, token)) client := internal.NewClient(ctx, c.API, config.Client(ctx, token))
curr, err := client.FindCurrent() curr, err := client.FindCurrent()
if err != nil { if err != nil {
return nil, err return nil, err
@ -87,8 +88,8 @@ func (c *config) Login(w http.ResponseWriter, req *http.Request) (*model.User, e
// Auth uses the Bitbucket oauth2 access token and refresh token to authenticate // Auth uses the Bitbucket oauth2 access token and refresh token to authenticate
// a session and return the Bitbucket account login. // a session and return the Bitbucket account login.
func (c *config) Auth(token, secret string) (string, error) { func (c *config) Auth(ctx context.Context, token, secret string) (string, error) {
client := c.newClientToken(token, secret) client := c.newClientToken(ctx, token, secret)
user, err := client.FindCurrent() user, err := client.FindCurrent()
if err != nil { if err != nil {
return "", err return "", err
@ -98,10 +99,10 @@ func (c *config) Auth(token, secret string) (string, error) {
// Refresh refreshes the Bitbucket oauth2 access token. If the token is // Refresh refreshes the Bitbucket oauth2 access token. If the token is
// refreshed the user is updated and a true value is returned. // refreshed the user is updated and a true value is returned.
func (c *config) Refresh(user *model.User) (bool, error) { func (c *config) Refresh(ctx context.Context, user *model.User) (bool, error) {
config := c.newConfig("") config := c.newConfig("")
source := config.TokenSource( source := config.TokenSource(
oauth2.NoContext, &oauth2.Token{RefreshToken: user.Secret}) ctx, &oauth2.Token{RefreshToken: user.Secret})
token, err := source.Token() token, err := source.Token()
if err != nil || len(token.AccessToken) == 0 { if err != nil || len(token.AccessToken) == 0 {
@ -115,12 +116,12 @@ func (c *config) Refresh(user *model.User) (bool, error) {
} }
// Teams returns a list of all team membership for the Bitbucket account. // Teams returns a list of all team membership for the Bitbucket account.
func (c *config) Teams(u *model.User) ([]*model.Team, error) { func (c *config) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) {
opts := &internal.ListTeamOpts{ opts := &internal.ListTeamOpts{
PageLen: 100, PageLen: 100,
Role: "member", Role: "member",
} }
resp, err := c.newClient(u).ListTeams(opts) resp, err := c.newClient(ctx, u).ListTeams(opts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -128,8 +129,8 @@ func (c *config) Teams(u *model.User) ([]*model.Team, error) {
} }
// Repo returns the named Bitbucket repository. // Repo returns the named Bitbucket repository.
func (c *config) Repo(u *model.User, owner, name string) (*model.Repo, error) { func (c *config) Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error) {
repo, err := c.newClient(u).FindRepo(owner, name) repo, err := c.newClient(ctx, u).FindRepo(owner, name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -138,8 +139,8 @@ func (c *config) Repo(u *model.User, owner, name string) (*model.Repo, error) {
// Repos returns a list of all repositories for Bitbucket account, including // Repos returns a list of all repositories for Bitbucket account, including
// organization repositories. // organization repositories.
func (c *config) Repos(u *model.User) ([]*model.Repo, error) { func (c *config) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) {
client := c.newClient(u) client := c.newClient(ctx, u)
var all []*model.Repo var all []*model.Repo
@ -171,8 +172,8 @@ func (c *config) Repos(u *model.User) ([]*model.Repo, error) {
// does not have an endpoint to access user permissions, we attempt to fetch // does not have an endpoint to access user permissions, we attempt to fetch
// the repository hook list, which is restricted to administrators to calculate // the repository hook list, which is restricted to administrators to calculate
// administrative access to a repository. // administrative access to a repository.
func (c *config) Perm(u *model.User, owner, name string) (*model.Perm, error) { func (c *config) Perm(ctx context.Context, u *model.User, owner, name string) (*model.Perm, error) {
client := c.newClient(u) client := c.newClient(ctx, u)
perms := new(model.Perm) perms := new(model.Perm)
repo, err := client.FindRepo(owner, name) repo, err := client.FindRepo(owner, name)
@ -200,40 +201,40 @@ func (c *config) Perm(u *model.User, owner, name string) (*model.Perm, error) {
} }
// File fetches the file from the Bitbucket repository and returns its contents. // File fetches the file from the Bitbucket repository and returns its contents.
func (c *config) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { func (c *config) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
config, err := c.newClient(u).FindSource(r.Owner, r.Name, b.Commit, f) config, err := c.newClient(ctx, u).FindSource(r.Owner, r.Name, b.Commit, f)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return []byte(*config), err return []byte(*config), err
} }
func (c *config) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { func (c *config) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) {
return nil, fmt.Errorf("Not implemented") return nil, fmt.Errorf("Not implemented")
} }
// Status creates a build status for the Bitbucket commit. // Status creates a build status for the Bitbucket commit.
func (c *config) Status(u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { func (c *config) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
status := internal.BuildStatus{ status := internal.BuildStatus{
State: convertStatus(b.Status), State: convertStatus(b.Status),
Desc: convertDesc(b.Status), Desc: convertDesc(b.Status),
Key: "Drone", Key: "Drone",
Url: link, Url: link,
} }
return c.newClient(u).CreateStatus(r.Owner, r.Name, b.Commit, &status) return c.newClient(ctx, u).CreateStatus(r.Owner, r.Name, b.Commit, &status)
} }
// Activate activates the repository by registering repository push hooks with // Activate activates the repository by registering repository push hooks with
// the Bitbucket repository. Prior to registering hook, previously created hooks // the Bitbucket repository. Prior to registering hook, previously created hooks
// are deleted. // are deleted.
func (c *config) Activate(u *model.User, r *model.Repo, link string) error { func (c *config) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
rawurl, err := url.Parse(link) rawurl, err := url.Parse(link)
if err != nil { if err != nil {
return err return err
} }
c.Deactivate(u, r, link) c.Deactivate(ctx, u, r, link)
return c.newClient(u).CreateHook(r.Owner, r.Name, &internal.Hook{ return c.newClient(ctx, u).CreateHook(r.Owner, r.Name, &internal.Hook{
Active: true, Active: true,
Desc: rawurl.Host, Desc: rawurl.Host,
Events: []string{"repo:push"}, Events: []string{"repo:push"},
@ -243,8 +244,8 @@ func (c *config) Activate(u *model.User, r *model.Repo, link string) error {
// Deactivate deactives the repository be removing repository push hooks from // Deactivate deactives the repository be removing repository push hooks from
// the Bitbucket repository. // the Bitbucket repository.
func (c *config) Deactivate(u *model.User, r *model.Repo, link string) error { func (c *config) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
client := c.newClient(u) client := c.newClient(ctx, u)
hooks, err := client.ListHooks(r.Owner, r.Name, &internal.ListOpts{}) hooks, err := client.ListHooks(r.Owner, r.Name, &internal.ListOpts{})
if err != nil { if err != nil {
@ -274,13 +275,14 @@ func (c *config) Hook(req *http.Request) (*model.Repo, *model.Build, error) {
} }
// helper function to return the bitbucket oauth2 client // helper function to return the bitbucket oauth2 client
func (c *config) newClient(u *model.User) *internal.Client { func (c *config) newClient(ctx context.Context, u *model.User) *internal.Client {
return c.newClientToken(u.Token, u.Secret) return c.newClientToken(ctx, u.Token, u.Secret)
} }
// helper function to return the bitbucket oauth2 client // helper function to return the bitbucket oauth2 client
func (c *config) newClientToken(token, secret string) *internal.Client { func (c *config) newClientToken(ctx context.Context, token, secret string) *internal.Client {
return internal.NewClientToken( return internal.NewClientToken(
ctx,
c.API, c.API,
c.Client, c.Client,
c.Secret, c.Secret,

View file

@ -16,6 +16,7 @@ package bitbucket
import ( import (
"bytes" "bytes"
"context"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@ -35,6 +36,7 @@ func Test_bitbucket(t *testing.T) {
c := &config{URL: s.URL, API: s.URL} c := &config{URL: s.URL, API: s.URL}
g := goblin.Goblin(t) g := goblin.Goblin(t)
ctx := context.Background()
g.Describe("Bitbucket client", func() { g.Describe("Bitbucket client", func() {
g.After(func() { g.After(func() {
@ -61,13 +63,13 @@ func Test_bitbucket(t *testing.T) {
g.It("Should redirect to authorize", func() { g.It("Should redirect to authorize", func() {
w := httptest.NewRecorder() w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", "", nil) r, _ := http.NewRequest("GET", "", nil)
_, err := c.Login(w, r) _, err := c.Login(ctx, w, r)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(w.Code).Equal(http.StatusSeeOther) g.Assert(w.Code).Equal(http.StatusSeeOther)
}) })
g.It("Should return authenticated user", func() { g.It("Should return authenticated user", func() {
r, _ := http.NewRequest("GET", "?code=code", nil) r, _ := http.NewRequest("GET", "?code=code", nil)
u, err := c.Login(nil, r) u, err := c.Login(ctx, nil, r)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(u.Login).Equal(fakeUser.Login) g.Assert(u.Login).Equal(fakeUser.Login)
g.Assert(u.Token).Equal("2YotnFZFEjr1zCsicMWpAA") g.Assert(u.Token).Equal("2YotnFZFEjr1zCsicMWpAA")
@ -76,54 +78,48 @@ func Test_bitbucket(t *testing.T) {
g.It("Should handle failure to exchange code", func() { g.It("Should handle failure to exchange code", func() {
w := httptest.NewRecorder() w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", "?code=code_bad_request", nil) r, _ := http.NewRequest("GET", "?code=code_bad_request", nil)
_, err := c.Login(w, r) _, err := c.Login(ctx, w, r)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
g.It("Should handle failure to resolve user", func() { g.It("Should handle failure to resolve user", func() {
r, _ := http.NewRequest("GET", "?code=code_user_not_found", nil) r, _ := http.NewRequest("GET", "?code=code_user_not_found", nil)
_, err := c.Login(nil, r) _, err := c.Login(ctx, nil, r)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
g.It("Should handle authentication errors", func() { g.It("Should handle authentication errors", func() {
r, _ := http.NewRequest("GET", "?error=invalid_scope", nil) r, _ := http.NewRequest("GET", "?error=invalid_scope", nil)
_, err := c.Login(nil, r) _, err := c.Login(ctx, nil, r)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
}) })
g.Describe("Given an access token", func() { g.Describe("Given an access token", func() {
g.It("Should return the authenticated user", func() { g.It("Should return the authenticated user", func() {
login, err := c.Auth( login, err := c.Auth(ctx, fakeUser.Token, fakeUser.Secret)
fakeUser.Token,
fakeUser.Secret,
)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(login).Equal(fakeUser.Login) g.Assert(login).Equal(fakeUser.Login)
}) })
g.It("Should handle a failure to resolve user", func() { g.It("Should handle a failure to resolve user", func() {
_, err := c.Auth( _, err := c.Auth(ctx, fakeUserNotFound.Token, fakeUserNotFound.Secret)
fakeUserNotFound.Token,
fakeUserNotFound.Secret,
)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
}) })
g.Describe("Given a refresh token", func() { g.Describe("Given a refresh token", func() {
g.It("Should return a refresh access token", func() { g.It("Should return a refresh access token", func() {
ok, err := c.Refresh(fakeUserRefresh) ok, err := c.Refresh(ctx, fakeUserRefresh)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(ok).IsTrue() g.Assert(ok).IsTrue()
g.Assert(fakeUserRefresh.Token).Equal("2YotnFZFEjr1zCsicMWpAA") g.Assert(fakeUserRefresh.Token).Equal("2YotnFZFEjr1zCsicMWpAA")
g.Assert(fakeUserRefresh.Secret).Equal("tGzv3JOkF0XG5Qx2TlKWIA") g.Assert(fakeUserRefresh.Secret).Equal("tGzv3JOkF0XG5Qx2TlKWIA")
}) })
g.It("Should handle an empty access token", func() { g.It("Should handle an empty access token", func() {
ok, err := c.Refresh(fakeUserRefreshEmpty) ok, err := c.Refresh(ctx, fakeUserRefreshEmpty)
g.Assert(err == nil).IsFalse() g.Assert(err == nil).IsFalse()
g.Assert(ok).IsFalse() g.Assert(ok).IsFalse()
}) })
g.It("Should handle a failure to refresh", func() { g.It("Should handle a failure to refresh", func() {
ok, err := c.Refresh(fakeUserRefreshFail) ok, err := c.Refresh(ctx, fakeUserRefreshFail)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
g.Assert(ok).IsFalse() g.Assert(ok).IsFalse()
}) })
@ -131,61 +127,37 @@ func Test_bitbucket(t *testing.T) {
g.Describe("When requesting a repository", func() { g.Describe("When requesting a repository", func() {
g.It("Should return the details", func() { g.It("Should return the details", func() {
repo, err := c.Repo( repo, err := c.Repo(ctx, fakeUser, fakeRepo.Owner, fakeRepo.Name)
fakeUser,
fakeRepo.Owner,
fakeRepo.Name,
)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(repo.FullName).Equal(fakeRepo.FullName) g.Assert(repo.FullName).Equal(fakeRepo.FullName)
}) })
g.It("Should handle not found errors", func() { g.It("Should handle not found errors", func() {
_, err := c.Repo( _, err := c.Repo(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name)
fakeUser,
fakeRepoNotFound.Owner,
fakeRepoNotFound.Name,
)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
}) })
g.Describe("When requesting repository permissions", func() { g.Describe("When requesting repository permissions", func() {
g.It("Should handle not found errors", func() { g.It("Should handle not found errors", func() {
_, err := c.Perm( _, err := c.Perm(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name)
fakeUser,
fakeRepoNotFound.Owner,
fakeRepoNotFound.Name,
)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
g.It("Should authorize read access", func() { g.It("Should authorize read access", func() {
perm, err := c.Perm( perm, err := c.Perm(ctx, fakeUser, fakeRepoReadOnly.Owner, fakeRepoReadOnly.Name)
fakeUser,
fakeRepoReadOnly.Owner,
fakeRepoReadOnly.Name,
)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(perm.Pull).IsTrue() g.Assert(perm.Pull).IsTrue()
g.Assert(perm.Push).IsFalse() g.Assert(perm.Push).IsFalse()
g.Assert(perm.Admin).IsFalse() g.Assert(perm.Admin).IsFalse()
}) })
g.It("Should authorize write access", func() { g.It("Should authorize write access", func() {
perm, err := c.Perm( perm, err := c.Perm(ctx, fakeUser, fakeRepoWriteOnly.Owner, fakeRepoWriteOnly.Name)
fakeUser,
fakeRepoWriteOnly.Owner,
fakeRepoWriteOnly.Name,
)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(perm.Pull).IsTrue() g.Assert(perm.Pull).IsTrue()
g.Assert(perm.Push).IsTrue() g.Assert(perm.Push).IsTrue()
g.Assert(perm.Admin).IsFalse() g.Assert(perm.Admin).IsFalse()
}) })
g.It("Should authorize admin access", func() { g.It("Should authorize admin access", func() {
perm, err := c.Perm( perm, err := c.Perm(ctx, fakeUser, fakeRepoAdmin.Owner, fakeRepoAdmin.Name)
fakeUser,
fakeRepoAdmin.Owner,
fakeRepoAdmin.Name,
)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(perm.Pull).IsTrue() g.Assert(perm.Pull).IsTrue()
g.Assert(perm.Push).IsTrue() g.Assert(perm.Push).IsTrue()
@ -195,67 +167,67 @@ func Test_bitbucket(t *testing.T) {
g.Describe("When requesting user repositories", func() { g.Describe("When requesting user repositories", func() {
g.It("Should return the details", func() { g.It("Should return the details", func() {
repos, err := c.Repos(fakeUser) repos, err := c.Repos(ctx, fakeUser)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(repos[0].FullName).Equal(fakeRepo.FullName) g.Assert(repos[0].FullName).Equal(fakeRepo.FullName)
}) })
g.It("Should handle organization not found errors", func() { g.It("Should handle organization not found errors", func() {
_, err := c.Repos(fakeUserNoTeams) _, err := c.Repos(ctx, fakeUserNoTeams)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
g.It("Should handle not found errors", func() { g.It("Should handle not found errors", func() {
_, err := c.Repos(fakeUserNoRepos) _, err := c.Repos(ctx, fakeUserNoRepos)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
}) })
g.Describe("When requesting user teams", func() { g.Describe("When requesting user teams", func() {
g.It("Should return the details", func() { g.It("Should return the details", func() {
teams, err := c.Teams(fakeUser) teams, err := c.Teams(ctx, fakeUser)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(teams[0].Login).Equal("superfriends") g.Assert(teams[0].Login).Equal("superfriends")
g.Assert(teams[0].Avatar).Equal("http://i.imgur.com/ZygP55A.jpg") g.Assert(teams[0].Avatar).Equal("http://i.imgur.com/ZygP55A.jpg")
}) })
g.It("Should handle not found error", func() { g.It("Should handle not found error", func() {
_, err := c.Teams(fakeUserNoTeams) _, err := c.Teams(ctx, fakeUserNoTeams)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
}) })
g.Describe("When downloading a file", func() { g.Describe("When downloading a file", func() {
g.It("Should return the bytes", func() { g.It("Should return the bytes", func() {
raw, err := c.File(fakeUser, fakeRepo, fakeBuild, "file") raw, err := c.File(ctx, fakeUser, fakeRepo, fakeBuild, "file")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(len(raw) != 0).IsTrue() g.Assert(len(raw) != 0).IsTrue()
}) })
g.It("Should handle not found error", func() { g.It("Should handle not found error", func() {
_, err := c.File(fakeUser, fakeRepo, fakeBuild, "file_not_found") _, err := c.File(ctx, fakeUser, fakeRepo, fakeBuild, "file_not_found")
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
}) })
g.Describe("When activating a repository", func() { g.Describe("When activating a repository", func() {
g.It("Should error when malformed hook", func() { g.It("Should error when malformed hook", func() {
err := c.Activate(fakeUser, fakeRepo, "%gh&%ij") err := c.Activate(ctx, fakeUser, fakeRepo, "%gh&%ij")
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
g.It("Should create the hook", func() { g.It("Should create the hook", func() {
err := c.Activate(fakeUser, fakeRepo, "http://127.0.0.1") err := c.Activate(ctx, fakeUser, fakeRepo, "http://127.0.0.1")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
}) })
}) })
g.Describe("When deactivating a repository", func() { g.Describe("When deactivating a repository", func() {
g.It("Should error when listing hooks fails", func() { g.It("Should error when listing hooks fails", func() {
err := c.Deactivate(fakeUser, fakeRepoNoHooks, "http://127.0.0.1") err := c.Deactivate(ctx, fakeUser, fakeRepoNoHooks, "http://127.0.0.1")
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
g.It("Should successfully remove hooks", func() { g.It("Should successfully remove hooks", func() {
err := c.Deactivate(fakeUser, fakeRepo, "http://127.0.0.1") err := c.Deactivate(ctx, fakeUser, fakeRepo, "http://127.0.0.1")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
}) })
g.It("Should successfully deactivate when hook already removed", func() { g.It("Should successfully deactivate when hook already removed", func() {
err := c.Deactivate(fakeUser, fakeRepoEmptyHook, "http://127.0.0.1") err := c.Deactivate(ctx, fakeUser, fakeRepoEmptyHook, "http://127.0.0.1")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
}) })
}) })
@ -283,7 +255,7 @@ func Test_bitbucket(t *testing.T) {
}) })
g.It("Should update the status", func() { g.It("Should update the status", func() {
err := c.Status(fakeUser, fakeRepo, fakeBuild, "http://127.0.0.1", nil) err := c.Status(ctx, fakeUser, fakeRepo, fakeBuild, "http://127.0.0.1", nil)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
}) })

View file

@ -16,6 +16,7 @@ package internal
import ( import (
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@ -50,19 +51,24 @@ const (
type Client struct { type Client struct {
*http.Client *http.Client
base string base string
ctx context.Context
} }
func NewClient(url string, client *http.Client) *Client { func NewClient(ctx context.Context, url string, client *http.Client) *Client {
return &Client{client, url} return &Client{
Client: client,
base: url,
ctx: ctx,
}
} }
func NewClientToken(url, client, secret string, token *oauth2.Token) *Client { func NewClientToken(ctx context.Context, url, client, secret string, token *oauth2.Token) *Client {
config := &oauth2.Config{ config := &oauth2.Config{
ClientID: client, ClientID: client,
ClientSecret: secret, ClientSecret: secret,
Endpoint: bitbucket.Endpoint, Endpoint: bitbucket.Endpoint,
} }
return NewClient(url, config.Client(oauth2.NoContext, token)) return NewClient(ctx, url, config.Client(ctx, token))
} }
func (c *Client) FindCurrent() (*Account, error) { func (c *Client) FindCurrent() (*Account, error) {
@ -172,7 +178,6 @@ func (c *Client) GetPermission(fullName string) (*RepoPerm, error) {
} }
func (c *Client) do(rawurl, method string, in, out interface{}) (*string, error) { func (c *Client) do(rawurl, method string, in, out interface{}) (*string, error) {
uri, err := url.Parse(rawurl) uri, err := url.Parse(rawurl)
if err != nil { if err != nil {
return nil, err return nil, err
@ -190,7 +195,7 @@ func (c *Client) do(rawurl, method string, in, out interface{}) (*string, error)
} }
// creates a new http request to bitbucket. // creates a new http request to bitbucket.
req, err := http.NewRequest(method, uri.String(), buf) req, err := http.NewRequestWithContext(c.ctx, method, uri.String(), buf)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -18,6 +18,7 @@ package bitbucketserver
// quality or security standards expected of this project. Please use with caution. // quality or security standards expected of this project. Please use with caution.
import ( import (
"context"
"crypto/rsa" "crypto/rsa"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
@ -103,7 +104,7 @@ func New(opts Opts) (remote.Remote, error) {
return config, nil return config, nil
} }
func (c *Config) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) { func (c *Config) Login(ctx context.Context, res http.ResponseWriter, req *http.Request) (*model.User, error) {
requestToken, u, err := c.Consumer.GetRequestTokenAndUrl("oob") requestToken, u, err := c.Consumer.GetRequestTokenAndUrl("oob")
if err != nil { if err != nil {
return nil, err return nil, err
@ -119,7 +120,7 @@ func (c *Config) Login(res http.ResponseWriter, req *http.Request) (*model.User,
return nil, err return nil, err
} }
client := internal.NewClientWithToken(c.URL, c.Consumer, accessToken.Token) client := internal.NewClientWithToken(ctx, c.URL, c.Consumer, accessToken.Token)
user, err := client.FindCurrentUser() user, err := client.FindCurrentUser()
if err != nil { if err != nil {
@ -131,12 +132,12 @@ func (c *Config) Login(res http.ResponseWriter, req *http.Request) (*model.User,
} }
// Auth is not supported by the Stash driver. // Auth is not supported by the Stash driver.
func (*Config) Auth(token, secret string) (string, error) { func (*Config) Auth(ctx context.Context, token, secret string) (string, error) {
return "", fmt.Errorf("Not Implemented") return "", fmt.Errorf("Not Implemented")
} }
// Teams is not supported by the Stash driver. // Teams is not supported by the Stash driver.
func (*Config) Teams(u *model.User) ([]*model.Team, error) { func (*Config) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) {
var teams []*model.Team var teams []*model.Team
return teams, nil return teams, nil
} }
@ -146,16 +147,16 @@ func (*Config) TeamPerm(u *model.User, org string) (*model.Perm, error) {
return nil, nil return nil, nil
} }
func (c *Config) Repo(u *model.User, owner, name string) (*model.Repo, error) { func (c *Config) Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error) {
repo, err := internal.NewClientWithToken(c.URL, c.Consumer, u.Token).FindRepo(owner, name) repo, err := internal.NewClientWithToken(ctx, c.URL, c.Consumer, u.Token).FindRepo(owner, name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return convertRepo(repo), nil return convertRepo(repo), nil
} }
func (c *Config) Repos(u *model.User) ([]*model.Repo, error) { func (c *Config) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) {
repos, err := internal.NewClientWithToken(c.URL, c.Consumer, u.Token).FindRepos() repos, err := internal.NewClientWithToken(ctx, c.URL, c.Consumer, u.Token).FindRepos()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -167,24 +168,24 @@ func (c *Config) Repos(u *model.User) ([]*model.Repo, error) {
return all, nil return all, nil
} }
func (c *Config) Perm(u *model.User, owner, repo string) (*model.Perm, error) { func (c *Config) Perm(ctx context.Context, u *model.User, owner, repo string) (*model.Perm, error) {
client := internal.NewClientWithToken(c.URL, c.Consumer, u.Token) client := internal.NewClientWithToken(ctx, c.URL, c.Consumer, u.Token)
return client.FindRepoPerms(owner, repo) return client.FindRepoPerms(owner, repo)
} }
func (c *Config) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { func (c *Config) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
client := internal.NewClientWithToken(c.URL, c.Consumer, u.Token) client := internal.NewClientWithToken(ctx, c.URL, c.Consumer, u.Token)
return client.FindFileForRepo(r.Owner, r.Name, f, b.Ref) return client.FindFileForRepo(r.Owner, r.Name, f, b.Ref)
} }
func (c *Config) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { func (c *Config) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) {
return nil, fmt.Errorf("Not implemented") return nil, fmt.Errorf("Not implemented")
} }
// Status is not supported by the bitbucketserver driver. // Status is not supported by the bitbucketserver driver.
func (c *Config) Status(u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { func (c *Config) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
status := internal.BuildStatus{ status := internal.BuildStatus{
State: convertStatus(b.Status), State: convertStatus(b.Status),
Desc: convertDesc(b.Status), Desc: convertDesc(b.Status),
@ -193,7 +194,7 @@ func (c *Config) Status(u *model.User, r *model.Repo, b *model.Build, link strin
Url: link, Url: link,
} }
client := internal.NewClientWithToken(c.URL, c.Consumer, u.Token) client := internal.NewClientWithToken(ctx, c.URL, c.Consumer, u.Token)
return client.CreateStatus(b.Commit, &status) return client.CreateStatus(b.Commit, &status)
} }
@ -217,14 +218,14 @@ func (c *Config) Netrc(user *model.User, r *model.Repo) (*model.Netrc, error) {
}, nil }, nil
} }
func (c *Config) Activate(u *model.User, r *model.Repo, link string) error { func (c *Config) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
client := internal.NewClientWithToken(c.URL, c.Consumer, u.Token) client := internal.NewClientWithToken(ctx, c.URL, c.Consumer, u.Token)
return client.CreateHook(r.Owner, r.Name, link) return client.CreateHook(r.Owner, r.Name, link)
} }
func (c *Config) Deactivate(u *model.User, r *model.Repo, link string) error { func (c *Config) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
client := internal.NewClientWithToken(c.URL, c.Consumer, u.Token) client := internal.NewClientWithToken(ctx, c.URL, c.Consumer, u.Token)
return client.DeleteHook(r.Owner, r.Name, link) return client.DeleteHook(r.Owner, r.Name, link)
} }

View file

@ -16,6 +16,7 @@ package internal
import ( import (
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@ -48,20 +49,27 @@ type Client struct {
client *http.Client client *http.Client
base string base string
accessToken string accessToken string
ctx context.Context
} }
func NewClientWithToken(url string, consumer *oauth.Consumer, AccessToken string) *Client { func NewClientWithToken(ctx context.Context, url string, consumer *oauth.Consumer, AccessToken string) *Client {
var token oauth.AccessToken var token oauth.AccessToken
token.Token = AccessToken token.Token = AccessToken
client, err := consumer.MakeHttpClient(&token) client, err := consumer.MakeHttpClient(&token)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
} }
return &Client{client, url, AccessToken}
return &Client{
client: client,
base: url,
accessToken: AccessToken,
ctx: ctx,
}
} }
func (c *Client) FindCurrentUser() (*User, error) { func (c *Client) FindCurrentUser() (*User, error) {
CurrentUserIdResponse, err := c.client.Get(fmt.Sprintf(currentUserId, c.base)) CurrentUserIdResponse, err := c.doGet(fmt.Sprintf(currentUserId, c.base))
if CurrentUserIdResponse != nil { if CurrentUserIdResponse != nil {
defer CurrentUserIdResponse.Body.Close() defer CurrentUserIdResponse.Body.Close()
} }
@ -75,7 +83,7 @@ func (c *Client) FindCurrentUser() (*User, error) {
} }
login := string(bits) login := string(bits)
CurrentUserResponse, err := c.client.Get(fmt.Sprintf(pathUser, c.base, login)) CurrentUserResponse, err := c.doGet(fmt.Sprintf(pathUser, c.base, login))
if CurrentUserResponse != nil { if CurrentUserResponse != nil {
defer CurrentUserResponse.Body.Close() defer CurrentUserResponse.Body.Close()
} }
@ -100,7 +108,7 @@ func (c *Client) FindCurrentUser() (*User, error) {
func (c *Client) FindRepo(owner string, name string) (*Repo, error) { func (c *Client) FindRepo(owner string, name string) (*Repo, error) {
urlString := fmt.Sprintf(pathRepo, c.base, owner, name) urlString := fmt.Sprintf(pathRepo, c.base, owner, name)
response, err := c.client.Get(urlString) response, err := c.doGet(urlString)
if response != nil { if response != nil {
defer response.Body.Close() defer response.Body.Close()
} }
@ -128,7 +136,7 @@ func (c *Client) FindRepoPerms(owner string, repo string) (*model.Perm, error) {
return perms, err return perms, err
} }
// Must have admin to be able to list hooks. If have access the enable perms // Must have admin to be able to list hooks. If have access the enable perms
resp, err := c.client.Get(fmt.Sprintf(pathHook, c.base, owner, repo, hookName)) resp, err := c.doGet(fmt.Sprintf(pathHook, c.base, owner, repo, hookName))
if resp != nil { if resp != nil {
defer resp.Body.Close() defer resp.Body.Close()
} }
@ -141,7 +149,7 @@ func (c *Client) FindRepoPerms(owner string, repo string) (*model.Perm, error) {
} }
func (c *Client) FindFileForRepo(owner string, repo string, fileName string, ref string) ([]byte, error) { func (c *Client) FindFileForRepo(owner string, repo string, fileName string, ref string) ([]byte, error) {
response, err := c.client.Get(fmt.Sprintf(pathSource, c.base, owner, repo, fileName, ref)) response, err := c.doGet(fmt.Sprintf(pathSource, c.base, owner, repo, fileName, ref))
if response != nil { if response != nil {
defer response.Body.Close() defer response.Body.Close()
} }
@ -203,7 +211,7 @@ func (c *Client) DeleteHook(owner string, name string, link string) error {
func (c *Client) GetHookDetails(owner string, name string) (*HookPluginDetails, error) { func (c *Client) GetHookDetails(owner string, name string) (*HookPluginDetails, error) {
urlString := fmt.Sprintf(pathHookDetails, c.base, owner, name, hookName) urlString := fmt.Sprintf(pathHookDetails, c.base, owner, name, hookName)
response, err := c.client.Get(urlString) response, err := c.doGet(urlString)
if response != nil { if response != nil {
defer response.Body.Close() defer response.Body.Close()
} }
@ -218,7 +226,7 @@ func (c *Client) GetHookDetails(owner string, name string) (*HookPluginDetails,
func (c *Client) GetHooks(owner string, name string) (*HookSettings, error) { func (c *Client) GetHooks(owner string, name string) (*HookSettings, error) {
urlString := fmt.Sprintf(pathHookSettings, c.base, owner, name, hookName) urlString := fmt.Sprintf(pathHookSettings, c.base, owner, name, hookName)
response, err := c.client.Get(urlString) response, err := c.doGet(urlString)
if response != nil { if response != nil {
defer response.Body.Close() defer response.Body.Close()
} }
@ -233,9 +241,22 @@ func (c *Client) GetHooks(owner string, name string) (*HookSettings, error) {
//TODO: make these as as general do with the action //TODO: make these as as general do with the action
//Helper function to help create get
func (c *Client) doGet(url string) (*http.Response, error) {
request, err := http.NewRequestWithContext(c.ctx, "GET", url, nil)
if err != nil {
return nil, err
}
request.Header.Add("Content-Type", "application/json")
return c.client.Do(request)
}
//Helper function to help create the hook //Helper function to help create the hook
func (c *Client) doPut(url string, body []byte) error { func (c *Client) doPut(url string, body []byte) error {
request, err := http.NewRequest("PUT", url, bytes.NewBuffer(body)) request, err := http.NewRequestWithContext(c.ctx, "PUT", url, bytes.NewBuffer(body))
if err != nil {
return err
}
request.Header.Add("Content-Type", "application/json") request.Header.Add("Content-Type", "application/json")
response, err := c.client.Do(request) response, err := c.client.Do(request)
if response != nil { if response != nil {
@ -258,7 +279,10 @@ func (c *Client) doPost(url string, status *BuildStatus) error {
return err return err
} }
} }
request, err := http.NewRequest("POST", url, buf) request, err := http.NewRequestWithContext(c.ctx, "POST", url, buf)
if err != nil {
return err
}
request.Header.Add("Content-Type", "application/json") request.Header.Add("Content-Type", "application/json")
response, err := c.client.Do(request) response, err := c.client.Do(request)
if response != nil { if response != nil {
@ -269,7 +293,10 @@ func (c *Client) doPost(url string, status *BuildStatus) error {
//Helper function to do delete on the hook //Helper function to do delete on the hook
func (c *Client) doDelete(url string) error { func (c *Client) doDelete(url string) error {
request, err := http.NewRequest("DELETE", url, nil) request, err := http.NewRequestWithContext(c.ctx, "DELETE", url, nil)
if err != nil {
return err
}
response, err := c.client.Do(request) response, err := c.client.Do(request)
if response != nil { if response != nil {
defer response.Body.Close() defer response.Body.Close()
@ -281,7 +308,7 @@ func (c *Client) doDelete(url string) error {
func (c *Client) paginatedRepos(start int) ([]*Repo, error) { func (c *Client) paginatedRepos(start int) ([]*Repo, error) {
limit := 1000 limit := 1000
requestUrl := fmt.Sprintf(pathRepos, c.base, strconv.Itoa(start), strconv.Itoa(limit)) requestUrl := fmt.Sprintf(pathRepos, c.base, strconv.Itoa(start), strconv.Itoa(limit))
response, err := c.client.Get(requestUrl) response, err := c.doGet(requestUrl)
if response != nil { if response != nil {
defer response.Body.Close() defer response.Body.Close()
} }

View file

@ -15,6 +15,7 @@
package coding package coding
import ( import (
"context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"net/http" "net/http"
@ -25,7 +26,6 @@ import (
"github.com/woodpecker-ci/woodpecker/server/remote" "github.com/woodpecker-ci/woodpecker/server/remote"
"github.com/woodpecker-ci/woodpecker/server/remote/coding/internal" "github.com/woodpecker-ci/woodpecker/server/remote/coding/internal"
"golang.org/x/net/context"
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
@ -78,7 +78,7 @@ type Coding struct {
// Login authenticates the session and returns the // Login authenticates the session and returns the
// remote user details. // remote user details.
func (c *Coding) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) { func (c *Coding) Login(ctx context.Context, res http.ResponseWriter, req *http.Request) (*model.User, error) {
config := c.newConfig(server.Config.Server.Host) config := c.newConfig(server.Config.Server.Host)
// get the OAuth errors // get the OAuth errors
@ -97,12 +97,12 @@ func (c *Coding) Login(res http.ResponseWriter, req *http.Request) (*model.User,
return nil, nil return nil, nil
} }
token, err := config.Exchange(c.newContext(), code) token, err := config.Exchange(c.newContext(ctx), code)
if err != nil { if err != nil {
return nil, err return nil, err
} }
user, err := c.newClientToken(token.AccessToken, token.RefreshToken).GetCurrentUser() user, err := c.newClientToken(ctx, token.AccessToken).GetCurrentUser()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -119,8 +119,8 @@ func (c *Coding) Login(res http.ResponseWriter, req *http.Request) (*model.User,
// Auth authenticates the session and returns the remote user // Auth authenticates the session and returns the remote user
// login for the given token and secret // login for the given token and secret
func (c *Coding) Auth(token, secret string) (string, error) { func (c *Coding) Auth(ctx context.Context, token, secret string) (string, error) {
user, err := c.newClientToken(token, secret).GetCurrentUser() user, err := c.newClientToken(ctx, token).GetCurrentUser()
if err != nil { if err != nil {
return "", err return "", err
} }
@ -130,9 +130,9 @@ func (c *Coding) Auth(token, secret string) (string, error) {
// Refresh refreshes an oauth token and expiration for the given // Refresh refreshes an oauth token and expiration for the given
// user. It returns true if the token was refreshed, false if the // user. It returns true if the token was refreshed, false if the
// token was not refreshed, and error if it failed to refersh. // token was not refreshed, and error if it failed to refersh.
func (c *Coding) Refresh(u *model.User) (bool, error) { func (c *Coding) Refresh(ctx context.Context, u *model.User) (bool, error) {
config := c.newConfig("") config := c.newConfig("")
source := config.TokenSource(c.newContext(), &oauth2.Token{RefreshToken: u.Secret}) source := config.TokenSource(c.newContext(ctx), &oauth2.Token{RefreshToken: u.Secret})
token, err := source.Token() token, err := source.Token()
if err != nil || len(token.AccessToken) == 0 { if err != nil || len(token.AccessToken) == 0 {
return false, err return false, err
@ -145,9 +145,9 @@ func (c *Coding) Refresh(u *model.User) (bool, error) {
} }
// Teams fetches a list of team memberships from the remote system. // Teams fetches a list of team memberships from the remote system.
func (c *Coding) Teams(u *model.User) ([]*model.Team, error) { func (c *Coding) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) {
// EMPTY: not implemented in Coding OAuth API // EMPTY: not implemented in Coding OAuth API
return nil, nil return nil, fmt.Errorf("Not implemented")
} }
// TeamPerm fetches the named organization permissions from // TeamPerm fetches the named organization permissions from
@ -158,12 +158,13 @@ func (c *Coding) TeamPerm(u *model.User, org string) (*model.Perm, error) {
} }
// Repo fetches the named repository from the remote system. // Repo fetches the named repository from the remote system.
func (c *Coding) Repo(u *model.User, owner, repo string) (*model.Repo, error) { func (c *Coding) Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error) {
project, err := c.newClient(u).GetProject(owner, repo) client := c.newClient(ctx, u)
project, err := client.GetProject(owner, name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
depot, err := c.newClient(u).GetDepot(owner, repo) depot, err := client.GetDepot(owner, name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -181,15 +182,16 @@ func (c *Coding) Repo(u *model.User, owner, repo string) (*model.Repo, error) {
} }
// Repos fetches a list of repos from the remote system. // Repos fetches a list of repos from the remote system.
func (c *Coding) Repos(u *model.User) ([]*model.Repo, error) { func (c *Coding) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) {
projectList, err := c.newClient(u).GetProjectList() client := c.newClient(ctx, u)
projectList, err := client.GetProjectList()
if err != nil { if err != nil {
return nil, err return nil, err
} }
repos := make([]*model.Repo, 0) repos := make([]*model.Repo, 0)
for _, project := range projectList { for _, project := range projectList {
depot, err := c.newClient(u).GetDepot(project.Owner, project.Name) depot, err := client.GetDepot(project.Owner, project.Name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -211,8 +213,8 @@ func (c *Coding) Repos(u *model.User) ([]*model.Repo, error) {
// Perm fetches the named repository permissions from // Perm fetches the named repository permissions from
// the remote system for the specified user. // the remote system for the specified user.
func (c *Coding) Perm(u *model.User, owner, repo string) (*model.Perm, error) { func (c *Coding) Perm(ctx context.Context, u *model.User, owner, repo string) (*model.Perm, error) {
project, err := c.newClient(u).GetProject(owner, repo) project, err := c.newClient(ctx, u).GetProject(owner, repo)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -228,20 +230,20 @@ func (c *Coding) Perm(u *model.User, owner, repo string) (*model.Perm, error) {
// File fetches a file from the remote repository and returns in string // File fetches a file from the remote repository and returns in string
// format. // format.
func (c *Coding) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { func (c *Coding) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
data, err := c.newClient(u).GetFile(r.Owner, r.Name, b.Commit, f) data, err := c.newClient(ctx, u).GetFile(r.Owner, r.Name, b.Commit, f)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return data, nil return data, nil
} }
func (c *Coding) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { func (c *Coding) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) {
return nil, fmt.Errorf("Not implemented") return nil, fmt.Errorf("Not implemented")
} }
// Status sends the commit status to the remote system. // Status sends the commit status to the remote system.
func (c *Coding) Status(u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { func (c *Coding) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
// EMPTY: not implemented in Coding OAuth API // EMPTY: not implemented in Coding OAuth API
return nil return nil
} }
@ -264,14 +266,14 @@ func (c *Coding) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
} }
// Activate activates a repository by creating the post-commit hook. // Activate activates a repository by creating the post-commit hook.
func (c *Coding) Activate(u *model.User, r *model.Repo, link string) error { func (c *Coding) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
return c.newClient(u).AddWebhook(r.Owner, r.Name, link) return c.newClient(ctx, u).AddWebhook(r.Owner, r.Name, link)
} }
// Deactivate deactivates a repository by removing all previously created // Deactivate deactivates a repository by removing all previously created
// post-commit hooks matching the given link. // post-commit hooks matching the given link.
func (c *Coding) Deactivate(u *model.User, r *model.Repo, link string) error { func (c *Coding) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
return c.newClient(u).RemoveWebhook(r.Owner, r.Name, link) return c.newClient(ctx, u).RemoveWebhook(r.Owner, r.Name, link)
} }
// Hook parses the post-commit hook from the Request body and returns the // Hook parses the post-commit hook from the Request body and returns the
@ -286,11 +288,11 @@ func (c *Coding) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
// helper function to return the Coding oauth2 context using an HTTPClient that // helper function to return the Coding oauth2 context using an HTTPClient that
// disables TLS verification if disabled in the remote settings. // disables TLS verification if disabled in the remote settings.
func (c *Coding) newContext() context.Context { func (c *Coding) newContext(ctx context.Context) context.Context {
if !c.SkipVerify { if !c.SkipVerify {
return oauth2.NoContext return ctx
} }
return context.WithValue(nil, oauth2.HTTPClient, &http.Client{ return context.WithValue(ctx, oauth2.HTTPClient, &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment, Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{ TLSClientConfig: &tls.Config{
@ -315,12 +317,12 @@ func (c *Coding) newConfig(redirect string) *oauth2.Config {
} }
// helper function to return the Coding oauth2 client // helper function to return the Coding oauth2 client
func (c *Coding) newClient(u *model.User) *internal.Client { func (c *Coding) newClient(ctx context.Context, u *model.User) *internal.Client {
return c.newClientToken(u.Token, u.Secret) return c.newClientToken(ctx, u.Token)
} }
// helper function to return the Coding oauth2 client // helper function to return the Coding oauth2 client
func (c *Coding) newClientToken(token, secret string) *internal.Client { func (c *Coding) newClientToken(ctx context.Context, token string) *internal.Client {
client := &http.Client{ client := &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment, Proxy: http.ProxyFromEnvironment,
@ -329,7 +331,7 @@ func (c *Coding) newClientToken(token, secret string) *internal.Client {
}, },
}, },
} }
return internal.NewClient(c.URL, "/api", token, "drone", client) return internal.NewClient(ctx, c.URL, "/api", token, "drone", client)
} }
func (c *Coding) resourceLink(resourcePath string) string { func (c *Coding) resourceLink(resourcePath string) string {

View file

@ -16,6 +16,7 @@ package coding
import ( import (
"bytes" "bytes"
"context"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@ -33,6 +34,7 @@ func Test_coding(t *testing.T) {
s := httptest.NewServer(fixtures.Handler()) s := httptest.NewServer(fixtures.Handler())
c := &Coding{URL: s.URL} c := &Coding{URL: s.URL}
ctx := context.Background()
g := goblin.Goblin(t) g := goblin.Goblin(t)
g.Describe("Coding", func() { g.Describe("Coding", func() {
@ -67,13 +69,13 @@ func Test_coding(t *testing.T) {
g.It("Should redirect to authorize", func() { g.It("Should redirect to authorize", func() {
w := httptest.NewRecorder() w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", "", nil) r, _ := http.NewRequest("GET", "", nil)
_, err := c.Login(w, r) _, err := c.Login(ctx, w, r)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(w.Code).Equal(http.StatusSeeOther) g.Assert(w.Code).Equal(http.StatusSeeOther)
}) })
g.It("Should return authenticated user", func() { g.It("Should return authenticated user", func() {
r, _ := http.NewRequest("GET", "?code=code", nil) r, _ := http.NewRequest("GET", "?code=code", nil)
u, err := c.Login(nil, r) u, err := c.Login(ctx, nil, r)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(u.Login).Equal(fakeUser.Login) g.Assert(u.Login).Equal(fakeUser.Login)
g.Assert(u.Token).Equal(fakeUser.Token) g.Assert(u.Token).Equal(fakeUser.Token)
@ -83,43 +85,33 @@ func Test_coding(t *testing.T) {
g.Describe("Given an access token", func() { g.Describe("Given an access token", func() {
g.It("Should return the anthenticated user", func() { g.It("Should return the anthenticated user", func() {
login, err := c.Auth( login, err := c.Auth(ctx, fakeUser.Token, fakeUser.Secret)
fakeUser.Token,
fakeUser.Secret,
)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(login).Equal(fakeUser.Login) g.Assert(login).Equal(fakeUser.Login)
}) })
g.It("Should handle a failure to resolve user", func() { g.It("Should handle a failure to resolve user", func() {
_, err := c.Auth( _, err := c.Auth(ctx, fakeUserNotFound.Token, fakeUserNotFound.Secret)
fakeUserNotFound.Token,
fakeUserNotFound.Secret,
)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
}) })
g.Describe("Given a refresh token", func() { g.Describe("Given a refresh token", func() {
g.It("Should return a refresh access token", func() { g.It("Should return a refresh access token", func() {
ok, err := c.Refresh(fakeUserRefresh) ok, err := c.Refresh(ctx, fakeUserRefresh)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(ok).IsTrue() g.Assert(ok).IsTrue()
g.Assert(fakeUserRefresh.Token).Equal("VDZupx0usVRV4oOd1FCu4xUxgk8SY0TK") g.Assert(fakeUserRefresh.Token).Equal("VDZupx0usVRV4oOd1FCu4xUxgk8SY0TK")
g.Assert(fakeUserRefresh.Secret).Equal("BenBQq7TWZ7Cp0aUM47nQjTz2QHNmTWcPctB609n") g.Assert(fakeUserRefresh.Secret).Equal("BenBQq7TWZ7Cp0aUM47nQjTz2QHNmTWcPctB609n")
}) })
g.It("Should handle an invalid refresh token", func() { g.It("Should handle an invalid refresh token", func() {
ok, _ := c.Refresh(fakeUserRefreshInvalid) ok, _ := c.Refresh(ctx, fakeUserRefreshInvalid)
g.Assert(ok).IsFalse() g.Assert(ok).IsFalse()
}) })
}) })
g.Describe("When requesting a repository", func() { g.Describe("When requesting a repository", func() {
g.It("Should return the details", func() { g.It("Should return the details", func() {
repo, err := c.Repo( repo, err := c.Repo(ctx, fakeUser, fakeRepo.Owner, fakeRepo.Name)
fakeUser,
fakeRepo.Owner,
fakeRepo.Name,
)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(repo.FullName).Equal(fakeRepo.FullName) g.Assert(repo.FullName).Equal(fakeRepo.FullName)
g.Assert(repo.Avatar).Equal(s.URL + fakeRepo.Avatar) g.Assert(repo.Avatar).Equal(s.URL + fakeRepo.Avatar)
@ -130,57 +122,49 @@ func Test_coding(t *testing.T) {
g.Assert(repo.IsPrivate).Equal(fakeRepo.IsPrivate) g.Assert(repo.IsPrivate).Equal(fakeRepo.IsPrivate)
}) })
g.It("Should handle not found errors", func() { g.It("Should handle not found errors", func() {
_, err := c.Repo( _, err := c.Repo(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name)
fakeUser,
fakeRepoNotFound.Owner,
fakeRepoNotFound.Name,
)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
}) })
g.Describe("When requesting repository permissions", func() { g.Describe("When requesting repository permissions", func() {
g.It("Should authorize admin access for project owner", func() { g.It("Should authorize admin access for project owner", func() {
perm, err := c.Perm(fakeUser, "demo1", "perm_owner") perm, err := c.Perm(ctx, fakeUser, "demo1", "perm_owner")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(perm.Pull).IsTrue() g.Assert(perm.Pull).IsTrue()
g.Assert(perm.Push).IsTrue() g.Assert(perm.Push).IsTrue()
g.Assert(perm.Admin).IsTrue() g.Assert(perm.Admin).IsTrue()
}) })
g.It("Should authorize admin access for project admin", func() { g.It("Should authorize admin access for project admin", func() {
perm, err := c.Perm(fakeUser, "demo1", "perm_admin") perm, err := c.Perm(ctx, fakeUser, "demo1", "perm_admin")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(perm.Pull).IsTrue() g.Assert(perm.Pull).IsTrue()
g.Assert(perm.Push).IsTrue() g.Assert(perm.Push).IsTrue()
g.Assert(perm.Admin).IsTrue() g.Assert(perm.Admin).IsTrue()
}) })
g.It("Should authorize read access for project member", func() { g.It("Should authorize read access for project member", func() {
perm, err := c.Perm(fakeUser, "demo1", "perm_member") perm, err := c.Perm(ctx, fakeUser, "demo1", "perm_member")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(perm.Pull).IsTrue() g.Assert(perm.Pull).IsTrue()
g.Assert(perm.Push).IsTrue() g.Assert(perm.Push).IsTrue()
g.Assert(perm.Admin).IsFalse() g.Assert(perm.Admin).IsFalse()
}) })
g.It("Should authorize no access for project guest", func() { g.It("Should authorize no access for project guest", func() {
perm, err := c.Perm(fakeUser, "demo1", "perm_guest") perm, err := c.Perm(ctx, fakeUser, "demo1", "perm_guest")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(perm.Pull).IsFalse() g.Assert(perm.Pull).IsFalse()
g.Assert(perm.Push).IsFalse() g.Assert(perm.Push).IsFalse()
g.Assert(perm.Admin).IsFalse() g.Assert(perm.Admin).IsFalse()
}) })
g.It("Should handle not found errors", func() { g.It("Should handle not found errors", func() {
_, err := c.Perm( _, err := c.Perm(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name)
fakeUser,
fakeRepoNotFound.Owner,
fakeRepoNotFound.Name,
)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
}) })
g.Describe("When downloading a file", func() { g.Describe("When downloading a file", func() {
g.It("Should return file for specified build", func() { g.It("Should return file for specified build", func() {
data, err := c.File(fakeUser, fakeRepo, fakeBuild, ".drone.yml") data, err := c.File(ctx, fakeUser, fakeRepo, fakeBuild, ".drone.yml")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(string(data)).Equal("pipeline:\n test:\n image: golang:1.6\n commands:\n - go test\n") g.Assert(string(data)).Equal("pipeline:\n test:\n image: golang:1.6\n commands:\n - go test\n")
}) })
@ -213,22 +197,22 @@ func Test_coding(t *testing.T) {
g.Describe("When activating a repository", func() { g.Describe("When activating a repository", func() {
g.It("Should create the hook", func() { g.It("Should create the hook", func() {
err := c.Activate(fakeUser, fakeRepo, "http://127.0.0.1") err := c.Activate(ctx, fakeUser, fakeRepo, "http://127.0.0.1")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
}) })
g.It("Should update the hook when exists", func() { g.It("Should update the hook when exists", func() {
err := c.Activate(fakeUser, fakeRepo, "http://127.0.0.2") err := c.Activate(ctx, fakeUser, fakeRepo, "http://127.0.0.2")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
}) })
}) })
g.Describe("When deactivating a repository", func() { g.Describe("When deactivating a repository", func() {
g.It("Should successfully remove hook", func() { g.It("Should successfully remove hook", func() {
err := c.Deactivate(fakeUser, fakeRepo, "http://127.0.0.3") err := c.Deactivate(ctx, fakeUser, fakeRepo, "http://127.0.0.3")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
}) })
g.It("Should successfully deactivate when hook already removed", func() { g.It("Should successfully deactivate when hook already removed", func() {
err := c.Deactivate(fakeUser, fakeRepo, "http://127.0.0.4") err := c.Deactivate(ctx, fakeUser, fakeRepo, "http://127.0.0.4")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
}) })
}) })

View file

@ -15,6 +15,7 @@
package internal package internal
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -29,6 +30,7 @@ type Client struct {
token string token string
agent string agent string
client *http.Client client *http.Client
ctx context.Context
} }
type GenericAPIResponse struct { type GenericAPIResponse struct {
@ -36,13 +38,14 @@ type GenericAPIResponse struct {
Data json.RawMessage `json:"data,omitempty"` Data json.RawMessage `json:"data,omitempty"`
} }
func NewClient(baseURL, apiPath, token, agent string, client *http.Client) *Client { func NewClient(ctx context.Context, baseURL, apiPath, token, agent string, client *http.Client) *Client {
return &Client{ return &Client{
baseURL: baseURL, baseURL: baseURL,
apiPath: apiPath, apiPath: apiPath,
token: token, token: token,
agent: agent, agent: agent,
client: client, client: client,
ctx: ctx,
} }
} }
@ -63,10 +66,10 @@ func (c *Client) Do(method, u string, params url.Values) ([]byte, error) {
var req *http.Request var req *http.Request
var err error var err error
if method != "GET" { if method != "GET" {
req, err = http.NewRequest(method, rawURL+"?access_token="+c.token, strings.NewReader(params.Encode())) req, err = http.NewRequestWithContext(c.ctx, method, rawURL+"?access_token="+c.token, strings.NewReader(params.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8") req.Header.Set("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
} else { } else {
req, err = http.NewRequest("GET", rawURL+"?"+params.Encode(), nil) req, err = http.NewRequestWithContext(c.ctx, "GET", rawURL+"?"+params.Encode(), nil)
} }
if err != nil { if err != nil {
return nil, fmt.Errorf("fail to create request for url %q: %v", rawURL, err) return nil, fmt.Errorf("fail to create request for url %q: %v", rawURL, err)

View file

@ -91,7 +91,7 @@ func New(opts Opts) (remote.Remote, error) {
// Login authenticates an account with Gitea using basic authentication. The // Login authenticates an account with Gitea using basic authentication. The
// Gitea account details are returned when the user is successfully authenticated. // Gitea account details are returned when the user is successfully authenticated.
func (c *Gitea) Login(w http.ResponseWriter, req *http.Request) (*model.User, error) { func (c *Gitea) Login(ctx context.Context, w http.ResponseWriter, req *http.Request) (*model.User, error) {
config := &oauth2.Config{ config := &oauth2.Config{
ClientID: c.ClientID, ClientID: c.ClientID,
ClientSecret: c.ClientSecret, ClientSecret: c.ClientSecret,
@ -123,7 +123,7 @@ func (c *Gitea) Login(w http.ResponseWriter, req *http.Request) (*model.User, er
return nil, err return nil, err
} }
client, err := c.newClientToken(token.AccessToken) client, err := c.newClientToken(ctx, token.AccessToken)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -144,8 +144,8 @@ func (c *Gitea) Login(w http.ResponseWriter, req *http.Request) (*model.User, er
// Auth uses the Gitea oauth2 access token and refresh token to authenticate // Auth uses the Gitea oauth2 access token and refresh token to authenticate
// a session and return the Gitea account login. // a session and return the Gitea account login.
func (c *Gitea) Auth(token, _ string) (string, error) { func (c *Gitea) Auth(ctx context.Context, token, secret string) (string, error) {
client, err := c.newClientToken(token) client, err := c.newClientToken(ctx, token)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -158,7 +158,7 @@ func (c *Gitea) Auth(token, _ string) (string, error) {
// Refresh refreshes the Gitea oauth2 access token. If the token is // Refresh refreshes the Gitea oauth2 access token. If the token is
// refreshed the user is updated and a true value is returned. // refreshed the user is updated and a true value is returned.
func (c *Gitea) Refresh(user *model.User) (bool, error) { func (c *Gitea) Refresh(ctx context.Context, user *model.User) (bool, error) {
config := &oauth2.Config{ config := &oauth2.Config{
ClientID: c.ClientID, ClientID: c.ClientID,
ClientSecret: c.ClientSecret, ClientSecret: c.ClientSecret,
@ -167,7 +167,7 @@ func (c *Gitea) Refresh(user *model.User) (bool, error) {
TokenURL: fmt.Sprintf(accessTokenURL, c.URL), TokenURL: fmt.Sprintf(accessTokenURL, c.URL),
}, },
} }
source := config.TokenSource(context.TODO(), &oauth2.Token{RefreshToken: user.Secret}) source := config.TokenSource(ctx, &oauth2.Token{RefreshToken: user.Secret})
token, err := source.Token() token, err := source.Token()
if err != nil || len(token.AccessToken) == 0 { if err != nil || len(token.AccessToken) == 0 {
@ -181,8 +181,8 @@ func (c *Gitea) Refresh(user *model.User) (bool, error) {
} }
// Teams is supported by the Gitea driver. // Teams is supported by the Gitea driver.
func (c *Gitea) Teams(u *model.User) ([]*model.Team, error) { func (c *Gitea) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) {
client, err := c.newClientToken(u.Token) client, err := c.newClientToken(ctx, u.Token)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -222,8 +222,8 @@ func (c *Gitea) TeamPerm(u *model.User, org string) (*model.Perm, error) {
} }
// Repo returns the named Gitea repository. // Repo returns the named Gitea repository.
func (c *Gitea) Repo(u *model.User, owner, name string) (*model.Repo, error) { func (c *Gitea) Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error) {
client, err := c.newClientToken(u.Token) client, err := c.newClientToken(ctx, u.Token)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -240,10 +240,10 @@ func (c *Gitea) Repo(u *model.User, owner, name string) (*model.Repo, error) {
// Repos returns a list of all repositories for the Gitea account, including // Repos returns a list of all repositories for the Gitea account, including
// organization repositories. // organization repositories.
func (c *Gitea) Repos(u *model.User) ([]*model.Repo, error) { func (c *Gitea) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) {
repos := make([]*model.Repo, 0, perPage) repos := make([]*model.Repo, 0, perPage)
client, err := c.newClientToken(u.Token) client, err := c.newClientToken(ctx, u.Token)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -278,8 +278,8 @@ func (c *Gitea) Repos(u *model.User) ([]*model.Repo, error) {
} }
// Perm returns the user permissions for the named Gitea repository. // Perm returns the user permissions for the named Gitea repository.
func (c *Gitea) Perm(u *model.User, owner, name string) (*model.Perm, error) { func (c *Gitea) Perm(ctx context.Context, u *model.User, owner, name string) (*model.Perm, error) {
client, err := c.newClientToken(u.Token) client, err := c.newClientToken(ctx, u.Token)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -292,8 +292,8 @@ func (c *Gitea) Perm(u *model.User, owner, name string) (*model.Perm, error) {
} }
// File fetches the file from the Gitea repository and returns its contents. // File fetches the file from the Gitea repository and returns its contents.
func (c *Gitea) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { func (c *Gitea) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
client, err := c.newClientToken(u.Token) client, err := c.newClientToken(ctx, u.Token)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -302,10 +302,10 @@ func (c *Gitea) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]
return cfg, err return cfg, err
} }
func (c *Gitea) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { func (c *Gitea) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) {
var configs []*remote.FileMeta var configs []*remote.FileMeta
client, err := c.newClientToken(u.Token) client, err := c.newClientToken(ctx, u.Token)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -321,7 +321,7 @@ func (c *Gitea) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*
for _, e := range tree.Entries { for _, e := range tree.Entries {
// Filter path matching pattern and type file (blob) // Filter path matching pattern and type file (blob)
if m, _ := filepath.Match(f, e.Path); m && e.Type == "blob" { if m, _ := filepath.Match(f, e.Path); m && e.Type == "blob" {
data, err := c.File(u, r, b, e.Path) data, err := c.File(ctx, u, r, b, e.Path)
if err != nil { if err != nil {
return nil, fmt.Errorf("multi-pipeline cannot get %s: %s", e.Path, err) return nil, fmt.Errorf("multi-pipeline cannot get %s: %s", e.Path, err)
} }
@ -337,8 +337,8 @@ func (c *Gitea) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*
} }
// Status is supported by the Gitea driver. // Status is supported by the Gitea driver.
func (c *Gitea) Status(u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { func (c *Gitea) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
client, err := c.newClientToken(u.Token) client, err := c.newClientToken(ctx, u.Token)
if err != nil { if err != nil {
return err return err
} }
@ -381,7 +381,7 @@ func (c *Gitea) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
// Activate activates the repository by registering post-commit hooks with // Activate activates the repository by registering post-commit hooks with
// the Gitea repository. // the Gitea repository.
func (c *Gitea) Activate(u *model.User, r *model.Repo, link string) error { func (c *Gitea) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
config := map[string]string{ config := map[string]string{
"url": link, "url": link,
"secret": r.Hash, "secret": r.Hash,
@ -394,7 +394,7 @@ func (c *Gitea) Activate(u *model.User, r *model.Repo, link string) error {
Active: true, Active: true,
} }
client, err := c.newClientToken(u.Token) client, err := c.newClientToken(ctx, u.Token)
if err != nil { if err != nil {
return err return err
} }
@ -404,8 +404,8 @@ func (c *Gitea) Activate(u *model.User, r *model.Repo, link string) error {
// Deactivate deactives the repository be removing repository push hooks from // Deactivate deactives the repository be removing repository push hooks from
// the Gitea repository. // the Gitea repository.
func (c *Gitea) Deactivate(u *model.User, r *model.Repo, link string) error { func (c *Gitea) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
client, err := c.newClientToken(u.Token) client, err := c.newClientToken(ctx, u.Token)
if err != nil { if err != nil {
return err return err
} }
@ -431,14 +431,14 @@ func (c *Gitea) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
} }
// helper function to return the Gitea client with Token // helper function to return the Gitea client with Token
func (c *Gitea) newClientToken(token string) (*gitea.Client, error) { func (c *Gitea) newClientToken(ctx context.Context, token string) (*gitea.Client, error) {
httpClient := &http.Client{} httpClient := &http.Client{}
if c.SkipVerify { if c.SkipVerify {
httpClient.Transport = &http.Transport{ httpClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
} }
} }
return gitea.NewClient(c.URL, gitea.SetToken(token), gitea.SetHTTPClient(httpClient)) return gitea.NewClient(c.URL, gitea.SetToken(token), gitea.SetHTTPClient(httpClient), gitea.SetContext(ctx))
} }
const ( const (

View file

@ -15,6 +15,7 @@
package gitea package gitea
import ( import (
"context"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@ -33,6 +34,7 @@ func Test_gitea(t *testing.T) {
SkipVerify: true, SkipVerify: true,
}) })
ctx := context.Background()
g := goblin.Goblin(t) g := goblin.Goblin(t)
g.Describe("Gitea", func() { g.Describe("Gitea", func() {
@ -89,7 +91,7 @@ func Test_gitea(t *testing.T) {
g.Describe("Requesting a repository", func() { g.Describe("Requesting a repository", func() {
g.It("Should return the repository details", func() { g.It("Should return the repository details", func() {
repo, err := c.Repo(fakeUser, fakeRepo.Owner, fakeRepo.Name) repo, err := c.Repo(ctx, fakeUser, fakeRepo.Owner, fakeRepo.Name)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(repo.Owner).Equal(fakeRepo.Owner) g.Assert(repo.Owner).Equal(fakeRepo.Owner)
g.Assert(repo.Name).Equal(fakeRepo.Name) g.Assert(repo.Name).Equal(fakeRepo.Name)
@ -99,57 +101,57 @@ func Test_gitea(t *testing.T) {
g.Assert(repo.Link).Equal("http://localhost/test_name/repo_name") g.Assert(repo.Link).Equal("http://localhost/test_name/repo_name")
}) })
g.It("Should handle a not found error", func() { g.It("Should handle a not found error", func() {
_, err := c.Repo(fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) _, err := c.Repo(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
}) })
g.Describe("Requesting repository permissions", func() { g.Describe("Requesting repository permissions", func() {
g.It("Should return the permission details", func() { g.It("Should return the permission details", func() {
perm, err := c.Perm(fakeUser, fakeRepo.Owner, fakeRepo.Name) perm, err := c.Perm(ctx, fakeUser, fakeRepo.Owner, fakeRepo.Name)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(perm.Admin).IsTrue() g.Assert(perm.Admin).IsTrue()
g.Assert(perm.Push).IsTrue() g.Assert(perm.Push).IsTrue()
g.Assert(perm.Pull).IsTrue() g.Assert(perm.Pull).IsTrue()
}) })
g.It("Should handle a not found error", func() { g.It("Should handle a not found error", func() {
_, err := c.Perm(fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) _, err := c.Perm(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
}) })
g.Describe("Requesting a repository list", func() { g.Describe("Requesting a repository list", func() {
g.It("Should return the repository list", func() { g.It("Should return the repository list", func() {
repos, err := c.Repos(fakeUser) repos, err := c.Repos(ctx, fakeUser)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(repos[0].Owner).Equal(fakeRepo.Owner) g.Assert(repos[0].Owner).Equal(fakeRepo.Owner)
g.Assert(repos[0].Name).Equal(fakeRepo.Name) g.Assert(repos[0].Name).Equal(fakeRepo.Name)
g.Assert(repos[0].FullName).Equal(fakeRepo.Owner + "/" + fakeRepo.Name) g.Assert(repos[0].FullName).Equal(fakeRepo.Owner + "/" + fakeRepo.Name)
}) })
g.It("Should handle a not found error", func() { g.It("Should handle a not found error", func() {
_, err := c.Repos(fakeUserNoRepos) _, err := c.Repos(ctx, fakeUserNoRepos)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
}) })
g.It("Should register repository hooks", func() { g.It("Should register repository hooks", func() {
err := c.Activate(fakeUser, fakeRepo, "http://localhost") err := c.Activate(ctx, fakeUser, fakeRepo, "http://localhost")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
}) })
g.It("Should remove repository hooks", func() { g.It("Should remove repository hooks", func() {
err := c.Deactivate(fakeUser, fakeRepo, "http://localhost") err := c.Deactivate(ctx, fakeUser, fakeRepo, "http://localhost")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
}) })
g.It("Should return a repository file", func() { g.It("Should return a repository file", func() {
raw, err := c.File(fakeUser, fakeRepo, fakeBuild, ".drone.yml") raw, err := c.File(ctx, fakeUser, fakeRepo, fakeBuild, ".drone.yml")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(string(raw)).Equal("{ platform: linux/amd64 }") g.Assert(string(raw)).Equal("{ platform: linux/amd64 }")
}) })
g.It("Should return nil from send build status", func() { g.It("Should return nil from send build status", func() {
err := c.Status(fakeUser, fakeRepo, fakeBuild, "http://gitea.io", nil) err := c.Status(ctx, fakeUser, fakeRepo, fakeBuild, "http://gitea.io", nil)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
}) })

View file

@ -15,6 +15,7 @@
package github package github
import ( import (
"context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"net" "net"
@ -28,8 +29,8 @@ import (
"github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/model"
"github.com/woodpecker-ci/woodpecker/server/remote" "github.com/woodpecker-ci/woodpecker/server/remote"
// TODO upgrade to v39 to pass context down
"github.com/google/go-github/github" "github.com/google/go-github/github"
"golang.org/x/net/context"
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
@ -82,9 +83,6 @@ func New(opts Opts) (remote.Remote, error) {
r.API = r.URL + "/api/v3/" r.API = r.URL + "/api/v3/"
} }
// Hack to enable oauth2 access in older GHE
// TODO: dont use deprecated func
oauth2.RegisterBrokenAuthHeaderProvider(r.URL)
return r, nil return r, nil
} }
@ -104,7 +102,7 @@ type client struct {
} }
// Login authenticates the session and returns the remote user details. // Login authenticates the session and returns the remote user details.
func (c *client) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) { func (c *client) Login(ctx context.Context, res http.ResponseWriter, req *http.Request) (*model.User, error) {
config := c.newConfig(req) config := c.newConfig(req)
// get the OAuth errors // get the OAuth errors
@ -126,12 +124,12 @@ func (c *client) Login(res http.ResponseWriter, req *http.Request) (*model.User,
return nil, nil return nil, nil
} }
token, err := config.Exchange(c.newContext(), code) token, err := config.Exchange(c.newContext(ctx), code)
if err != nil { if err != nil {
return nil, err return nil, err
} }
client := c.newClientToken(token.AccessToken) client := c.newClientToken(ctx, token.AccessToken)
user, _, err := client.Users.Get("") user, _, err := client.Users.Get("")
if err != nil { if err != nil {
return nil, err return nil, err
@ -155,8 +153,8 @@ func (c *client) Login(res http.ResponseWriter, req *http.Request) (*model.User,
} }
// Auth returns the GitHub user login for the given access token. // Auth returns the GitHub user login for the given access token.
func (c *client) Auth(token, secret string) (string, error) { func (c *client) Auth(ctx context.Context, token, secret string) (string, error) {
client := c.newClientToken(token) client := c.newClientToken(ctx, token)
user, _, err := client.Users.Get("") user, _, err := client.Users.Get("")
if err != nil { if err != nil {
return "", err return "", err
@ -165,8 +163,8 @@ func (c *client) Auth(token, secret string) (string, error) {
} }
// Teams returns a list of all team membership for the GitHub account. // Teams returns a list of all team membership for the GitHub account.
func (c *client) Teams(u *model.User) ([]*model.Team, error) { func (c *client) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) {
client := c.newClientToken(u.Token) client := c.newClientToken(ctx, u.Token)
opts := new(github.ListOptions) opts := new(github.ListOptions)
opts.Page = 1 opts.Page = 1
@ -184,8 +182,8 @@ func (c *client) Teams(u *model.User) ([]*model.Team, error) {
} }
// Repo returns the named GitHub repository. // Repo returns the named GitHub repository.
func (c *client) Repo(u *model.User, owner, name string) (*model.Repo, error) { func (c *client) Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error) {
client := c.newClientToken(u.Token) client := c.newClientToken(ctx, u.Token)
repo, _, err := client.Repositories.Get(owner, name) repo, _, err := client.Repositories.Get(owner, name)
if err != nil { if err != nil {
return nil, err return nil, err
@ -195,8 +193,8 @@ func (c *client) Repo(u *model.User, owner, name string) (*model.Repo, error) {
// Repos returns a list of all repositories for GitHub account, including // Repos returns a list of all repositories for GitHub account, including
// organization repositories. // organization repositories.
func (c *client) Repos(u *model.User) ([]*model.Repo, error) { func (c *client) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) {
client := c.newClientToken(u.Token) client := c.newClientToken(ctx, u.Token)
opts := new(github.RepositoryListOptions) opts := new(github.RepositoryListOptions)
opts.PerPage = 100 opts.PerPage = 100
@ -215,8 +213,8 @@ func (c *client) Repos(u *model.User) ([]*model.Repo, error) {
} }
// Perm returns the user permissions for the named GitHub repository. // Perm returns the user permissions for the named GitHub repository.
func (c *client) Perm(u *model.User, owner, name string) (*model.Perm, error) { func (c *client) Perm(ctx context.Context, u *model.User, owner, name string) (*model.Perm, error) {
client := c.newClientToken(u.Token) client := c.newClientToken(ctx, u.Token)
repo, _, err := client.Repositories.Get(owner, name) repo, _, err := client.Repositories.Get(owner, name)
if err != nil { if err != nil {
return nil, err return nil, err
@ -225,8 +223,8 @@ func (c *client) Perm(u *model.User, owner, name string) (*model.Perm, error) {
} }
// File fetches the file from the GitHub repository and returns its contents. // File fetches the file from the GitHub repository and returns its contents.
func (c *client) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { func (c *client) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
client := c.newClientToken(u.Token) client := c.newClientToken(ctx, u.Token)
opts := new(github.RepositoryContentGetOptions) opts := new(github.RepositoryContentGetOptions)
opts.Ref = b.Commit opts.Ref = b.Commit
@ -240,8 +238,8 @@ func (c *client) File(u *model.User, r *model.Repo, b *model.Build, f string) ([
return data.Decode() return data.Decode()
} }
func (c *client) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { func (c *client) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) {
client := c.newClientToken(u.Token) client := c.newClientToken(ctx, u.Token)
opts := new(github.RepositoryContentGetOptions) opts := new(github.RepositoryContentGetOptions)
opts.Ref = b.Commit opts.Ref = b.Commit
@ -255,7 +253,7 @@ func (c *client) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]
for _, file := range data { for _, file := range data {
go func(path string) { go func(path string) {
content, err := c.File(u, r, b, path) content, err := c.File(ctx, u, r, b, path)
if err != nil { if err != nil {
errc <- err errc <- err
} else { } else {
@ -305,8 +303,8 @@ func (c *client) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
// Deactivate deactives the repository be removing registered push hooks from // Deactivate deactives the repository be removing registered push hooks from
// the GitHub repository. // the GitHub repository.
func (c *client) Deactivate(u *model.User, r *model.Repo, link string) error { func (c *client) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
client := c.newClientToken(u.Token) client := c.newClientToken(ctx, u.Token)
hooks, _, err := client.Repositories.ListHooks(r.Owner, r.Name, nil) hooks, _, err := client.Repositories.ListHooks(r.Owner, r.Name, nil)
if err != nil { if err != nil {
return err return err
@ -321,11 +319,11 @@ func (c *client) Deactivate(u *model.User, r *model.Repo, link string) error {
// helper function to return the GitHub oauth2 context using an HTTPClient that // helper function to return the GitHub oauth2 context using an HTTPClient that
// disables TLS verification if disabled in the remote settings. // disables TLS verification if disabled in the remote settings.
func (c *client) newContext() context.Context { func (c *client) newContext(ctx context.Context) context.Context {
if !c.SkipVerify { if !c.SkipVerify {
return oauth2.NoContext return ctx
} }
return context.WithValue(nil, oauth2.HTTPClient, &http.Client{ return context.WithValue(ctx, oauth2.HTTPClient, &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment, Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{ TLSClientConfig: &tls.Config{
@ -359,11 +357,11 @@ func (c *client) newConfig(req *http.Request) *oauth2.Config {
} }
// helper function to return the GitHub oauth2 client // helper function to return the GitHub oauth2 client
func (c *client) newClientToken(token string) *github.Client { func (c *client) newClientToken(ctx context.Context, token string) *github.Client {
ts := oauth2.StaticTokenSource( ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token}, &oauth2.Token{AccessToken: token},
) )
tc := oauth2.NewClient(oauth2.NoContext, ts) tc := oauth2.NewClient(ctx, ts)
if c.SkipVerify { if c.SkipVerify {
tc.Transport.(*oauth2.Transport).Base = &http.Transport{ tc.Transport.(*oauth2.Transport).Base = &http.Transport{
Proxy: http.ProxyFromEnvironment, Proxy: http.ProxyFromEnvironment,
@ -427,8 +425,8 @@ func matchingHooks(hooks []github.Hook, rawurl string) *github.Hook {
// Status sends the commit status to the remote system. // Status sends the commit status to the remote system.
// An example would be the GitHub pull request status. // An example would be the GitHub pull request status.
func (c *client) Status(u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { func (c *client) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
client := c.newClientToken(u.Token) client := c.newClientToken(ctx, u.Token)
switch b.Event { switch b.Event {
case "deployment": case "deployment":
return deploymentStatus(client, r, b, link) return deploymentStatus(client, r, b, link)
@ -486,11 +484,11 @@ func deploymentStatus(client *github.Client, r *model.Repo, b *model.Build, link
// Activate activates a repository by creating the post-commit hook and // Activate activates a repository by creating the post-commit hook and
// adding the SSH deploy key, if applicable. // adding the SSH deploy key, if applicable.
func (c *client) Activate(u *model.User, r *model.Repo, link string) error { func (c *client) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
if err := c.Deactivate(u, r, link); err != nil { if err := c.Deactivate(ctx, u, r, link); err != nil {
return err return err
} }
client := c.newClientToken(u.Token) client := c.newClientToken(ctx, u.Token)
hook := &github.Hook{ hook := &github.Hook{
Name: github.String("web"), Name: github.String("web"),
Events: []string{ Events: []string{

View file

@ -15,6 +15,7 @@
package github package github
import ( import (
"context"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@ -34,6 +35,7 @@ func Test_github(t *testing.T) {
SkipVerify: true, SkipVerify: true,
}) })
ctx := context.Background()
g := goblin.Goblin(t) g := goblin.Goblin(t)
g.Describe("GitHub", func() { g.Describe("GitHub", func() {
@ -95,7 +97,7 @@ func Test_github(t *testing.T) {
g.Describe("Requesting a repository", func() { g.Describe("Requesting a repository", func() {
g.It("Should return the repository details", func() { g.It("Should return the repository details", func() {
repo, err := c.Repo(fakeUser, fakeRepo.Owner, fakeRepo.Name) repo, err := c.Repo(ctx, fakeUser, fakeRepo.Owner, fakeRepo.Name)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(repo.Owner).Equal(fakeRepo.Owner) g.Assert(repo.Owner).Equal(fakeRepo.Owner)
g.Assert(repo.Name).Equal(fakeRepo.Name) g.Assert(repo.Name).Equal(fakeRepo.Name)
@ -105,21 +107,21 @@ func Test_github(t *testing.T) {
g.Assert(repo.Link).Equal(fakeRepo.Link) g.Assert(repo.Link).Equal(fakeRepo.Link)
}) })
g.It("Should handle a not found error", func() { g.It("Should handle a not found error", func() {
_, err := c.Repo(fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) _, err := c.Repo(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
}) })
g.Describe("Requesting repository permissions", func() { g.Describe("Requesting repository permissions", func() {
g.It("Should return the permission details", func() { g.It("Should return the permission details", func() {
perm, err := c.Perm(fakeUser, fakeRepo.Owner, fakeRepo.Name) perm, err := c.Perm(ctx, fakeUser, fakeRepo.Owner, fakeRepo.Name)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(perm.Admin).IsTrue() g.Assert(perm.Admin).IsTrue()
g.Assert(perm.Push).IsTrue() g.Assert(perm.Push).IsTrue()
g.Assert(perm.Pull).IsTrue() g.Assert(perm.Pull).IsTrue()
}) })
g.It("Should handle a not found error", func() { g.It("Should handle a not found error", func() {
_, err := c.Perm(fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) _, err := c.Perm(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
}) })

View file

@ -15,6 +15,7 @@
package gitlab package gitlab
import ( import (
"context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -113,7 +114,7 @@ func Load(config string) *Gitlab {
// Login authenticates the session and returns the // Login authenticates the session and returns the
// remote user details. // remote user details.
func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) { func (g *Gitlab) Login(ctx context.Context, res http.ResponseWriter, req *http.Request) (*model.User, error) {
var config = &oauth2.Config{ var config = &oauth2.Config{
ClientId: g.Client, ClientId: g.Client,
@ -193,7 +194,7 @@ func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User,
return user, nil return user, nil
} }
func (g *Gitlab) Auth(token, secret string) (string, error) { func (g *Gitlab) Auth(ctx context.Context, token, secret string) (string, error) {
client := NewClient(g.URL, token, g.SkipVerify) client := NewClient(g.URL, token, g.SkipVerify)
login, err := client.CurrentUser() login, err := client.CurrentUser()
if err != nil { if err != nil {
@ -202,7 +203,7 @@ func (g *Gitlab) Auth(token, secret string) (string, error) {
return login.Username, nil return login.Username, nil
} }
func (g *Gitlab) Teams(u *model.User) ([]*model.Team, error) { func (g *Gitlab) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) {
client := NewClient(g.URL, u.Token, g.SkipVerify) client := NewClient(g.URL, u.Token, g.SkipVerify)
groups, err := client.AllGroups() groups, err := client.AllGroups()
if err != nil { if err != nil {
@ -218,7 +219,7 @@ func (g *Gitlab) Teams(u *model.User) ([]*model.Team, error) {
} }
// Repo fetches the named repository from the remote system. // Repo fetches the named repository from the remote system.
func (g *Gitlab) Repo(u *model.User, owner, name string) (*model.Repo, error) { func (g *Gitlab) Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error) {
client := NewClient(g.URL, u.Token, g.SkipVerify) client := NewClient(g.URL, u.Token, g.SkipVerify)
id, err := GetProjectId(g, client, owner, name) id, err := GetProjectId(g, client, owner, name)
if err != nil { if err != nil {
@ -257,7 +258,7 @@ func (g *Gitlab) Repo(u *model.User, owner, name string) (*model.Repo, error) {
} }
// Repos fetches a list of repos from the remote system. // Repos fetches a list of repos from the remote system.
func (g *Gitlab) Repos(u *model.User) ([]*model.Repo, error) { func (g *Gitlab) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) {
client := NewClient(g.URL, u.Token, g.SkipVerify) client := NewClient(g.URL, u.Token, g.SkipVerify)
var repos = []*model.Repo{} var repos = []*model.Repo{}
@ -297,7 +298,7 @@ func (g *Gitlab) Repos(u *model.User) ([]*model.Repo, error) {
} }
// Perm fetches the named repository from the remote system. // Perm fetches the named repository from the remote system.
func (g *Gitlab) Perm(u *model.User, owner, name string) (*model.Perm, error) { func (g *Gitlab) Perm(ctx context.Context, u *model.User, owner, name string) (*model.Perm, error) {
client := NewClient(g.URL, u.Token, g.SkipVerify) client := NewClient(g.URL, u.Token, g.SkipVerify)
id, err := GetProjectId(g, client, owner, name) id, err := GetProjectId(g, client, owner, name)
@ -324,35 +325,35 @@ func (g *Gitlab) Perm(u *model.User, owner, name string) (*model.Perm, error) {
} }
// File fetches a file from the remote repository and returns in string format. // File fetches a file from the remote repository and returns in string format.
func (g *Gitlab) File(user *model.User, repo *model.Repo, build *model.Build, f string) ([]byte, error) { func (g *Gitlab) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
var client = NewClient(g.URL, user.Token, g.SkipVerify) var client = NewClient(g.URL, u.Token, g.SkipVerify)
id, err := GetProjectId(g, client, repo.Owner, repo.Name) id, err := GetProjectId(g, client, r.Owner, r.Name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
out, err := client.RepoRawFileRef(id, build.Commit, f) out, err := client.RepoRawFileRef(id, b.Commit, f)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return out, err return out, err
} }
func (c *Gitlab) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { func (c *Gitlab) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) {
return nil, fmt.Errorf("Not implemented") return nil, fmt.Errorf("Not implemented")
} }
// NOTE Currently gitlab doesn't support status for commits and events, // NOTE Currently gitlab doesn't support status for commits and events,
// also if we want get MR status in gitlab we need implement a special plugin for gitlab, // also if we want get MR status in gitlab we need implement a special plugin for gitlab,
// gitlab uses API to fetch build status on client side. But for now we skip this. // gitlab uses API to fetch build status on client side. But for now we skip this.
func (g *Gitlab) Status(u *model.User, repo *model.Repo, b *model.Build, link string, proc *model.Proc) error { func (g *Gitlab) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
client := NewClient(g.URL, u.Token, g.SkipVerify) client := NewClient(g.URL, u.Token, g.SkipVerify)
status := getStatus(b.Status) status := getStatus(b.Status)
desc := getDesc(b.Status) desc := getDesc(b.Status)
client.SetStatus( client.SetStatus(
ns(repo.Owner, repo.Name), ns(r.Owner, r.Name),
b.Commit, b.Commit,
status, status,
desc, desc,
@ -407,9 +408,9 @@ func (g *Gitlab) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
// Activate activates a repository by adding a Post-commit hook and // Activate activates a repository by adding a Post-commit hook and
// a Public Deploy key, if applicable. // a Public Deploy key, if applicable.
func (g *Gitlab) Activate(user *model.User, repo *model.Repo, link string) error { func (g *Gitlab) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
var client = NewClient(g.URL, user.Token, g.SkipVerify) var client = NewClient(g.URL, u.Token, g.SkipVerify)
id, err := GetProjectId(g, client, repo.Owner, repo.Name) id, err := GetProjectId(g, client, r.Owner, r.Name)
if err != nil { if err != nil {
return err return err
} }
@ -432,9 +433,9 @@ func (g *Gitlab) Activate(user *model.User, repo *model.Repo, link string) error
// Deactivate removes a repository by removing all the post-commit hooks // Deactivate removes a repository by removing all the post-commit hooks
// which are equal to link and removing the SSH deploy key. // which are equal to link and removing the SSH deploy key.
func (g *Gitlab) Deactivate(user *model.User, repo *model.Repo, link string) error { func (g *Gitlab) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
var client = NewClient(g.URL, user.Token, g.SkipVerify) var client = NewClient(g.URL, u.Token, g.SkipVerify)
id, err := GetProjectId(g, client, repo.Owner, repo.Name) id, err := GetProjectId(g, client, r.Owner, r.Name)
if err != nil { if err != nil {
return err return err
} }

View file

@ -16,6 +16,7 @@ package gitlab
import ( import (
"bytes" "bytes"
"context"
"net/http" "net/http"
"testing" "testing"
@ -43,13 +44,14 @@ func Test_Gitlab(t *testing.T) {
Owner: "diaspora", Owner: "diaspora",
} }
ctx := context.Background()
g := goblin.Goblin(t) g := goblin.Goblin(t)
g.Describe("Gitlab Plugin", func() { g.Describe("Gitlab Plugin", func() {
// Test projects method // Test projects method
g.Describe("AllProjects", func() { g.Describe("AllProjects", func() {
g.It("Should return only non-archived projects is hidden", func() { g.It("Should return only non-archived projects is hidden", func() {
gitlab.HideArchives = true gitlab.HideArchives = true
_projects, err := gitlab.Repos(&user) _projects, err := gitlab.Repos(ctx, &user)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(len(_projects)).Equal(1) g.Assert(len(_projects)).Equal(1)
@ -57,7 +59,7 @@ func Test_Gitlab(t *testing.T) {
g.It("Should return all the projects", func() { g.It("Should return all the projects", func() {
gitlab.HideArchives = false gitlab.HideArchives = false
_projects, err := gitlab.Repos(&user) _projects, err := gitlab.Repos(ctx, &user)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(len(_projects)).Equal(2) g.Assert(len(_projects)).Equal(2)
@ -67,7 +69,7 @@ func Test_Gitlab(t *testing.T) {
// Test repository method // Test repository method
g.Describe("Repo", func() { g.Describe("Repo", func() {
g.It("Should return valid repo", func() { g.It("Should return valid repo", func() {
_repo, err := gitlab.Repo(&user, "diaspora", "diaspora-client") _repo, err := gitlab.Repo(ctx, &user, "diaspora", "diaspora-client")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(_repo.Name).Equal("diaspora-client") g.Assert(_repo.Name).Equal("diaspora-client")
@ -76,7 +78,7 @@ func Test_Gitlab(t *testing.T) {
}) })
g.It("Should return error, when repo not exist", func() { g.It("Should return error, when repo not exist", func() {
_, err := gitlab.Repo(&user, "not-existed", "not-existed") _, err := gitlab.Repo(ctx, &user, "not-existed", "not-existed")
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
@ -85,21 +87,21 @@ func Test_Gitlab(t *testing.T) {
// Test permissions method // Test permissions method
g.Describe("Perm", func() { g.Describe("Perm", func() {
g.It("Should return repo permissions", func() { g.It("Should return repo permissions", func() {
perm, err := gitlab.Perm(&user, "diaspora", "diaspora-client") perm, err := gitlab.Perm(ctx, &user, "diaspora", "diaspora-client")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(perm.Admin).Equal(true) g.Assert(perm.Admin).Equal(true)
g.Assert(perm.Pull).Equal(true) g.Assert(perm.Pull).Equal(true)
g.Assert(perm.Push).Equal(true) g.Assert(perm.Push).Equal(true)
}) })
g.It("Should return repo permissions when user is admin", func() { g.It("Should return repo permissions when user is admin", func() {
perm, err := gitlab.Perm(&user, "brightbox", "puppet") perm, err := gitlab.Perm(ctx, &user, "brightbox", "puppet")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(perm.Admin).Equal(true) g.Assert(perm.Admin).Equal(true)
g.Assert(perm.Pull).Equal(true) g.Assert(perm.Pull).Equal(true)
g.Assert(perm.Push).Equal(true) g.Assert(perm.Push).Equal(true)
}) })
g.It("Should return error, when repo is not exist", func() { g.It("Should return error, when repo is not exist", func() {
_, err := gitlab.Perm(&user, "not-existed", "not-existed") _, err := gitlab.Perm(ctx, &user, "not-existed", "not-existed")
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
@ -108,13 +110,13 @@ func Test_Gitlab(t *testing.T) {
// Test activate method // Test activate method
g.Describe("Activate", func() { g.Describe("Activate", func() {
g.It("Should be success", func() { g.It("Should be success", func() {
err := gitlab.Activate(&user, &repo, "http://example.com/api/hook/test/test?access_token=token") err := gitlab.Activate(ctx, &user, &repo, "http://example.com/api/hook/test/test?access_token=token")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
}) })
g.It("Should be failed, when token not given", func() { g.It("Should be failed, when token not given", func() {
err := gitlab.Activate(&user, &repo, "http://example.com/api/hook/test/test") err := gitlab.Activate(ctx, &user, &repo, "http://example.com/api/hook/test/test")
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
@ -123,7 +125,7 @@ func Test_Gitlab(t *testing.T) {
// Test deactivate method // Test deactivate method
g.Describe("Deactivate", func() { g.Describe("Deactivate", func() {
g.It("Should be success", func() { g.It("Should be success", func() {
err := gitlab.Deactivate(&user, &repo, "http://example.com/api/hook/test/test?access_token=token") err := gitlab.Deactivate(ctx, &user, &repo, "http://example.com/api/hook/test/test?access_token=token")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
}) })

View file

@ -15,6 +15,7 @@
package gitlab3 package gitlab3
import ( import (
"context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -113,7 +114,7 @@ func Load(config string) *Gitlab {
// Login authenticates the session and returns the // Login authenticates the session and returns the
// remote user details. // remote user details.
func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) { func (g *Gitlab) Login(ctx context.Context, res http.ResponseWriter, req *http.Request) (*model.User, error) {
var config = &oauth2.Config{ var config = &oauth2.Config{
ClientId: g.Client, ClientId: g.Client,
@ -193,7 +194,7 @@ func (g *Gitlab) Login(res http.ResponseWriter, req *http.Request) (*model.User,
return user, nil return user, nil
} }
func (g *Gitlab) Auth(token, secret string) (string, error) { func (g *Gitlab) Auth(ctx context.Context, token, secret string) (string, error) {
client := NewClient(g.URL, token, g.SkipVerify) client := NewClient(g.URL, token, g.SkipVerify)
login, err := client.CurrentUser() login, err := client.CurrentUser()
if err != nil { if err != nil {
@ -202,7 +203,7 @@ func (g *Gitlab) Auth(token, secret string) (string, error) {
return login.Username, nil return login.Username, nil
} }
func (g *Gitlab) Teams(u *model.User) ([]*model.Team, error) { func (g *Gitlab) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) {
client := NewClient(g.URL, u.Token, g.SkipVerify) client := NewClient(g.URL, u.Token, g.SkipVerify)
groups, err := client.AllGroups() groups, err := client.AllGroups()
if err != nil { if err != nil {
@ -218,7 +219,7 @@ func (g *Gitlab) Teams(u *model.User) ([]*model.Team, error) {
} }
// Repo fetches the named repository from the remote system. // Repo fetches the named repository from the remote system.
func (g *Gitlab) Repo(u *model.User, owner, name string) (*model.Repo, error) { func (g *Gitlab) Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error) {
client := NewClient(g.URL, u.Token, g.SkipVerify) client := NewClient(g.URL, u.Token, g.SkipVerify)
id, err := GetProjectId(g, client, owner, name) id, err := GetProjectId(g, client, owner, name)
if err != nil { if err != nil {
@ -257,7 +258,7 @@ func (g *Gitlab) Repo(u *model.User, owner, name string) (*model.Repo, error) {
} }
// Repos fetches a list of repos from the remote system. // Repos fetches a list of repos from the remote system.
func (g *Gitlab) Repos(u *model.User) ([]*model.Repo, error) { func (g *Gitlab) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) {
client := NewClient(g.URL, u.Token, g.SkipVerify) client := NewClient(g.URL, u.Token, g.SkipVerify)
var repos = []*model.Repo{} var repos = []*model.Repo{}
@ -297,7 +298,7 @@ func (g *Gitlab) Repos(u *model.User) ([]*model.Repo, error) {
} }
// Perm fetches the named repository from the remote system. // Perm fetches the named repository from the remote system.
func (g *Gitlab) Perm(u *model.User, owner, name string) (*model.Perm, error) { func (g *Gitlab) Perm(ctx context.Context, u *model.User, owner, name string) (*model.Perm, error) {
client := NewClient(g.URL, u.Token, g.SkipVerify) client := NewClient(g.URL, u.Token, g.SkipVerify)
id, err := GetProjectId(g, client, owner, name) id, err := GetProjectId(g, client, owner, name)
@ -324,7 +325,7 @@ func (g *Gitlab) Perm(u *model.User, owner, name string) (*model.Perm, error) {
} }
// File fetches a file from the remote repository and returns in string format. // File fetches a file from the remote repository and returns in string format.
func (g *Gitlab) File(user *model.User, repo *model.Repo, build *model.Build, f string) ([]byte, error) { func (g *Gitlab) File(ctx context.Context, user *model.User, repo *model.Repo, build *model.Build, f string) ([]byte, error) {
var client = NewClient(g.URL, user.Token, g.SkipVerify) var client = NewClient(g.URL, user.Token, g.SkipVerify)
id, err := GetProjectId(g, client, repo.Owner, repo.Name) id, err := GetProjectId(g, client, repo.Owner, repo.Name)
if err != nil { if err != nil {
@ -338,21 +339,21 @@ func (g *Gitlab) File(user *model.User, repo *model.Repo, build *model.Build, f
return out, err return out, err
} }
func (c *Gitlab) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { func (c *Gitlab) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) {
return nil, fmt.Errorf("Not implemented") return nil, fmt.Errorf("Not implemented")
} }
// NOTE Currently gitlab doesn't support status for commits and events, // NOTE Currently gitlab doesn't support status for commits and events,
// also if we want get MR status in gitlab we need implement a special plugin for gitlab, // also if we want get MR status in gitlab we need implement a special plugin for gitlab,
// gitlab uses API to fetch build status on client side. But for now we skip this. // gitlab uses API to fetch build status on client side. But for now we skip this.
func (g *Gitlab) Status(u *model.User, repo *model.Repo, b *model.Build, link string, proc *model.Proc) error { func (g *Gitlab) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
client := NewClient(g.URL, u.Token, g.SkipVerify) client := NewClient(g.URL, u.Token, g.SkipVerify)
status := getStatus(b.Status) status := getStatus(b.Status)
desc := getDesc(b.Status) desc := getDesc(b.Status)
client.SetStatus( client.SetStatus(
ns(repo.Owner, repo.Name), ns(r.Owner, r.Name),
b.Commit, b.Commit,
status, status,
desc, desc,
@ -407,9 +408,9 @@ func (g *Gitlab) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
// Activate activates a repository by adding a Post-commit hook and // Activate activates a repository by adding a Post-commit hook and
// a Public Deploy key, if applicable. // a Public Deploy key, if applicable.
func (g *Gitlab) Activate(user *model.User, repo *model.Repo, link string) error { func (g *Gitlab) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
var client = NewClient(g.URL, user.Token, g.SkipVerify) var client = NewClient(g.URL, u.Token, g.SkipVerify)
id, err := GetProjectId(g, client, repo.Owner, repo.Name) id, err := GetProjectId(g, client, r.Owner, r.Name)
if err != nil { if err != nil {
return err return err
} }
@ -432,9 +433,9 @@ func (g *Gitlab) Activate(user *model.User, repo *model.Repo, link string) error
// Deactivate removes a repository by removing all the post-commit hooks // Deactivate removes a repository by removing all the post-commit hooks
// which are equal to link and removing the SSH deploy key. // which are equal to link and removing the SSH deploy key.
func (g *Gitlab) Deactivate(user *model.User, repo *model.Repo, link string) error { func (g *Gitlab) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
var client = NewClient(g.URL, user.Token, g.SkipVerify) var client = NewClient(g.URL, u.Token, g.SkipVerify)
id, err := GetProjectId(g, client, repo.Owner, repo.Name) id, err := GetProjectId(g, client, r.Owner, r.Name)
if err != nil { if err != nil {
return err return err
} }

View file

@ -49,7 +49,7 @@ func Test_Gitlab(t *testing.T) {
g.Describe("AllProjects", func() { g.Describe("AllProjects", func() {
g.It("Should return only non-archived projects is hidden", func() { g.It("Should return only non-archived projects is hidden", func() {
gitlab.HideArchives = true gitlab.HideArchives = true
_projects, err := gitlab.Repos(&user) _projects, err := gitlab.Repos(nil, &user)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(len(_projects)).Equal(1) g.Assert(len(_projects)).Equal(1)
@ -57,7 +57,7 @@ func Test_Gitlab(t *testing.T) {
g.It("Should return all the projects", func() { g.It("Should return all the projects", func() {
gitlab.HideArchives = false gitlab.HideArchives = false
_projects, err := gitlab.Repos(&user) _projects, err := gitlab.Repos(nil, &user)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(len(_projects)).Equal(2) g.Assert(len(_projects)).Equal(2)
@ -67,7 +67,7 @@ func Test_Gitlab(t *testing.T) {
// Test repository method // Test repository method
g.Describe("Repo", func() { g.Describe("Repo", func() {
g.It("Should return valid repo", func() { g.It("Should return valid repo", func() {
_repo, err := gitlab.Repo(&user, "diaspora", "diaspora-client") _repo, err := gitlab.Repo(nil, &user, "diaspora", "diaspora-client")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(_repo.Name).Equal("diaspora-client") g.Assert(_repo.Name).Equal("diaspora-client")
@ -76,7 +76,7 @@ func Test_Gitlab(t *testing.T) {
}) })
g.It("Should return error, when repo not exist", func() { g.It("Should return error, when repo not exist", func() {
_, err := gitlab.Repo(&user, "not-existed", "not-existed") _, err := gitlab.Repo(nil, &user, "not-existed", "not-existed")
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
@ -85,21 +85,21 @@ func Test_Gitlab(t *testing.T) {
// Test permissions method // Test permissions method
g.Describe("Perm", func() { g.Describe("Perm", func() {
g.It("Should return repo permissions", func() { g.It("Should return repo permissions", func() {
perm, err := gitlab.Perm(&user, "diaspora", "diaspora-client") perm, err := gitlab.Perm(nil, &user, "diaspora", "diaspora-client")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(perm.Admin).Equal(true) g.Assert(perm.Admin).Equal(true)
g.Assert(perm.Pull).Equal(true) g.Assert(perm.Pull).Equal(true)
g.Assert(perm.Push).Equal(true) g.Assert(perm.Push).Equal(true)
}) })
g.It("Should return repo permissions when user is admin", func() { g.It("Should return repo permissions when user is admin", func() {
perm, err := gitlab.Perm(&user, "brightbox", "puppet") perm, err := gitlab.Perm(nil, &user, "brightbox", "puppet")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(perm.Admin).Equal(true) g.Assert(perm.Admin).Equal(true)
g.Assert(perm.Pull).Equal(true) g.Assert(perm.Pull).Equal(true)
g.Assert(perm.Push).Equal(true) g.Assert(perm.Push).Equal(true)
}) })
g.It("Should return error, when repo is not exist", func() { g.It("Should return error, when repo is not exist", func() {
_, err := gitlab.Perm(&user, "not-existed", "not-existed") _, err := gitlab.Perm(nil, &user, "not-existed", "not-existed")
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
@ -108,13 +108,13 @@ func Test_Gitlab(t *testing.T) {
// Test activate method // Test activate method
g.Describe("Activate", func() { g.Describe("Activate", func() {
g.It("Should be success", func() { g.It("Should be success", func() {
err := gitlab.Activate(&user, &repo, "http://example.com/api/hook/test/test?access_token=token") err := gitlab.Activate(nil, &user, &repo, "http://example.com/api/hook/test/test?access_token=token")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
}) })
g.It("Should be failed, when token not given", func() { g.It("Should be failed, when token not given", func() {
err := gitlab.Activate(&user, &repo, "http://example.com/api/hook/test/test") err := gitlab.Activate(nil, &user, &repo, "http://example.com/api/hook/test/test")
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
@ -123,7 +123,7 @@ func Test_Gitlab(t *testing.T) {
// Test deactivate method // Test deactivate method
g.Describe("Deactivate", func() { g.Describe("Deactivate", func() {
g.It("Should be success", func() { g.It("Should be success", func() {
err := gitlab.Deactivate(&user, &repo, "http://example.com/api/hook/test/test?access_token=token") err := gitlab.Deactivate(nil, &user, &repo, "http://example.com/api/hook/test/test?access_token=token")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
}) })

View file

@ -15,6 +15,7 @@
package gogs package gogs
import ( import (
"context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"net" "net"
@ -68,7 +69,7 @@ func New(opts Opts) (remote.Remote, error) {
// Login authenticates an account with Gogs using basic authentication. The // Login authenticates an account with Gogs using basic authentication. The
// Gogs account details are returned when the user is successfully authenticated. // Gogs account details are returned when the user is successfully authenticated.
func (c *client) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) { func (c *client) Login(ctx context.Context, res http.ResponseWriter, req *http.Request) (*model.User, error) {
var ( var (
username = req.FormValue("username") username = req.FormValue("username")
password = req.FormValue("password") password = req.FormValue("password")
@ -122,12 +123,12 @@ func (c *client) Login(res http.ResponseWriter, req *http.Request) (*model.User,
} }
// Auth is not supported by the Gogs driver. // Auth is not supported by the Gogs driver.
func (c *client) Auth(token, secret string) (string, error) { func (c *client) Auth(ctx context.Context, token, secret string) (string, error) {
return "", fmt.Errorf("Not Implemented") return "", fmt.Errorf("Not Implemented")
} }
// Teams is not supported by the Gogs driver. // Teams is not supported by the Gogs driver.
func (c *client) Teams(u *model.User) ([]*model.Team, error) { func (c *client) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) {
client := c.newClientToken(u.Token) client := c.newClientToken(u.Token)
orgs, err := client.ListMyOrgs() orgs, err := client.ListMyOrgs()
if err != nil { if err != nil {
@ -142,7 +143,7 @@ func (c *client) Teams(u *model.User) ([]*model.Team, error) {
} }
// Repo returns the named Gogs repository. // Repo returns the named Gogs repository.
func (c *client) Repo(u *model.User, owner, name string) (*model.Repo, error) { func (c *client) Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error) {
client := c.newClientToken(u.Token) client := c.newClientToken(u.Token)
repo, err := client.GetRepo(owner, name) repo, err := client.GetRepo(owner, name)
if err != nil { if err != nil {
@ -153,7 +154,7 @@ func (c *client) Repo(u *model.User, owner, name string) (*model.Repo, error) {
// Repos returns a list of all repositories for the Gogs account, including // Repos returns a list of all repositories for the Gogs account, including
// organization repositories. // organization repositories.
func (c *client) Repos(u *model.User) ([]*model.Repo, error) { func (c *client) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) {
repos := []*model.Repo{} repos := []*model.Repo{}
client := c.newClientToken(u.Token) client := c.newClientToken(u.Token)
@ -169,7 +170,7 @@ func (c *client) Repos(u *model.User) ([]*model.Repo, error) {
} }
// Perm returns the user permissions for the named Gogs repository. // Perm returns the user permissions for the named Gogs repository.
func (c *client) Perm(u *model.User, owner, name string) (*model.Perm, error) { func (c *client) Perm(ctx context.Context, u *model.User, owner, name string) (*model.Perm, error) {
client := c.newClientToken(u.Token) client := c.newClientToken(u.Token)
repo, err := client.GetRepo(owner, name) repo, err := client.GetRepo(owner, name)
if err != nil { if err != nil {
@ -179,7 +180,7 @@ func (c *client) Perm(u *model.User, owner, name string) (*model.Perm, error) {
} }
// File fetches the file from the Gogs repository and returns its contents. // File fetches the file from the Gogs repository and returns its contents.
func (c *client) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) { func (c *client) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
client := c.newClientToken(u.Token) client := c.newClientToken(u.Token)
ref := b.Commit ref := b.Commit
@ -202,12 +203,12 @@ func (c *client) File(u *model.User, r *model.Repo, b *model.Build, f string) ([
return cfg, err return cfg, err
} }
func (c *client) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) { func (c *client) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) {
return nil, fmt.Errorf("Not implemented") return nil, fmt.Errorf("Not implemented")
} }
// Status is not supported by the Gogs driver. // Status is not supported by the Gogs driver.
func (c *client) Status(u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { func (c *client) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
return nil return nil
} }
@ -231,7 +232,7 @@ func (c *client) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
// Activate activates the repository by registering post-commit hooks with // Activate activates the repository by registering post-commit hooks with
// the Gogs repository. // the Gogs repository.
func (c *client) Activate(u *model.User, r *model.Repo, link string) error { func (c *client) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
config := map[string]string{ config := map[string]string{
"url": link, "url": link,
"secret": r.Hash, "secret": r.Hash,
@ -250,7 +251,7 @@ func (c *client) Activate(u *model.User, r *model.Repo, link string) error {
} }
// Deactivate is not supported by the Gogs driver. // Deactivate is not supported by the Gogs driver.
func (c *client) Deactivate(u *model.User, r *model.Repo, link string) error { func (c *client) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
return nil return nil
} }

View file

@ -15,6 +15,7 @@
package gogs package gogs
import ( import (
"context"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@ -34,6 +35,7 @@ func Test_gogs(t *testing.T) {
SkipVerify: true, SkipVerify: true,
}) })
ctx := context.Background()
g := goblin.Goblin(t) g := goblin.Goblin(t)
g.Describe("Gogs", func() { g.Describe("Gogs", func() {
@ -88,7 +90,7 @@ func Test_gogs(t *testing.T) {
g.Describe("Requesting a repository", func() { g.Describe("Requesting a repository", func() {
g.It("Should return the repository details", func() { g.It("Should return the repository details", func() {
repo, err := c.Repo(fakeUser, fakeRepo.Owner, fakeRepo.Name) repo, err := c.Repo(ctx, fakeUser, fakeRepo.Owner, fakeRepo.Name)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(repo.Owner).Equal(fakeRepo.Owner) g.Assert(repo.Owner).Equal(fakeRepo.Owner)
g.Assert(repo.Name).Equal(fakeRepo.Name) g.Assert(repo.Name).Equal(fakeRepo.Name)
@ -98,52 +100,52 @@ func Test_gogs(t *testing.T) {
g.Assert(repo.Link).Equal("http://localhost/test_name/repo_name") g.Assert(repo.Link).Equal("http://localhost/test_name/repo_name")
}) })
g.It("Should handle a not found error", func() { g.It("Should handle a not found error", func() {
_, err := c.Repo(fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) _, err := c.Repo(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
}) })
g.Describe("Requesting repository permissions", func() { g.Describe("Requesting repository permissions", func() {
g.It("Should return the permission details", func() { g.It("Should return the permission details", func() {
perm, err := c.Perm(fakeUser, fakeRepo.Owner, fakeRepo.Name) perm, err := c.Perm(ctx, fakeUser, fakeRepo.Owner, fakeRepo.Name)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(perm.Admin).IsTrue() g.Assert(perm.Admin).IsTrue()
g.Assert(perm.Push).IsTrue() g.Assert(perm.Push).IsTrue()
g.Assert(perm.Pull).IsTrue() g.Assert(perm.Pull).IsTrue()
}) })
g.It("Should handle a not found error", func() { g.It("Should handle a not found error", func() {
_, err := c.Perm(fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name) _, err := c.Perm(ctx, fakeUser, fakeRepoNotFound.Owner, fakeRepoNotFound.Name)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
}) })
g.Describe("Requesting a repository list", func() { g.Describe("Requesting a repository list", func() {
g.It("Should return the repository list", func() { g.It("Should return the repository list", func() {
repos, err := c.Repos(fakeUser) repos, err := c.Repos(ctx, fakeUser)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(repos[0].Owner).Equal(fakeRepo.Owner) g.Assert(repos[0].Owner).Equal(fakeRepo.Owner)
g.Assert(repos[0].Name).Equal(fakeRepo.Name) g.Assert(repos[0].Name).Equal(fakeRepo.Name)
g.Assert(repos[0].FullName).Equal(fakeRepo.Owner + "/" + fakeRepo.Name) g.Assert(repos[0].FullName).Equal(fakeRepo.Owner + "/" + fakeRepo.Name)
}) })
g.It("Should handle a not found error", func() { g.It("Should handle a not found error", func() {
_, err := c.Repos(fakeUserNoRepos) _, err := c.Repos(ctx, fakeUserNoRepos)
g.Assert(err != nil).IsTrue() g.Assert(err != nil).IsTrue()
}) })
}) })
g.It("Should register repositroy hooks", func() { g.It("Should register repositroy hooks", func() {
err := c.Activate(fakeUser, fakeRepo, "http://localhost") err := c.Activate(ctx, fakeUser, fakeRepo, "http://localhost")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
}) })
g.It("Should return a repository file", func() { g.It("Should return a repository file", func() {
raw, err := c.File(fakeUser, fakeRepo, fakeBuild, ".drone.yml") raw, err := c.File(ctx, fakeUser, fakeRepo, fakeBuild, ".drone.yml")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(string(raw)).Equal("{ platform: linux/amd64 }") g.Assert(string(raw)).Equal("{ platform: linux/amd64 }")
}) })
g.It("Should return a repository file from a ref", func() { g.It("Should return a repository file from a ref", func() {
raw, err := c.File(fakeUser, fakeRepo, fakeBuildWithRef, ".drone.yml") raw, err := c.File(ctx, fakeUser, fakeRepo, fakeBuildWithRef, ".drone.yml")
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(string(raw)).Equal("{ platform: linux/amd64 }") g.Assert(string(raw)).Equal("{ platform: linux/amd64 }")
}) })
@ -162,9 +164,9 @@ func Test_gogs(t *testing.T) {
}) })
g.It("Should return no-op for usupporeted features", func() { g.It("Should return no-op for usupporeted features", func() {
_, err1 := c.Auth("octocat", "4vyW6b49Z") _, err1 := c.Auth(ctx, "octocat", "4vyW6b49Z")
err2 := c.Status(nil, nil, nil, "", nil) err2 := c.Status(ctx, nil, nil, nil, "", nil)
err3 := c.Deactivate(nil, nil, "") err3 := c.Deactivate(ctx, nil, nil, "")
g.Assert(err1 != nil).IsTrue() g.Assert(err1 != nil).IsTrue()
g.Assert(err2 == nil).IsTrue() g.Assert(err2 == nil).IsTrue()
g.Assert(err3 == nil).IsTrue() g.Assert(err3 == nil).IsTrue()

View file

@ -1,296 +0,0 @@
// Code generated by mockery v0.0.0-dev. DO NOT EDIT.
package mocks
import (
http "net/http"
mock "github.com/stretchr/testify/mock"
model "github.com/woodpecker-ci/woodpecker/server/model"
remote "github.com/woodpecker-ci/woodpecker/server/remote"
)
// Remote is an autogenerated mock type for the Remote type
type Remote struct {
mock.Mock
}
// Activate provides a mock function with given fields: u, r, link
func (_m *Remote) Activate(u *model.User, r *model.Repo, link string) error {
ret := _m.Called(u, r, link)
var r0 error
if rf, ok := ret.Get(0).(func(*model.User, *model.Repo, string) error); ok {
r0 = rf(u, r, link)
} else {
r0 = ret.Error(0)
}
return r0
}
// Auth provides a mock function with given fields: token, secret
func (_m *Remote) Auth(token string, secret string) (string, error) {
ret := _m.Called(token, secret)
var r0 string
if rf, ok := ret.Get(0).(func(string, string) string); ok {
r0 = rf(token, secret)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(string, string) error); ok {
r1 = rf(token, secret)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Deactivate provides a mock function with given fields: u, r, link
func (_m *Remote) Deactivate(u *model.User, r *model.Repo, link string) error {
ret := _m.Called(u, r, link)
var r0 error
if rf, ok := ret.Get(0).(func(*model.User, *model.Repo, string) error); ok {
r0 = rf(u, r, link)
} else {
r0 = ret.Error(0)
}
return r0
}
// Dir provides a mock function with given fields: u, r, b, f
func (_m *Remote) Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) {
ret := _m.Called(u, r, b, f)
var r0 []*remote.FileMeta
if rf, ok := ret.Get(0).(func(*model.User, *model.Repo, *model.Build, string) []*remote.FileMeta); ok {
r0 = rf(u, r, b, f)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*remote.FileMeta)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*model.User, *model.Repo, *model.Build, string) error); ok {
r1 = rf(u, r, b, f)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// File provides a mock function with given fields: u, r, b, f
func (_m *Remote) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
ret := _m.Called(u, r, b, f)
var r0 []byte
if rf, ok := ret.Get(0).(func(*model.User, *model.Repo, *model.Build, string) []byte); ok {
r0 = rf(u, r, b, f)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]byte)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*model.User, *model.Repo, *model.Build, string) error); ok {
r1 = rf(u, r, b, f)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Hook provides a mock function with given fields: r
func (_m *Remote) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
ret := _m.Called(r)
var r0 *model.Repo
if rf, ok := ret.Get(0).(func(*http.Request) *model.Repo); ok {
r0 = rf(r)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Repo)
}
}
var r1 *model.Build
if rf, ok := ret.Get(1).(func(*http.Request) *model.Build); ok {
r1 = rf(r)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.Build)
}
}
var r2 error
if rf, ok := ret.Get(2).(func(*http.Request) error); ok {
r2 = rf(r)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
// Login provides a mock function with given fields: w, r
func (_m *Remote) Login(w http.ResponseWriter, r *http.Request) (*model.User, error) {
ret := _m.Called(w, r)
var r0 *model.User
if rf, ok := ret.Get(0).(func(http.ResponseWriter, *http.Request) *model.User); ok {
r0 = rf(w, r)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.User)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(http.ResponseWriter, *http.Request) error); ok {
r1 = rf(w, r)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Netrc provides a mock function with given fields: u, r
func (_m *Remote) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
ret := _m.Called(u, r)
var r0 *model.Netrc
if rf, ok := ret.Get(0).(func(*model.User, *model.Repo) *model.Netrc); ok {
r0 = rf(u, r)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Netrc)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*model.User, *model.Repo) error); ok {
r1 = rf(u, r)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Perm provides a mock function with given fields: u, owner, repo
func (_m *Remote) Perm(u *model.User, owner string, repo string) (*model.Perm, error) {
ret := _m.Called(u, owner, repo)
var r0 *model.Perm
if rf, ok := ret.Get(0).(func(*model.User, string, string) *model.Perm); ok {
r0 = rf(u, owner, repo)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Perm)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*model.User, string, string) error); ok {
r1 = rf(u, owner, repo)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Repo provides a mock function with given fields: u, owner, repo
func (_m *Remote) Repo(u *model.User, owner string, repo string) (*model.Repo, error) {
ret := _m.Called(u, owner, repo)
var r0 *model.Repo
if rf, ok := ret.Get(0).(func(*model.User, string, string) *model.Repo); ok {
r0 = rf(u, owner, repo)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Repo)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*model.User, string, string) error); ok {
r1 = rf(u, owner, repo)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Repos provides a mock function with given fields: u
func (_m *Remote) Repos(u *model.User) ([]*model.Repo, error) {
ret := _m.Called(u)
var r0 []*model.Repo
if rf, ok := ret.Get(0).(func(*model.User) []*model.Repo); ok {
r0 = rf(u)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Repo)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*model.User) error); ok {
r1 = rf(u)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Status provides a mock function with given fields: u, r, b, link, proc
func (_m *Remote) Status(u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
ret := _m.Called(u, r, b, link, proc)
var r0 error
if rf, ok := ret.Get(0).(func(*model.User, *model.Repo, *model.Build, string, *model.Proc) error); ok {
r0 = rf(u, r, b, link, proc)
} else {
r0 = ret.Error(0)
}
return r0
}
// Teams provides a mock function with given fields: u
func (_m *Remote) Teams(u *model.User) ([]*model.Team, error) {
ret := _m.Called(u)
var r0 []*model.Team
if rf, ok := ret.Get(0).(func(*model.User) []*model.Team); ok {
r0 = rf(u)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Team)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*model.User) error); ok {
r1 = rf(u)
} else {
r1 = ret.Error(1)
}
return r0, r1
}

View file

@ -0,0 +1,298 @@
// Code generated by mockery v1.0.0. DO NOT EDIT.
package mocks
import (
context "context"
http "net/http"
mock "github.com/stretchr/testify/mock"
model "github.com/woodpecker-ci/woodpecker/server/model"
remote "github.com/woodpecker-ci/woodpecker/server/remote"
)
// Remote is an autogenerated mock type for the Remote type
type Remote struct {
mock.Mock
}
// Activate provides a mock function with given fields: ctx, u, r, link
func (_m *Remote) Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
ret := _m.Called(ctx, u, r, link)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *model.User, *model.Repo, string) error); ok {
r0 = rf(ctx, u, r, link)
} else {
r0 = ret.Error(0)
}
return r0
}
// Auth provides a mock function with given fields: ctx, token, secret
func (_m *Remote) Auth(ctx context.Context, token string, secret string) (string, error) {
ret := _m.Called(ctx, token, secret)
var r0 string
if rf, ok := ret.Get(0).(func(context.Context, string, string) string); ok {
r0 = rf(ctx, token, secret)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
r1 = rf(ctx, token, secret)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Deactivate provides a mock function with given fields: ctx, u, r, link
func (_m *Remote) Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error {
ret := _m.Called(ctx, u, r, link)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *model.User, *model.Repo, string) error); ok {
r0 = rf(ctx, u, r, link)
} else {
r0 = ret.Error(0)
}
return r0
}
// Dir provides a mock function with given fields: ctx, u, r, b, f
func (_m *Remote) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*remote.FileMeta, error) {
ret := _m.Called(ctx, u, r, b, f)
var r0 []*remote.FileMeta
if rf, ok := ret.Get(0).(func(context.Context, *model.User, *model.Repo, *model.Build, string) []*remote.FileMeta); ok {
r0 = rf(ctx, u, r, b, f)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*remote.FileMeta)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *model.User, *model.Repo, *model.Build, string) error); ok {
r1 = rf(ctx, u, r, b, f)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// File provides a mock function with given fields: ctx, u, r, b, f
func (_m *Remote) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
ret := _m.Called(ctx, u, r, b, f)
var r0 []byte
if rf, ok := ret.Get(0).(func(context.Context, *model.User, *model.Repo, *model.Build, string) []byte); ok {
r0 = rf(ctx, u, r, b, f)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]byte)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *model.User, *model.Repo, *model.Build, string) error); ok {
r1 = rf(ctx, u, r, b, f)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Hook provides a mock function with given fields: r
func (_m *Remote) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
ret := _m.Called(r)
var r0 *model.Repo
if rf, ok := ret.Get(0).(func(*http.Request) *model.Repo); ok {
r0 = rf(r)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Repo)
}
}
var r1 *model.Build
if rf, ok := ret.Get(1).(func(*http.Request) *model.Build); ok {
r1 = rf(r)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.Build)
}
}
var r2 error
if rf, ok := ret.Get(2).(func(*http.Request) error); ok {
r2 = rf(r)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
// Login provides a mock function with given fields: ctx, w, r
func (_m *Remote) Login(ctx context.Context, w http.ResponseWriter, r *http.Request) (*model.User, error) {
ret := _m.Called(ctx, w, r)
var r0 *model.User
if rf, ok := ret.Get(0).(func(context.Context, http.ResponseWriter, *http.Request) *model.User); ok {
r0 = rf(ctx, w, r)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.User)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, http.ResponseWriter, *http.Request) error); ok {
r1 = rf(ctx, w, r)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Netrc provides a mock function with given fields: u, r
func (_m *Remote) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
ret := _m.Called(u, r)
var r0 *model.Netrc
if rf, ok := ret.Get(0).(func(*model.User, *model.Repo) *model.Netrc); ok {
r0 = rf(u, r)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Netrc)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*model.User, *model.Repo) error); ok {
r1 = rf(u, r)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Perm provides a mock function with given fields: ctx, u, owner, repo
func (_m *Remote) Perm(ctx context.Context, u *model.User, owner string, repo string) (*model.Perm, error) {
ret := _m.Called(ctx, u, owner, repo)
var r0 *model.Perm
if rf, ok := ret.Get(0).(func(context.Context, *model.User, string, string) *model.Perm); ok {
r0 = rf(ctx, u, owner, repo)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Perm)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *model.User, string, string) error); ok {
r1 = rf(ctx, u, owner, repo)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Repo provides a mock function with given fields: ctx, u, owner, name
func (_m *Remote) Repo(ctx context.Context, u *model.User, owner string, name string) (*model.Repo, error) {
ret := _m.Called(ctx, u, owner, name)
var r0 *model.Repo
if rf, ok := ret.Get(0).(func(context.Context, *model.User, string, string) *model.Repo); ok {
r0 = rf(ctx, u, owner, name)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Repo)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *model.User, string, string) error); ok {
r1 = rf(ctx, u, owner, name)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Repos provides a mock function with given fields: ctx, u
func (_m *Remote) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error) {
ret := _m.Called(ctx, u)
var r0 []*model.Repo
if rf, ok := ret.Get(0).(func(context.Context, *model.User) []*model.Repo); ok {
r0 = rf(ctx, u)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Repo)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *model.User) error); ok {
r1 = rf(ctx, u)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Status provides a mock function with given fields: ctx, u, r, b, link, proc
func (_m *Remote) Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
ret := _m.Called(ctx, u, r, b, link, proc)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *model.User, *model.Repo, *model.Build, string, *model.Proc) error); ok {
r0 = rf(ctx, u, r, b, link, proc)
} else {
r0 = ret.Error(0)
}
return r0
}
// Teams provides a mock function with given fields: ctx, u
func (_m *Remote) Teams(ctx context.Context, u *model.User) ([]*model.Team, error) {
ret := _m.Called(ctx, u)
var r0 []*model.Team
if rf, ok := ret.Get(0).(func(context.Context, *model.User) []*model.Team); ok {
r0 = rf(ctx, u)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Team)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *model.User) error); ok {
r1 = rf(ctx, u)
} else {
r1 = ret.Error(1)
}
return r0, r1
}

View file

@ -14,63 +14,61 @@
package remote package remote
//go:generate mockery -name Remote -output mock -case=underscore //go:generate mockery -name Remote -output mocks -case=underscore
import ( import (
"context"
"net/http" "net/http"
"github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/model"
"golang.org/x/net/context"
) )
// TODO: use pagination // TODO: use pagination
// TODO: use context
// TODO: add Driver() who return source forge back // TODO: add Driver() who return source forge back
type Remote interface { type Remote interface {
// Login authenticates the session and returns the // Login authenticates the session and returns the
// remote user details. // remote user details.
Login(w http.ResponseWriter, r *http.Request) (*model.User, error) Login(ctx context.Context, w http.ResponseWriter, r *http.Request) (*model.User, error)
// Auth authenticates the session and returns the remote user // Auth authenticates the session and returns the remote user
// login for the given token and secret // login for the given token and secret
Auth(token, secret string) (string, error) Auth(ctx context.Context, token, secret string) (string, error)
// Teams fetches a list of team memberships from the remote system. // Teams fetches a list of team memberships from the remote system.
Teams(u *model.User) ([]*model.Team, error) Teams(ctx context.Context, u *model.User) ([]*model.Team, error)
// Repo fetches the named repository from the remote system. // Repo fetches the named repository from the remote system.
Repo(u *model.User, owner, repo string) (*model.Repo, error) Repo(ctx context.Context, u *model.User, owner, name string) (*model.Repo, error)
// Repos fetches a list of repos from the remote system. // Repos fetches a list of repos from the remote system.
Repos(u *model.User) ([]*model.Repo, error) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error)
// Perm fetches the named repository permissions from // Perm fetches the named repository permissions from
// the remote system for the specified user. // the remote system for the specified user.
Perm(u *model.User, owner, repo string) (*model.Perm, error) Perm(ctx context.Context, u *model.User, owner, repo string) (*model.Perm, error)
// File fetches a file from the remote repository and returns in string // File fetches a file from the remote repository and returns in string
// format. // format.
File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) File(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error)
// Dir fetches a folder from the remote repository // Dir fetches a folder from the remote repository
Dir(u *model.User, r *model.Repo, b *model.Build, f string) ([]*FileMeta, error) Dir(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, f string) ([]*FileMeta, error)
// Status sends the commit status to the remote system. // Status sends the commit status to the remote system.
// An example would be the GitHub pull request status. // An example would be the GitHub pull request status.
Status(u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error Status(ctx context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error
// Netrc returns a .netrc file that can be used to clone // Netrc returns a .netrc file that can be used to clone
// private repositories from a remote system. // private repositories from a remote system.
Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error)
// Activate activates a repository by creating the post-commit hook. // Activate activates a repository by creating the post-commit hook.
Activate(u *model.User, r *model.Repo, link string) error Activate(ctx context.Context, u *model.User, r *model.Repo, link string) error
// Deactivate deactivates a repository by removing all previously created // Deactivate deactivates a repository by removing all previously created
// post-commit hooks matching the given link. // post-commit hooks matching the given link.
Deactivate(u *model.User, r *model.Repo, link string) error Deactivate(ctx context.Context, u *model.User, r *model.Repo, link string) error
// Hook parses the post-commit hook from the Request body and returns the // Hook parses the post-commit hook from the Request body and returns the
// required data in a standard format. // required data in a standard format.
@ -93,46 +91,46 @@ func (a ByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// returns true if the token was refreshed, false if the token was not refreshed, // returns true if the token was refreshed, false if the token was not refreshed,
// and error if it failed to refersh. // and error if it failed to refersh.
type Refresher interface { type Refresher interface {
Refresh(*model.User) (bool, error) Refresh(context.Context, *model.User) (bool, error)
} }
// Login authenticates the session and returns the // Login authenticates the session and returns the
// remote user details. // remote user details.
func Login(c context.Context, w http.ResponseWriter, r *http.Request) (*model.User, error) { func Login(c context.Context, w http.ResponseWriter, r *http.Request) (*model.User, error) {
return FromContext(c).Login(w, r) return FromContext(c).Login(c, w, r)
} }
// Auth authenticates the session and returns the remote user // Auth authenticates the session and returns the remote user
// login for the given token and secret // login for the given token and secret
func Auth(c context.Context, token, secret string) (string, error) { func Auth(c context.Context, token, secret string) (string, error) {
return FromContext(c).Auth(token, secret) return FromContext(c).Auth(c, token, secret)
} }
// Teams fetches a list of team memberships from the remote system. // Teams fetches a list of team memberships from the remote system.
func Teams(c context.Context, u *model.User) ([]*model.Team, error) { func Teams(c context.Context, u *model.User) ([]*model.Team, error) {
return FromContext(c).Teams(u) return FromContext(c).Teams(c, u)
} }
// Repo fetches the named repository from the remote system. // Repo fetches the named repository from the remote system.
func Repo(c context.Context, u *model.User, owner, repo string) (*model.Repo, error) { func Repo(c context.Context, u *model.User, owner, repo string) (*model.Repo, error) {
return FromContext(c).Repo(u, owner, repo) return FromContext(c).Repo(c, u, owner, repo)
} }
// Repos fetches a list of repos from the remote system. // Repos fetches a list of repos from the remote system.
func Repos(c context.Context, u *model.User) ([]*model.Repo, error) { func Repos(c context.Context, u *model.User) ([]*model.Repo, error) {
return FromContext(c).Repos(u) return FromContext(c).Repos(c, u)
} }
// Perm fetches the named repository permissions from // Perm fetches the named repository permissions from
// the remote system for the specified user. // the remote system for the specified user.
func Perm(c context.Context, u *model.User, owner, repo string) (*model.Perm, error) { func Perm(c context.Context, u *model.User, owner, repo string) (*model.Perm, error) {
return FromContext(c).Perm(u, owner, repo) return FromContext(c).Perm(c, u, owner, repo)
} }
// Status sends the commit status to the remote system. // Status sends the commit status to the remote system.
// An example would be the GitHub pull request status. // An example would be the GitHub pull request status.
func Status(c context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error { func Status(c context.Context, u *model.User, r *model.Repo, b *model.Build, link string, proc *model.Proc) error {
return FromContext(c).Status(u, r, b, link, proc) return FromContext(c).Status(c, u, r, b, link, proc)
} }
// Netrc returns a .netrc file that can be used to clone // Netrc returns a .netrc file that can be used to clone
@ -144,13 +142,13 @@ func Netrc(c context.Context, u *model.User, r *model.Repo) (*model.Netrc, error
// Activate activates a repository by creating the post-commit hook and // Activate activates a repository by creating the post-commit hook and
// adding the SSH deploy key, if applicable. // adding the SSH deploy key, if applicable.
func Activate(c context.Context, u *model.User, r *model.Repo, link string) error { func Activate(c context.Context, u *model.User, r *model.Repo, link string) error {
return FromContext(c).Activate(u, r, link) return FromContext(c).Activate(c, u, r, link)
} }
// Deactivate removes a repository by removing all the post-commit hooks // Deactivate removes a repository by removing all the post-commit hooks
// which are equal to link and removing the SSH deploy key. // which are equal to link and removing the SSH deploy key.
func Deactivate(c context.Context, u *model.User, r *model.Repo, link string) error { func Deactivate(c context.Context, u *model.User, r *model.Repo, link string) error {
return FromContext(c).Deactivate(u, r, link) return FromContext(c).Deactivate(c, u, r, link)
} }
// Hook parses the post-commit hook from the Request body // Hook parses the post-commit hook from the Request body
@ -168,5 +166,5 @@ func Refresh(c context.Context, u *model.User) (bool, error) {
if !ok { if !ok {
return false, nil return false, nil
} }
return refresher.Refresh(u) return refresher.Refresh(c, u)
} }

View file

@ -95,7 +95,7 @@ func SetPerm() gin.HandlerFunc {
user.Login, repo.FullName, err) user.Login, repo.FullName, err)
} }
if time.Unix(perm.Synced, 0).Add(time.Hour).Before(time.Now()) { if time.Unix(perm.Synced, 0).Add(time.Hour).Before(time.Now()) {
perm, err = remote.FromContext(c).Perm(user, repo.Owner, repo.Name) perm, err = remote.FromContext(c).Perm(c, user, repo.Owner, repo.Name)
if err == nil { if err == nil {
log.Debugf("Synced user permission for %s %s", user.Login, repo.FullName) log.Debugf("Synced user permission for %s %s", user.Login, repo.FullName)
perm.Repo = repo.FullName perm.Repo = repo.FullName

View file

@ -52,7 +52,7 @@ func Refresh(c *gin.Context) {
// attempts to refresh the access token. If the // attempts to refresh the access token. If the
// token is refreshed, we must also persist to the // token is refreshed, we must also persist to the
// database. // database.
ok, _ = refresher.Refresh(user) ok, _ = refresher.Refresh(c, user)
if ok { if ok {
err := store.UpdateUser(c, user) err := store.UpdateUser(c, user)
if err != nil { if err != nil {

View file

@ -1,6 +1,8 @@
package shared package shared
import ( import (
"context"
"errors"
"fmt" "fmt"
"strings" "strings"
"time" "time"
@ -27,21 +29,32 @@ func NewConfigFetcher(remote remote.Remote, user *model.User, repo *model.Repo,
} }
} }
// Fetch // Fetch pipeline config from source forge
// TODO: dedupe code func (cf *configFetcher) Fetch(ctx context.Context) (files []*remote.FileMeta, err error) {
func (cf *configFetcher) Fetch() (files []*remote.FileMeta, err error) {
var file []byte
config := strings.TrimSpace(cf.repo.Config)
logrus.Tracef("Start Fetching config for '%s'", cf.repo.FullName) logrus.Tracef("Start Fetching config for '%s'", cf.repo.FullName)
for i := 0; i < 5; i++ { // try to fetch 3 times, timeout is one second longer each time
select { for i := 0; i < 3; i++ {
case <-time.After(time.Second * time.Duration(i)): files, err = cf.fetch(ctx, time.Second*time.Duration(i), strings.TrimSpace(cf.repo.Config))
if errors.Is(err, context.DeadlineExceeded) {
continue
}
return
}
return
}
// fetch config by timeout
// TODO: dedupe code
func (cf *configFetcher) fetch(c context.Context, timeout time.Duration, config string) ([]*remote.FileMeta, error) {
ctx, cancel := context.WithTimeout(c, timeout)
defer cancel()
if len(config) > 0 { if len(config) > 0 {
logrus.Tracef("ConfigFetch[%s]: use user config '%s'", cf.repo.FullName, config)
// either a file // either a file
if !strings.HasSuffix(config, "/") { if !strings.HasSuffix(config, "/") {
file, err = cf.remote_.File(cf.user, cf.repo, cf.build, config) file, err := cf.remote_.File(ctx, cf.user, cf.repo, cf.build, config)
if err == nil && len(file) != 0 { if err == nil && len(file) != 0 {
logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config) logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config)
return []*remote.FileMeta{{ return []*remote.FileMeta{{
@ -52,58 +65,56 @@ func (cf *configFetcher) Fetch() (files []*remote.FileMeta, err error) {
} }
// or a folder // or a folder
files, err = cf.remote_.Dir(cf.user, cf.repo, cf.build, strings.TrimSuffix(config, "/")) files, err := cf.remote_.Dir(ctx, cf.user, cf.repo, cf.build, strings.TrimSuffix(config, "/"))
if err == nil { if err == nil && len(files) != 0 {
logrus.Tracef("ConfigFetch[%s]: found %d files in '%s'", cf.repo.FullName, len(files), config) logrus.Tracef("ConfigFetch[%s]: found %d files in '%s'", cf.repo.FullName, len(files), config)
return filterPipelineFiles(files), nil return filterPipelineFiles(files), nil
} }
} else {
return nil, fmt.Errorf("config '%s' not found: %s", config, err)
}
logrus.Tracef("ConfigFetch[%s]: user did not defined own config follow default procedure", cf.repo.FullName) logrus.Tracef("ConfigFetch[%s]: user did not defined own config follow default procedure", cf.repo.FullName)
// no user defined config so try .woodpecker/*.yml -> .woodpecker.yml -> .drone.yml // no user defined config so try .woodpecker/*.yml -> .woodpecker.yml -> .drone.yml
// test .woodpecker/ folder // test .woodpecker/ folder
// if folder is not supported we will get a "Not implemented" error and continue // if folder is not supported we will get a "Not implemented" error and continue
config = ".woodpecker" config = ".woodpecker"
files, err = cf.remote_.Dir(cf.user, cf.repo, cf.build, config) files, err := cf.remote_.Dir(ctx, cf.user, cf.repo, cf.build, config)
logrus.Tracef("ConfigFetch[%s]: found %d files in '%s'", cf.repo.FullName, len(files), config)
files = filterPipelineFiles(files) files = filterPipelineFiles(files)
if err == nil && len(files) != 0 { if err == nil && len(files) != 0 {
logrus.Tracef("ConfigFetch[%s]: found %d files in '%s'", cf.repo.FullName, len(files), config)
return files, nil return files, nil
} }
config = ".woodpecker.yml" config = ".woodpecker.yml"
file, err = cf.remote_.File(cf.user, cf.repo, cf.build, config) file, err := cf.remote_.File(ctx, cf.user, cf.repo, cf.build, config)
if err == nil && len(file) != 0 { if err == nil && len(file) != 0 {
logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config) logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config)
return []*remote.FileMeta{{ return []*remote.FileMeta{{
Name: ".woodpecker.yml", Name: config,
Data: file, Data: file,
}}, nil }}, nil
} }
config = ".drone.yml" config = ".drone.yml"
file, err = cf.remote_.File(cf.user, cf.repo, cf.build, config) file, err = cf.remote_.File(ctx, cf.user, cf.repo, cf.build, config)
if err == nil && len(file) != 0 { if err == nil && len(file) != 0 {
logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config) logrus.Tracef("ConfigFetch[%s]: found file '%s'", cf.repo.FullName, config)
return []*remote.FileMeta{{ return []*remote.FileMeta{{
Name: ".drone.yml", Name: config,
Data: file, Data: file,
}}, nil }}, nil
} }
if err == nil && len(files) == 0 { select {
return nil, fmt.Errorf("ConfigFetcher: Fallback did not found config") case <-ctx.Done():
return nil, ctx.Err()
default:
return []*remote.FileMeta{}, fmt.Errorf("ConfigFetcher: Fallback did not found config: %s", err)
} }
} }
// TODO: retry loop is inactive and could maybe be fixed/deleted
return nil, err
}
}
return []*remote.FileMeta{}, nil
}
func filterPipelineFiles(files []*remote.FileMeta) []*remote.FileMeta { func filterPipelineFiles(files []*remote.FileMeta) []*remote.FileMeta {
var res []*remote.FileMeta var res []*remote.FileMeta

View file

@ -1,6 +1,7 @@
package shared_test package shared_test
import ( import (
"context"
"fmt" "fmt"
"path/filepath" "path/filepath"
"testing" "testing"
@ -216,7 +217,7 @@ func TestFetch(t *testing.T) {
r := new(mocks.Remote) r := new(mocks.Remote)
dirs := map[string][]*remote.FileMeta{} dirs := map[string][]*remote.FileMeta{}
for _, file := range tt.files { for _, file := range tt.files {
r.On("File", mock.Anything, mock.Anything, mock.Anything, file.name).Return(file.data, nil) r.On("File", mock.Anything, mock.Anything, mock.Anything, mock.Anything, file.name).Return(file.data, nil)
path := filepath.Dir(file.name) path := filepath.Dir(file.name)
if path != "." { if path != "." {
dirs[path] = append(dirs[path], &remote.FileMeta{ dirs[path] = append(dirs[path], &remote.FileMeta{
@ -227,12 +228,12 @@ func TestFetch(t *testing.T) {
} }
for path, files := range dirs { for path, files := range dirs {
r.On("Dir", mock.Anything, mock.Anything, mock.Anything, path).Return(files, nil) r.On("Dir", mock.Anything, mock.Anything, mock.Anything, mock.Anything, path).Return(files, nil)
} }
// if the previous mocks do not match return not found errors // if the previous mocks do not match return not found errors
r.On("File", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("File not found")) r.On("File", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("File not found"))
r.On("Dir", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("Directory not found")) r.On("Dir", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("Directory not found"))
configFetcher := shared.NewConfigFetcher( configFetcher := shared.NewConfigFetcher(
r, r,
@ -240,7 +241,7 @@ func TestFetch(t *testing.T) {
repo, repo,
&model.Build{Commit: "89ab7b2d6bfb347144ac7c557e638ab402848fee"}, &model.Build{Commit: "89ab7b2d6bfb347144ac7c557e638ab402848fee"},
) )
files, err := configFetcher.Fetch() files, err := configFetcher.Fetch(context.Background())
if tt.expectedError && err == nil { if tt.expectedError && err == nil {
t.Fatal("expected an error") t.Fatal("expected an error")
} else if !tt.expectedError && err != nil { } else if !tt.expectedError && err != nil {

View file

@ -15,6 +15,7 @@
package shared package shared
import ( import (
"context"
"time" "time"
"github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/model"
@ -24,7 +25,7 @@ import (
// UserSyncer syncs the user repository and permissions. // UserSyncer syncs the user repository and permissions.
type UserSyncer interface { type UserSyncer interface {
Sync(user *model.User) error Sync(ctx context.Context, user *model.User) error
} }
type Syncer struct { type Syncer struct {
@ -62,9 +63,9 @@ func (s *Syncer) SetFilter(fn FilterFunc) {
s.Match = fn s.Match = fn
} }
func (s *Syncer) Sync(user *model.User) error { func (s *Syncer) Sync(ctx context.Context, user *model.User) error {
unix := time.Now().Unix() - (3601) // force immediate expiration. note 1 hour expiration is hard coded at the moment unix := time.Now().Unix() - (3601) // force immediate expiration. note 1 hour expiration is hard coded at the moment
repos, err := s.Remote.Repos(user) repos, err := s.Remote.Repos(ctx, user)
if err != nil { if err != nil {
return err return err
} }