// 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 web import ( "context" "net/http" "net/http/httputil" "os" "strconv" "time" "github.com/sirupsen/logrus" "github.com/drone/drone/core" "github.com/drone/drone/logger" "github.com/drone/go-scm/scm" ) // this is intended for local testing and instructs the handler // to print the contents of the hook to stdout. var debugPrintHook = false func init() { debugPrintHook, _ = strconv.ParseBool( os.Getenv("DRONE_DEBUG_DUMP_HOOK"), ) } // HandleHook returns an http.HandlerFunc that handles webhooks // triggered by source code management. func HandleHook( repos core.RepositoryStore, builds core.BuildStore, triggerer core.Triggerer, parser core.HookParser, ) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if debugPrintHook { // if DRONE_DEBUG_DUMP_HOOK=true print the http.Request // headers and body to stdout. out, _ := httputil.DumpRequest(r, true) os.Stderr.Write(out) } hook, remote, err := parser.Parse(r, func(slug string) string { namespace, name := scm.Split(slug) repo, err := repos.FindName(r.Context(), namespace, name) if err != nil { logrus.WithFields( logrus.Fields{ "namespace": namespace, "name": name, }).Debugln("cannot find repository") return "" } return repo.Signer }) if err != nil { logrus.Debugf("cannot parse webhook: %s", err) writeBadRequest(w, err) return } if hook == nil { logrus.Debugf("webhook ignored") return } // TODO handle ping requests // TODO consider using scm.Repository in the function callback. log := logrus.WithFields(logrus.Fields{ "namespace": remote.Namespace, "name": remote.Name, "event": hook.Event, "commit": hook.After, }) log.Debugln("webhook parsed") repo, err := repos.FindName(r.Context(), remote.Namespace, remote.Name) if err != nil { log = log.WithError(err) log.Debugln("cannot find repository") writeNotFound(w, err) return } if !repo.Active { log.Debugln("ignore webhook, repository inactive") w.WriteHeader(200) return } ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5) ctx = logger.WithContext(ctx, log) defer cancel() if hook.Event == core.EventPush && hook.Action == core.ActionDelete { log.WithField("branch", hook.Target).Debugln("branch deleted") builds.DeleteBranch(ctx, repo.ID, hook.Target) w.WriteHeader(http.StatusNoContent) return } if hook.Event == core.EventPullRequest && hook.Action == core.ActionClose { log.WithField("ref", hook.Ref).Debugln("pull request closed") builds.DeletePull(ctx, repo.ID, scm.ExtractPullRequest(hook.Ref)) w.WriteHeader(http.StatusNoContent) return } builds, err := triggerer.Trigger(ctx, repo, hook) if err != nil { writeError(w, err) return } writeJSON(w, builds, 200) } }