Spaces:
Paused
Paused
| // Copyright 2024 Woodpecker Authors | |
| // | |
| // 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 forgejo | |
| import ( | |
| "io" | |
| "net/http" | |
| "slices" | |
| "strings" | |
| "github.com/rs/zerolog/log" | |
| "go.woodpecker-ci.org/woodpecker/v3/server/forge/common" | |
| "go.woodpecker-ci.org/woodpecker/v3/server/forge/types" | |
| "go.woodpecker-ci.org/woodpecker/v3/server/model" | |
| ) | |
| const ( | |
| hookEvent = "X-Forgejo-Event" | |
| hookPush = "push" | |
| hookCreated = "create" | |
| hookPullRequest = "pull_request" | |
| hookRelease = "release" | |
| actionOpen = "opened" | |
| actionSync = "synchronized" | |
| actionClose = "closed" | |
| actionEdited = "edited" | |
| actionLabelUpdate = "label_updated" | |
| actionLabelCleared = "label_cleared" | |
| actionMilestoned = "milestoned" | |
| actionDeMilestoned = "demilestoned" | |
| actionAssigned = "assigned" | |
| actionUnAssigned = "unassigned" | |
| actionReopen = "reopened" | |
| refBranch = "branch" | |
| refTag = "tag" | |
| ) | |
| var actionList = []string{ | |
| actionOpen, | |
| actionSync, | |
| actionClose, | |
| actionEdited, | |
| actionLabelUpdate, | |
| actionMilestoned, | |
| actionDeMilestoned, | |
| actionLabelCleared, | |
| actionAssigned, | |
| actionUnAssigned, | |
| actionReopen, | |
| } | |
| func supportedAction(action string) bool { | |
| return slices.Contains(actionList, action) | |
| } | |
| // parseHook parses a Forgejo hook from an http.Request and returns | |
| // Repo and Pipeline detail. If a hook type is unsupported nil values are returned. | |
| func parseHook(r *http.Request) (*model.Repo, *model.Pipeline, error) { | |
| hookType := r.Header.Get(hookEvent) | |
| switch hookType { | |
| case hookPush: | |
| return parsePushHook(r.Body) | |
| case hookCreated: | |
| return parseCreatedHook(r.Body) | |
| case hookPullRequest: | |
| return parsePullRequestHook(r.Body) | |
| case hookRelease: | |
| return parseReleaseHook(r.Body) | |
| } | |
| log.Debug().Msgf("unsupported hook type: '%s'", hookType) | |
| return nil, nil, &types.ErrIgnoreEvent{Event: hookType} | |
| } | |
| // parsePushHook parses a push hook and returns the Repo and Pipeline details. | |
| // If the commit type is unsupported nil values are returned. | |
| func parsePushHook(payload io.Reader) (repo *model.Repo, pipeline *model.Pipeline, err error) { | |
| push, err := parsePush(payload) | |
| if err != nil { | |
| return nil, nil, err | |
| } | |
| // ignore push events for tags | |
| if strings.HasPrefix(push.Ref, "refs/tags/") { | |
| return nil, nil, nil | |
| } | |
| // TODO is this even needed? | |
| if push.RefType == refBranch { | |
| return nil, nil, nil | |
| } | |
| repo = toRepo(push.Repo) | |
| pipeline = pipelineFromPush(push) | |
| return repo, pipeline, err | |
| } | |
| // parseCreatedHook parses a push hook and returns the Repo and Pipeline details. | |
| // If the commit type is unsupported nil values are returned. | |
| func parseCreatedHook(payload io.Reader) (repo *model.Repo, pipeline *model.Pipeline, err error) { | |
| push, err := parsePush(payload) | |
| if err != nil { | |
| return nil, nil, err | |
| } | |
| if push.RefType != refTag { | |
| return nil, nil, nil | |
| } | |
| repo = toRepo(push.Repo) | |
| pipeline = pipelineFromTag(push) | |
| return repo, pipeline, nil | |
| } | |
| // parsePullRequestHook parses a pull_request hook and returns the Repo and Pipeline details. | |
| func parsePullRequestHook(payload io.Reader) (*model.Repo, *model.Pipeline, error) { | |
| var ( | |
| repo *model.Repo | |
| pipeline *model.Pipeline | |
| ) | |
| pr, err := parsePullRequest(payload) | |
| if err != nil { | |
| return nil, nil, err | |
| } | |
| // Only trigger pipelines for supported event types | |
| if !supportedAction(pr.Action) { | |
| log.Debug().Msgf("pull_request action is '%s'. Only '%s' are supported", pr.Action, strings.Join(actionList, "', '")) | |
| return nil, nil, nil | |
| } | |
| repo = toRepo(pr.Repo) | |
| pipeline = pipelineFromPullRequest(pr) | |
| // all other actions return the state of labels after the actions where done ... so we should too | |
| if pr.Action == actionLabelCleared { | |
| pipeline.PullRequestLabels = []string{} | |
| } | |
| if pr.Action == actionDeMilestoned { | |
| pipeline.PullRequestMilestone = "" | |
| } | |
| for i := range pipeline.EventReason { | |
| pipeline.EventReason[i] = common.NormalizeEventReason(pipeline.EventReason[i]) | |
| } | |
| return repo, pipeline, err | |
| } | |
| // parseReleaseHook parses a release hook and returns the Repo and Pipeline details. | |
| func parseReleaseHook(payload io.Reader) (*model.Repo, *model.Pipeline, error) { | |
| var ( | |
| repo *model.Repo | |
| pipeline *model.Pipeline | |
| ) | |
| release, err := parseRelease(payload) | |
| if err != nil { | |
| return nil, nil, err | |
| } | |
| repo = toRepo(release.Repo) | |
| pipeline = pipelineFromRelease(release) | |
| return repo, pipeline, err | |
| } | |