614 lines
23 KiB
Go
614 lines
23 KiB
Go
package gitlab
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha1"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/gokins/gokins/hook"
|
|
"github.com/sirupsen/logrus"
|
|
"hash"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"runtime/debug"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func Parse(req *http.Request, secret string) (hook.WebHook, error) {
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
logrus.Warnf("WebhookService Parse err:%+v", err)
|
|
logrus.Warnf("%s", string(debug.Stack()))
|
|
}
|
|
}()
|
|
data, err := ioutil.ReadAll(
|
|
io.LimitReader(req.Body, 10000000),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var wb hook.WebHook
|
|
switch req.Header.Get(hook.GITLAB_EVENT) {
|
|
case hook.GITLAB_EVENT_PUSH:
|
|
wb, err = parsePushHook(data)
|
|
case hook.GITLAB_EVENT_NOTE:
|
|
wb, err = parseCommentHook(data)
|
|
case hook.GITLAB_EVENT_PR:
|
|
wb, err = parsePullRequestHook(data)
|
|
default:
|
|
return nil, errors.New(fmt.Sprintf("hook含有未知的header:%v", req.Header.Get(hook.GITEE_EVENT)))
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sig := req.Header.Get("X-Gitlab-Token")
|
|
if secret != sig {
|
|
return wb, errors.New("密钥不正确")
|
|
}
|
|
return wb, nil
|
|
}
|
|
|
|
func Validate(h func() hash.Hash, message, key []byte, signature string) bool {
|
|
decoded, err := hex.DecodeString(signature)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return validate(h, message, key, decoded)
|
|
}
|
|
|
|
func validatePrefix(message, key []byte, signature string) bool {
|
|
parts := strings.Split(signature, "=")
|
|
if len(parts) != 2 {
|
|
return false
|
|
}
|
|
switch parts[0] {
|
|
case "sha1":
|
|
return Validate(sha1.New, message, key, parts[1])
|
|
case "sha256":
|
|
return Validate(sha256.New, message, key, parts[1])
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func validate(h func() hash.Hash, message, key, signature []byte) bool {
|
|
mac := hmac.New(h, key)
|
|
mac.Write(message)
|
|
sum := mac.Sum(nil)
|
|
return hmac.Equal(signature, sum)
|
|
}
|
|
|
|
func parseCommentHook(data []byte) (*hook.PullRequestCommentHook, error) {
|
|
gp := new(gitlabCommentHook)
|
|
err := json.Unmarshal(data, gp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return convertCommentHook(gp)
|
|
}
|
|
|
|
func parsePullRequestHook(data []byte) (*hook.PullRequestHook, error) {
|
|
gp := new(gitlabPRHook)
|
|
err := json.Unmarshal(data, gp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if gp.ObjectAttributes.Action != "" {
|
|
if gp.ObjectAttributes.Action != hook.ActionOpen && gp.ObjectAttributes.Action != hook.ActionUpdate {
|
|
return nil, fmt.Errorf("action is %v", gp.ObjectAttributes.Action)
|
|
}
|
|
}
|
|
return convertPullRequestHook(gp), nil
|
|
}
|
|
|
|
func parsePushHook(data []byte) (*hook.PushHook, error) {
|
|
gp := new(gitlabPushHook)
|
|
err := json.Unmarshal(data, gp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return convertPushHook(gp), nil
|
|
}
|
|
func convertPullRequestHook(gp *gitlabPRHook) *hook.PullRequestHook {
|
|
return &hook.PullRequestHook{
|
|
Action: gp.ObjectAttributes.Action,
|
|
Repo: hook.Repository{
|
|
Ref: gp.ObjectAttributes.SourceBranch,
|
|
Sha: gp.ObjectAttributes.LastCommit.Id,
|
|
CloneURL: gp.ObjectAttributes.Source.HttpUrl,
|
|
Branch: gp.ObjectAttributes.SourceBranch,
|
|
Description: gp.ObjectAttributes.Source.Description,
|
|
FullName: gp.ObjectAttributes.Source.PathWithNamespace,
|
|
GitHttpURL: gp.ObjectAttributes.Source.GitHttpUrl,
|
|
GitShhURL: gp.ObjectAttributes.Source.SshUrl,
|
|
GitURL: gp.ObjectAttributes.Source.Url,
|
|
HtmlURL: gp.ObjectAttributes.Source.WebUrl,
|
|
SshURL: gp.ObjectAttributes.Source.SshUrl,
|
|
Name: gp.ObjectAttributes.Source.Name,
|
|
URL: gp.ObjectAttributes.Source.Url,
|
|
Owner: gp.User.Username,
|
|
RepoType: "gitlab",
|
|
RepoOpenid: strconv.Itoa(gp.Project.Id),
|
|
},
|
|
TargetRepo: hook.Repository{
|
|
Ref: gp.ObjectAttributes.TargetBranch,
|
|
Sha: gp.ObjectAttributes.LastCommit.Id,
|
|
CloneURL: gp.ObjectAttributes.Target.HttpUrl,
|
|
Branch: gp.ObjectAttributes.TargetBranch,
|
|
Description: gp.ObjectAttributes.Target.Description,
|
|
FullName: gp.ObjectAttributes.Target.PathWithNamespace,
|
|
GitHttpURL: gp.ObjectAttributes.Target.GitHttpUrl,
|
|
GitShhURL: gp.ObjectAttributes.Target.SshUrl,
|
|
GitURL: gp.ObjectAttributes.Target.Url,
|
|
HtmlURL: gp.ObjectAttributes.Target.WebUrl,
|
|
SshURL: gp.ObjectAttributes.Target.SshUrl,
|
|
Name: gp.ObjectAttributes.Target.Name,
|
|
URL: gp.ObjectAttributes.Target.Url,
|
|
Owner: gp.User.Username,
|
|
RepoType: "gitlab",
|
|
RepoOpenid: strconv.Itoa(gp.Project.Id),
|
|
},
|
|
Sender: hook.User{
|
|
UserName: gp.User.Username,
|
|
},
|
|
PullRequest: hook.PullRequest{
|
|
Number: gp.ObjectAttributes.Iid,
|
|
Title: gp.ObjectAttributes.Title,
|
|
Base: hook.Reference{
|
|
Name: gp.ObjectAttributes.Source.Name,
|
|
Sha: gp.ObjectAttributes.LastCommit.Id,
|
|
},
|
|
Head: hook.Reference{
|
|
Name: gp.ObjectAttributes.Target.Name,
|
|
Sha: gp.ObjectAttributes.LastCommit.Id,
|
|
},
|
|
Author: hook.User{
|
|
UserName: gp.User.Username,
|
|
},
|
|
Created: time.Time{},
|
|
Updated: time.Time{},
|
|
},
|
|
}
|
|
}
|
|
func convertPushHook(gp *gitlabPushHook) *hook.PushHook {
|
|
branch := ""
|
|
if gp.Ref != "" {
|
|
if len(strings.Split(gp.Ref, "/")) > 2 {
|
|
branch = strings.Split(gp.Ref, "/")[2]
|
|
}
|
|
}
|
|
return &hook.PushHook{
|
|
Ref: gp.Ref,
|
|
Repo: hook.Repository{
|
|
Ref: gp.Ref,
|
|
Sha: gp.After,
|
|
CloneURL: gp.Project.HttpUrl,
|
|
Branch: branch,
|
|
Description: gp.Repository.Description,
|
|
FullName: gp.Project.PathWithNamespace,
|
|
GitHttpURL: gp.Repository.GitHttpUrl,
|
|
GitShhURL: gp.Repository.GitSshUrl,
|
|
GitURL: gp.Repository.Url,
|
|
SshURL: gp.Project.SshUrl,
|
|
Name: gp.Repository.Name,
|
|
URL: gp.Repository.Url,
|
|
Owner: gp.UserUsername,
|
|
RepoType: "gitlab",
|
|
RepoOpenid: strconv.Itoa(gp.ProjectId),
|
|
},
|
|
Before: gp.Before,
|
|
After: gp.After,
|
|
Sender: hook.User{
|
|
UserName: gp.UserUsername,
|
|
},
|
|
}
|
|
}
|
|
func convertCommentHook(gp *gitlabCommentHook) (*hook.PullRequestCommentHook, error) {
|
|
return &hook.PullRequestCommentHook{
|
|
Action: hook.EVENTS_TYPE_COMMENT,
|
|
Repo: hook.Repository{
|
|
Ref: gp.MergeRequest.SourceBranch,
|
|
Sha: gp.MergeRequest.LastCommit.Id,
|
|
CloneURL: gp.MergeRequest.Source.HttpUrl,
|
|
Branch: gp.MergeRequest.SourceBranch,
|
|
Description: gp.MergeRequest.Source.Description,
|
|
FullName: gp.MergeRequest.Source.PathWithNamespace,
|
|
GitHttpURL: gp.MergeRequest.Source.GitHttpUrl,
|
|
GitShhURL: gp.MergeRequest.Source.SshUrl,
|
|
GitURL: gp.MergeRequest.Source.Url,
|
|
HtmlURL: gp.MergeRequest.Source.WebUrl,
|
|
SshURL: gp.MergeRequest.Source.SshUrl,
|
|
Name: gp.MergeRequest.Source.Name,
|
|
URL: gp.MergeRequest.Source.Url,
|
|
Owner: gp.User.Username,
|
|
RepoType: "gitlab",
|
|
RepoOpenid: strconv.Itoa(gp.Project.Id),
|
|
},
|
|
TargetRepo: hook.Repository{
|
|
Ref: gp.MergeRequest.TargetBranch,
|
|
Sha: gp.MergeRequest.LastCommit.Id,
|
|
CloneURL: gp.MergeRequest.Target.HttpUrl,
|
|
Branch: gp.MergeRequest.TargetBranch,
|
|
Description: gp.MergeRequest.Target.Description,
|
|
FullName: gp.MergeRequest.Target.PathWithNamespace,
|
|
GitHttpURL: gp.MergeRequest.Target.GitHttpUrl,
|
|
GitShhURL: gp.MergeRequest.Target.SshUrl,
|
|
GitURL: gp.MergeRequest.Target.Url,
|
|
HtmlURL: gp.MergeRequest.Target.WebUrl,
|
|
SshURL: gp.MergeRequest.Target.SshUrl,
|
|
Name: gp.MergeRequest.Target.Name,
|
|
URL: gp.MergeRequest.Target.Url,
|
|
Owner: gp.User.Username,
|
|
RepoType: "gitlab",
|
|
RepoOpenid: strconv.Itoa(gp.Project.Id),
|
|
},
|
|
Comment: hook.Comment{
|
|
Body: gp.ObjectAttributes.Note,
|
|
Author: hook.User{
|
|
UserName: gp.User.Username,
|
|
},
|
|
},
|
|
PullRequest: hook.PullRequest{
|
|
Number: gp.MergeRequest.Iid,
|
|
Title: gp.MergeRequest.Title,
|
|
Base: hook.Reference{
|
|
Name: gp.MergeRequest.Source.Name,
|
|
Sha: gp.MergeRequest.LastCommit.Id,
|
|
},
|
|
Head: hook.Reference{
|
|
Name: gp.MergeRequest.Target.Name,
|
|
Sha: gp.MergeRequest.LastCommit.Id,
|
|
},
|
|
Author: hook.User{
|
|
UserName: gp.User.Username,
|
|
},
|
|
Created: time.Time{},
|
|
Updated: time.Time{},
|
|
},
|
|
Sender: hook.User{
|
|
UserName: gp.User.Username,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
type gitlabPushHook struct {
|
|
ObjectKind string `json:"object_kind"`
|
|
EventName string `json:"event_name"`
|
|
Before string `json:"before"`
|
|
After string `json:"after"`
|
|
Ref string `json:"ref"`
|
|
CheckoutSha string `json:"checkout_sha"`
|
|
Message interface{} `json:"message"`
|
|
UserId int `json:"user_id"`
|
|
UserName string `json:"user_name"`
|
|
UserUsername string `json:"user_username"`
|
|
UserEmail string `json:"user_email"`
|
|
UserAvatar string `json:"user_avatar"`
|
|
ProjectId int `json:"project_id"`
|
|
Project struct {
|
|
Id int `json:"id"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
WebUrl string `json:"web_url"`
|
|
AvatarUrl interface{} `json:"avatar_url"`
|
|
GitSshUrl string `json:"git_ssh_url"`
|
|
GitHttpUrl string `json:"git_http_url"`
|
|
Namespace string `json:"namespace"`
|
|
VisibilityLevel int `json:"visibility_level"`
|
|
PathWithNamespace string `json:"path_with_namespace"`
|
|
DefaultBranch string `json:"default_branch"`
|
|
CiConfigPath string `json:"ci_config_path"`
|
|
Homepage string `json:"homepage"`
|
|
Url string `json:"url"`
|
|
SshUrl string `json:"ssh_url"`
|
|
HttpUrl string `json:"http_url"`
|
|
} `json:"project"`
|
|
Commits []struct {
|
|
Id string `json:"id"`
|
|
Message string `json:"message"`
|
|
Title string `json:"title"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
Url string `json:"url"`
|
|
Author struct {
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
} `json:"author"`
|
|
Added []interface{} `json:"added"`
|
|
Modified []string `json:"modified"`
|
|
Removed []interface{} `json:"removed"`
|
|
} `json:"commits"`
|
|
TotalCommitsCount int `json:"total_commits_count"`
|
|
PushOptions struct {
|
|
} `json:"push_options"`
|
|
Repository struct {
|
|
Name string `json:"name"`
|
|
Url string `json:"url"`
|
|
Description string `json:"description"`
|
|
Homepage string `json:"homepage"`
|
|
GitHttpUrl string `json:"git_http_url"`
|
|
GitSshUrl string `json:"git_ssh_url"`
|
|
VisibilityLevel int `json:"visibility_level"`
|
|
} `json:"repository"`
|
|
}
|
|
type gitlabPRHook struct {
|
|
ObjectKind string `json:"object_kind"`
|
|
EventType string `json:"event_type"`
|
|
User struct {
|
|
Id int `json:"id"`
|
|
Name string `json:"name"`
|
|
Username string `json:"username"`
|
|
AvatarUrl string `json:"avatar_url"`
|
|
Email string `json:"email"`
|
|
} `json:"user"`
|
|
Project struct {
|
|
Id int `json:"id"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
WebUrl string `json:"web_url"`
|
|
AvatarUrl interface{} `json:"avatar_url"`
|
|
GitSshUrl string `json:"git_ssh_url"`
|
|
GitHttpUrl string `json:"git_http_url"`
|
|
Namespace string `json:"namespace"`
|
|
VisibilityLevel int `json:"visibility_level"`
|
|
PathWithNamespace string `json:"path_with_namespace"`
|
|
DefaultBranch string `json:"default_branch"`
|
|
CiConfigPath string `json:"ci_config_path"`
|
|
Homepage string `json:"homepage"`
|
|
Url string `json:"url"`
|
|
SshUrl string `json:"ssh_url"`
|
|
HttpUrl string `json:"http_url"`
|
|
} `json:"project"`
|
|
ObjectAttributes struct {
|
|
AssigneeId interface{} `json:"assignee_id"`
|
|
AuthorId int `json:"author_id"`
|
|
CreatedAt string `json:"created_at"`
|
|
Description string `json:"description"`
|
|
HeadPipelineId interface{} `json:"head_pipeline_id"`
|
|
Id int `json:"id"`
|
|
Iid int64 `json:"iid"`
|
|
LastEditedAt interface{} `json:"last_edited_at"`
|
|
LastEditedById interface{} `json:"last_edited_by_id"`
|
|
MergeCommitSha interface{} `json:"merge_commit_sha"`
|
|
MergeError interface{} `json:"merge_error"`
|
|
MergeParams struct {
|
|
ForceRemoveSourceBranch string `json:"force_remove_source_branch"`
|
|
} `json:"merge_params"`
|
|
MergeStatus string `json:"merge_status"`
|
|
MergeUserId interface{} `json:"merge_user_id"`
|
|
MergeWhenPipelineSucceeds bool `json:"merge_when_pipeline_succeeds"`
|
|
MilestoneId interface{} `json:"milestone_id"`
|
|
SourceBranch string `json:"source_branch"`
|
|
SourceProjectId int `json:"source_project_id"`
|
|
StateId int `json:"state_id"`
|
|
TargetBranch string `json:"target_branch"`
|
|
TargetProjectId int `json:"target_project_id"`
|
|
TimeEstimate int `json:"time_estimate"`
|
|
Title string `json:"title"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
UpdatedById interface{} `json:"updated_by_id"`
|
|
Url string `json:"url"`
|
|
Source struct {
|
|
Id int `json:"id"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
WebUrl string `json:"web_url"`
|
|
AvatarUrl interface{} `json:"avatar_url"`
|
|
GitSshUrl string `json:"git_ssh_url"`
|
|
GitHttpUrl string `json:"git_http_url"`
|
|
Namespace string `json:"namespace"`
|
|
VisibilityLevel int `json:"visibility_level"`
|
|
PathWithNamespace string `json:"path_with_namespace"`
|
|
DefaultBranch string `json:"default_branch"`
|
|
CiConfigPath string `json:"ci_config_path"`
|
|
Homepage string `json:"homepage"`
|
|
Url string `json:"url"`
|
|
SshUrl string `json:"ssh_url"`
|
|
HttpUrl string `json:"http_url"`
|
|
} `json:"source"`
|
|
Target struct {
|
|
Id int `json:"id"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
WebUrl string `json:"web_url"`
|
|
AvatarUrl interface{} `json:"avatar_url"`
|
|
GitSshUrl string `json:"git_ssh_url"`
|
|
GitHttpUrl string `json:"git_http_url"`
|
|
Namespace string `json:"namespace"`
|
|
VisibilityLevel int `json:"visibility_level"`
|
|
PathWithNamespace string `json:"path_with_namespace"`
|
|
DefaultBranch string `json:"default_branch"`
|
|
CiConfigPath string `json:"ci_config_path"`
|
|
Homepage string `json:"homepage"`
|
|
Url string `json:"url"`
|
|
SshUrl string `json:"ssh_url"`
|
|
HttpUrl string `json:"http_url"`
|
|
} `json:"target"`
|
|
LastCommit struct {
|
|
Id string `json:"id"`
|
|
Message string `json:"message"`
|
|
Title string `json:"title"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
Url string `json:"url"`
|
|
Author struct {
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
} `json:"author"`
|
|
} `json:"last_commit"`
|
|
WorkInProgress bool `json:"work_in_progress"`
|
|
TotalTimeSpent int `json:"total_time_spent"`
|
|
TimeChange int `json:"time_change"`
|
|
HumanTotalTimeSpent interface{} `json:"human_total_time_spent"`
|
|
HumanTimeChange interface{} `json:"human_time_change"`
|
|
HumanTimeEstimate interface{} `json:"human_time_estimate"`
|
|
AssigneeIds []interface{} `json:"assignee_ids"`
|
|
State string `json:"state"`
|
|
Action string `json:"action"`
|
|
} `json:"object_attributes"`
|
|
Labels []interface{} `json:"labels"`
|
|
Changes struct {
|
|
MergeStatus struct {
|
|
Previous string `json:"previous"`
|
|
Current string `json:"current"`
|
|
} `json:"merge_status"`
|
|
} `json:"changes"`
|
|
Repository struct {
|
|
Name string `json:"name"`
|
|
Url string `json:"url"`
|
|
Description string `json:"description"`
|
|
Homepage string `json:"homepage"`
|
|
} `json:"repository"`
|
|
}
|
|
type gitlabCommentHook struct {
|
|
ObjectKind string `json:"object_kind"`
|
|
EventType string `json:"event_type"`
|
|
User struct {
|
|
Id int `json:"id"`
|
|
Name string `json:"name"`
|
|
Username string `json:"username"`
|
|
AvatarUrl string `json:"avatar_url"`
|
|
Email string `json:"email"`
|
|
} `json:"user"`
|
|
ProjectId int `json:"project_id"`
|
|
Project struct {
|
|
Id int `json:"id"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
WebUrl string `json:"web_url"`
|
|
AvatarUrl interface{} `json:"avatar_url"`
|
|
GitSshUrl string `json:"git_ssh_url"`
|
|
GitHttpUrl string `json:"git_http_url"`
|
|
Namespace string `json:"namespace"`
|
|
VisibilityLevel int `json:"visibility_level"`
|
|
PathWithNamespace string `json:"path_with_namespace"`
|
|
DefaultBranch string `json:"default_branch"`
|
|
CiConfigPath string `json:"ci_config_path"`
|
|
Homepage string `json:"homepage"`
|
|
Url string `json:"url"`
|
|
SshUrl string `json:"ssh_url"`
|
|
HttpUrl string `json:"http_url"`
|
|
} `json:"project"`
|
|
ObjectAttributes struct {
|
|
Attachment interface{} `json:"attachment"`
|
|
AuthorId int `json:"author_id"`
|
|
ChangePosition interface{} `json:"change_position"`
|
|
CommitId interface{} `json:"commit_id"`
|
|
CreatedAt string `json:"created_at"`
|
|
DiscussionId string `json:"discussion_id"`
|
|
Id int `json:"id"`
|
|
LineCode interface{} `json:"line_code"`
|
|
Note string `json:"note"`
|
|
NoteableId int `json:"noteable_id"`
|
|
NoteableType string `json:"noteable_type"`
|
|
OriginalPosition interface{} `json:"original_position"`
|
|
Position interface{} `json:"position"`
|
|
ProjectId int `json:"project_id"`
|
|
ResolvedAt interface{} `json:"resolved_at"`
|
|
ResolvedById interface{} `json:"resolved_by_id"`
|
|
ResolvedByPush interface{} `json:"resolved_by_push"`
|
|
StDiff interface{} `json:"st_diff"`
|
|
System bool `json:"system"`
|
|
Type interface{} `json:"type"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
UpdatedById interface{} `json:"updated_by_id"`
|
|
Description string `json:"description"`
|
|
Url string `json:"url"`
|
|
} `json:"object_attributes"`
|
|
Repository struct {
|
|
Name string `json:"name"`
|
|
Url string `json:"url"`
|
|
Description string `json:"description"`
|
|
Homepage string `json:"homepage"`
|
|
} `json:"repository"`
|
|
MergeRequest struct {
|
|
AssigneeId interface{} `json:"assignee_id"`
|
|
AuthorId int `json:"author_id"`
|
|
CreatedAt string `json:"created_at"`
|
|
Description string `json:"description"`
|
|
HeadPipelineId interface{} `json:"head_pipeline_id"`
|
|
Id int `json:"id"`
|
|
Iid int64 `json:"iid"`
|
|
LastEditedAt interface{} `json:"last_edited_at"`
|
|
LastEditedById interface{} `json:"last_edited_by_id"`
|
|
MergeCommitSha interface{} `json:"merge_commit_sha"`
|
|
MergeError interface{} `json:"merge_error"`
|
|
MergeParams struct {
|
|
ForceRemoveSourceBranch string `json:"force_remove_source_branch"`
|
|
} `json:"merge_params"`
|
|
MergeStatus string `json:"merge_status"`
|
|
MergeUserId interface{} `json:"merge_user_id"`
|
|
MergeWhenPipelineSucceeds bool `json:"merge_when_pipeline_succeeds"`
|
|
MilestoneId interface{} `json:"milestone_id"`
|
|
SourceBranch string `json:"source_branch"`
|
|
SourceProjectId int `json:"source_project_id"`
|
|
StateId int `json:"state_id"`
|
|
TargetBranch string `json:"target_branch"`
|
|
TargetProjectId int `json:"target_project_id"`
|
|
TimeEstimate int `json:"time_estimate"`
|
|
Title string `json:"title"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
UpdatedById interface{} `json:"updated_by_id"`
|
|
Url string `json:"url"`
|
|
Source struct {
|
|
Id int `json:"id"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
WebUrl string `json:"web_url"`
|
|
AvatarUrl interface{} `json:"avatar_url"`
|
|
GitSshUrl string `json:"git_ssh_url"`
|
|
GitHttpUrl string `json:"git_http_url"`
|
|
Namespace string `json:"namespace"`
|
|
VisibilityLevel int `json:"visibility_level"`
|
|
PathWithNamespace string `json:"path_with_namespace"`
|
|
DefaultBranch string `json:"default_branch"`
|
|
CiConfigPath string `json:"ci_config_path"`
|
|
Homepage string `json:"homepage"`
|
|
Url string `json:"url"`
|
|
SshUrl string `json:"ssh_url"`
|
|
HttpUrl string `json:"http_url"`
|
|
} `json:"source"`
|
|
Target struct {
|
|
Id int `json:"id"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
WebUrl string `json:"web_url"`
|
|
AvatarUrl interface{} `json:"avatar_url"`
|
|
GitSshUrl string `json:"git_ssh_url"`
|
|
GitHttpUrl string `json:"git_http_url"`
|
|
Namespace string `json:"namespace"`
|
|
VisibilityLevel int `json:"visibility_level"`
|
|
PathWithNamespace string `json:"path_with_namespace"`
|
|
DefaultBranch string `json:"default_branch"`
|
|
CiConfigPath string `json:"ci_config_path"`
|
|
Homepage string `json:"homepage"`
|
|
Url string `json:"url"`
|
|
SshUrl string `json:"ssh_url"`
|
|
HttpUrl string `json:"http_url"`
|
|
} `json:"target"`
|
|
LastCommit struct {
|
|
Id string `json:"id"`
|
|
Message string `json:"message"`
|
|
Title string `json:"title"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
Url string `json:"url"`
|
|
Author struct {
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
} `json:"author"`
|
|
} `json:"last_commit"`
|
|
WorkInProgress bool `json:"work_in_progress"`
|
|
TotalTimeSpent int `json:"total_time_spent"`
|
|
TimeChange int `json:"time_change"`
|
|
HumanTotalTimeSpent interface{} `json:"human_total_time_spent"`
|
|
HumanTimeChange interface{} `json:"human_time_change"`
|
|
HumanTimeEstimate interface{} `json:"human_time_estimate"`
|
|
AssigneeIds []interface{} `json:"assignee_ids"`
|
|
State string `json:"state"`
|
|
} `json:"merge_request"`
|
|
}
|