func PostRepoKey(c *gin.Context) { repo := session.Repo(c) keys, err := store.GetKey(c, repo) if err != nil { c.String(404, "Error fetching repository key") return } body, err := ioutil.ReadAll(c.Request.Body) if err != nil { c.String(500, "Error reading private key from body. %s", err) return } pkey := crypto.UnmarshalPrivateKey(body) if pkey == nil { c.String(500, "Cannot unmarshal private key. Invalid format.") return } keys.Public = string(crypto.MarshalPublicKey(&pkey.PublicKey)) keys.Private = string(crypto.MarshalPrivateKey(pkey)) err = store.UpdateKey(c, keys) if err != nil { c.String(500, "Error updating repository key") return } c.String(201, keys.Public) }
func GetRepoKey(c *gin.Context) { repo := session.Repo(c) keys, err := store.GetKey(c, repo) if err != nil { c.AbortWithError(http.StatusNotFound, err) } else { c.String(http.StatusOK, keys.Public) } }
func GetRepoKey(c *gin.Context) { repo := session.Repo(c) keys, err := store.GetKey(c, repo) if err != nil { c.String(404, "Error fetching repository key") } else { c.String(http.StatusOK, keys.Public) } }
func ShowRepoConf(c *gin.Context) { user := session.User(c) repo := session.Repo(c) key, _ := store.GetKey(c, repo) token, _ := token.New( token.CsrfToken, user.Login, ).Sign(user.Hash) c.HTML(200, "repo_config.html", gin.H{ "User": user, "Repo": repo, "Key": key, "Csrf": token, "Link": httputil.GetURL(c.Request), }) }
func PostSecure(c *gin.Context) { repo := session.Repo(c) in, err := ioutil.ReadAll(c.Request.Body) if err != nil { c.AbortWithError(http.StatusBadRequest, err) return } // we found some strange characters included in // the yaml file when entered into a browser textarea. // these need to be removed in = bytes.Replace(in, []byte{'\xA0'}, []byte{' '}, -1) // make sure the Yaml is valid format to prevent // a malformed value from being used in the build err = yaml.Unmarshal(in, &yaml.MapSlice{}) if err != nil { c.String(http.StatusBadRequest, err.Error()) return } key, err := store.GetKey(c, repo) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } // encrypts using go-jose out, err := crypto.Encrypt(string(in), key.Private) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } c.String(http.StatusOK, out) }
func PostBuild(c *gin.Context) { remote_ := remote.FromContext(c) repo := session.Repo(c) num, err := strconv.Atoi(c.Param("number")) if err != nil { c.AbortWithError(http.StatusBadRequest, err) return } user, err := store.GetUser(c, repo.UserID) if err != nil { log.Errorf("failure to find repo owner %s. %s", repo.FullName, err) c.AbortWithError(500, err) return } build, err := store.GetBuildNumber(c, repo, num) if err != nil { log.Errorf("failure to get build %d. %s", num, err) c.AbortWithError(404, err) return } // if the remote has a refresh token, the current access token // may be stale. Therefore, we should refresh prior to dispatching // the job. if refresher, ok := remote_.(remote.Refresher); ok { ok, _ := refresher.Refresh(user) if ok { store.UpdateUser(c, user) } } // fetch the .drone.yml file from the database raw, sec, err := remote_.Script(user, repo, build) if err != nil { log.Errorf("failure to get .drone.yml for %s. %s", repo.FullName, err) c.AbortWithError(404, err) return } key, _ := store.GetKey(c, repo) netrc, err := remote_.Netrc(user, repo) if err != nil { log.Errorf("failure to generate netrc for %s. %s", repo.FullName, err) c.AbortWithError(500, err) return } jobs, err := store.GetJobList(c, build) if err != nil { log.Errorf("failure to get build %d jobs. %s", build.Number, err) c.AbortWithError(404, err) return } // must not restart a running build if build.Status == model.StatusPending || build.Status == model.StatusRunning { c.AbortWithStatus(409) return } // todo move this to database tier // and wrap inside a transaction build.Status = model.StatusPending build.Started = 0 build.Finished = 0 build.Enqueued = time.Now().UTC().Unix() for _, job := range jobs { job.Status = model.StatusPending job.Started = 0 job.Finished = 0 job.ExitCode = 0 job.Enqueued = build.Enqueued store.UpdateJob(c, job) } err = store.UpdateBuild(c, build) if err != nil { c.AbortWithStatus(500) return } c.JSON(202, build) // get the previous build so taht we can send // on status change notifications last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID) engine_ := context.Engine(c) go engine_.Schedule(c.Copy(), &engine.Task{ User: user, Repo: repo, Build: build, BuildPrev: last, Jobs: jobs, Keys: key, Netrc: netrc, Config: string(raw), Secret: string(sec), System: &model.System{ Link: httputil.GetURL(c.Request), Plugins: strings.Split(os.Getenv("PLUGIN_FILTER"), " "), Globals: strings.Split(os.Getenv("PLUGIN_PARAMS"), " "), }, }) }
func PostHook(c *gin.Context) { remote_ := remote.FromContext(c) tmprepo, build, err := remote_.Hook(c.Request) if err != nil { log.Errorf("failure to parse hook. %s", err) c.AbortWithError(400, err) return } if build == nil { c.Writer.WriteHeader(200) return } if tmprepo == nil { log.Errorf("failure to ascertain repo from hook.") c.Writer.WriteHeader(400) return } // skip the build if any case-insensitive combination of the words "skip" and "ci" // wrapped in square brackets appear in the commit message skipMatch := skipRe.FindString(build.Message) if len(skipMatch) > 0 { log.Infof("ignoring hook. %s found in %s", skipMatch, build.Commit) c.Writer.WriteHeader(204) return } repo, err := store.GetRepoOwnerName(c, tmprepo.Owner, tmprepo.Name) if err != nil { log.Errorf("failure to find repo %s/%s from hook. %s", tmprepo.Owner, tmprepo.Name, err) c.AbortWithError(404, err) return } // get the token and verify the hook is authorized parsed, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) { return repo.Hash, nil }) if err != nil { log.Errorf("failure to parse token from hook for %s. %s", repo.FullName, err) c.AbortWithError(400, err) return } if parsed.Text != repo.FullName { log.Errorf("failure to verify token from hook. Expected %s, got %s", repo.FullName, parsed.Text) c.AbortWithStatus(403) return } if repo.UserID == 0 { log.Warnf("ignoring hook. repo %s has no owner.", repo.FullName) c.Writer.WriteHeader(204) return } var skipped = true if (build.Event == model.EventPush && repo.AllowPush) || (build.Event == model.EventPull && repo.AllowPull) || (build.Event == model.EventDeploy && repo.AllowDeploy) || (build.Event == model.EventTag && repo.AllowTag) { skipped = false } if skipped { log.Infof("ignoring hook. repo %s is disabled for %s events.", repo.FullName, build.Event) c.Writer.WriteHeader(204) return } user, err := store.GetUser(c, repo.UserID) if err != nil { log.Errorf("failure to find repo owner %s. %s", repo.FullName, err) c.AbortWithError(500, err) return } // if there is no email address associated with the pull request, // we lookup the email address based on the authors github login. // // my initial hesitation with this code is that it has the ability // to expose your email address. At the same time, your email address // is already exposed in the public .git log. So while some people will // a small number of people will probably be upset by this, I'm not sure // it is actually that big of a deal. if len(build.Email) == 0 { author, err := store.GetUserLogin(c, build.Author) if err == nil { build.Email = author.Email } } // if the remote has a refresh token, the current access token // may be stale. Therefore, we should refresh prior to dispatching // the job. if refresher, ok := remote_.(remote.Refresher); ok { ok, _ := refresher.Refresh(user) if ok { store.UpdateUser(c, user) } } // fetch the .drone.yml file from the database raw, sec, err := remote_.Script(user, repo, build) if err != nil { log.Errorf("failure to get .drone.yml for %s. %s", repo.FullName, err) c.AbortWithError(404, err) return } axes, err := matrix.Parse(string(raw)) if err != nil { log.Errorf("failure to calculate matrix for %s. %s", repo.FullName, err) c.AbortWithError(400, err) return } if len(axes) == 0 { axes = append(axes, matrix.Axis{}) } netrc, err := remote_.Netrc(user, repo) if err != nil { log.Errorf("failure to generate netrc for %s. %s", repo.FullName, err) c.AbortWithError(500, err) return } key, _ := store.GetKey(c, repo) // verify the branches can be built vs skipped yconfig, _ := yaml.Parse(string(raw)) var match = false for _, branch := range yconfig.Branches { if branch == build.Branch { match = true break } match, _ = filepath.Match(branch, build.Branch) if match { break } } if !match && len(yconfig.Branches) != 0 { log.Infof("ignoring hook. yaml file excludes repo and branch %s %s", repo.FullName, build.Branch) c.AbortWithStatus(200) return } // update some build fields build.Status = model.StatusPending build.RepoID = repo.ID // and use a transaction var jobs []*model.Job for num, axis := range axes { jobs = append(jobs, &model.Job{ BuildID: build.ID, Number: num + 1, Status: model.StatusPending, Environment: axis, }) } err = store.CreateBuild(c, build, jobs...) if err != nil { log.Errorf("failure to save commit for %s. %s", repo.FullName, err) c.AbortWithError(500, err) return } c.JSON(200, build) url := fmt.Sprintf("%s/%s/%d", httputil.GetURL(c.Request), repo.FullName, build.Number) err = remote_.Status(user, repo, build, url) if err != nil { log.Errorf("error setting commit status for %s/%d", repo.FullName, build.Number) } // get the previous build so taht we can send // on status change notifications last, _ := store.GetBuildLastBefore(c, repo, build.Branch, build.ID) engine_ := context.Engine(c) go engine_.Schedule(c.Copy(), &engine.Task{ User: user, Repo: repo, Build: build, BuildPrev: last, Jobs: jobs, Keys: key, Netrc: netrc, Config: string(raw), Secret: string(sec), System: &model.System{ Link: httputil.GetURL(c.Request), Plugins: strings.Split(os.Getenv("PLUGIN_FILTER"), " "), Globals: strings.Split(os.Getenv("PLUGIN_PARAMS"), " "), }, }) }