// Copyright 2019 Drone IO, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package contents import ( "context" "strings" "time" "github.com/drone/drone/core" "github.com/drone/go-scm/scm" ) // default number of backoff attempts. var attempts = 3 // default time to wait after failed attempt. var wait = time.Second * 15 // New returns a new FileService. func New(client *scm.Client, renewer core.Renewer) core.FileService { return &service{ client: client, renewer: renewer, attempts: attempts, wait: wait, } } type service struct { renewer core.Renewer client *scm.Client attempts int wait time.Duration } func (s *service) Find(ctx context.Context, user *core.User, repo, commit, ref, path string) (*core.File, error) { // TODO(gogs) ability to fetch a yaml by pull request ref. // it is not currently possible to fetch the yaml // configuration file from a pull request sha. This // workaround defaults to master. if s.client.Driver == scm.DriverGogs && strings.HasPrefix(ref, "refs/pull") { commit = "master" } // TODO(gogs) ability to fetch a file in tag from commit sha. // this is a workaround for gogs which does not allow // fetching a file by commit sha for a tag. This forces // fetching a file by reference instead. if s.client.Driver == scm.DriverGogs && strings.HasPrefix(ref, "refs/tag") { commit = ref } err := s.renewer.Renew(ctx, user, false) if err != nil { return nil, err } ctx = context.WithValue(ctx, scm.TokenKey{}, &scm.Token{ Token: user.Token, Refresh: user.Refresh, }) content, err := s.findRetry(ctx, repo, path, commit) if err != nil { return nil, err } return &core.File{ Data: content.Data, Hash: []byte{}, }, nil } // helper function attempts to get the yaml configuration file // with backoff on failure. This may be required due to eventual // consistency issues with the github datastore. func (s *service) findRetry(ctx context.Context, repo, path, commit string) (content *scm.Content, err error) { for i := 0; i < s.attempts; i++ { content, _, err = s.client.Contents.Find(ctx, repo, path, commit) // if no error is returned we can exit immediately. if err == nil { return } // wait a few seconds before retry. according to github // support 30 seconds total should be enough time. we // try 3 x 15 seconds, giving a total of 45 seconds. time.Sleep(s.wait) } return }