Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ name: Create and publish a Docker image

on:
push:
tags:
branches:
- "!main"
- "*"
tags:
- "v*.*.*"

env:
Expand Down Expand Up @@ -42,7 +45,7 @@ jobs:
uses: docker/setup-buildx-action@v1

- name: Cache Docker layers
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
### Commands
- !merge
- !check
- !update

### Setup
1. Invite bot ([@mergeapprovebot](https://gitlab.com/mergeapprovebot)) in your repository as **maintainer** (you can revoke permissions from usual developers in order to prevent merging)
Expand Down
9 changes: 3 additions & 6 deletions bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ func Handler(c echo.Context) error {
return err
}

err = hook.ParseRequest(c.Request())
if err != nil {
if err = hook.ParseRequest(c.Request()); err != nil {
slog.Error("ParseRequest", "err", err)
return err
}
Expand All @@ -55,8 +54,7 @@ func Handler(c echo.Context) error {

if f, ok := handlerFuncs[hook.Event]; ok {
go func() {
err := f(providerName, hook)
if err != nil {
if err := f(providerName, hook); err != nil {
slog.Error("handlerFunc", "err", err)
}
}()
Expand All @@ -71,8 +69,7 @@ var (

func handle(onEvent string, funcHandler func(string, *webhook.Webhook) error) {
handlerFuncs[onEvent] = func(provider string, hook *webhook.Webhook) error {
err := funcHandler(provider, hook)
if err != nil {
if err := funcHandler(provider, hook); err != nil {
// metrics.CommandFailedInc(hook.GetCmd(), provider)
return err
}
Expand Down
24 changes: 16 additions & 8 deletions commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,32 @@ import (
func init() {
handle("!merge", MergeCmd)
handle("!check", CheckCmd)
handle("!update", UpdateBranchCmd)
handle(webhook.OnNewMR, NewMR)
}

func UpdateBranchCmd(providerName string, hook *webhook.Webhook) error {
command, err := handlers.New(providerName)
if err != nil {
return err
}

if err := command.UpdateFromMaster(hook.GetProjectID(), hook.GetID()); err != nil {
slog.Error("updateBranchCmd", "error", err)
return command.LeaveComment(hook.GetProjectID(), hook.GetID(), "❌ i couldn't update branch from master")
}

return err
}

func MergeCmd(providerName string, hook *webhook.Webhook) error {
command, err := handlers.New(providerName)
if err != nil {
slog.Error("command merge", "err", err)
return err
}

ok, text, err := command.Merge(hook.GetProjectID(), hook.GetID())
if err != nil {
slog.Error("command merge", "err", err)
return err
}

Expand All @@ -34,13 +47,11 @@ func MergeCmd(providerName string, hook *webhook.Webhook) error {
func CheckCmd(providerName string, hook *webhook.Webhook) error {
command, err := handlers.New(providerName)
if err != nil {
slog.Error("command merge", "err", err)
return err
}

ok, text, err := command.IsValid(hook.GetProjectID(), hook.GetID())
if err != nil {
slog.Error("command check", "err", err)
return err
}

Expand All @@ -54,13 +65,10 @@ func CheckCmd(providerName string, hook *webhook.Webhook) error {
func NewMR(providerName string, hook *webhook.Webhook) error {
command, err := handlers.New(providerName)
if err != nil {
slog.Error("new mr", "err", err)
return err
}

err = command.Greetings(hook.GetProjectID(), hook.GetID())
if err != nil {
slog.Error("new mr", "err", err)
if err = command.Greetings(hook.GetProjectID(), hook.GetID()); err != nil {
return err
}

Expand Down
5 changes: 5 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ services:
bot:
image: ghcr.io/gasoid/merge-bot:latest
restart: always
volumes:
- tls-cache:/tmp/tls/.cache
ports:
- "443:443"
- "8080:8080"
env_file:
- bot.env
logging:
driver: syslog

volumes:
tls-cache:
15 changes: 9 additions & 6 deletions handlers/gitlab/gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ func (g *GitlabProvider) loadMR(projectId, mergeId int) error {
}

func (g *GitlabProvider) UpdateFromMaster(projectId, mergeId int) error {
err := g.loadMR(projectId, mergeId)
if err != nil {
if err := g.loadMR(projectId, mergeId); err != nil {
return err
}

Expand All @@ -54,8 +53,13 @@ func (g *GitlabProvider) UpdateFromMaster(projectId, mergeId int) error {
return handlers.RepoSizeError
}

err = handlers.MergeMaster(tokenUsername, os.Getenv(gitlabToken), project.HTTPURLToRepo, g.mr.SourceBranch, g.mr.TargetBranch)
if err != nil {
if err = handlers.MergeMaster(
tokenUsername,
os.Getenv(gitlabToken),
project.HTTPURLToRepo,
g.mr.SourceBranch,
g.mr.TargetBranch,
); err != nil {
return err
}

Expand Down Expand Up @@ -122,8 +126,7 @@ func (g *GitlabProvider) GetFailedPipelines() (int, error) {
}

func (g *GitlabProvider) IsValid(projectId, mergeId int) (bool, error) {
err := g.loadMR(projectId, mergeId)
if err != nil {
if err := g.loadMR(projectId, mergeId); err != nil {
return false, err
}

Expand Down
55 changes: 36 additions & 19 deletions handlers/merge.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package handlers

import (
"crypto/md5"
"encoding/hex"
"errors"
"fmt"
"log/slog"
"net/url"
"os"

"github.com/ldez/go-git-cmd-wrapper/v2/checkout"
"github.com/ldez/go-git-cmd-wrapper/v2/clone"
"github.com/ldez/go-git-cmd-wrapper/v2/config"
"github.com/ldez/go-git-cmd-wrapper/v2/git"
"github.com/ldez/go-git-cmd-wrapper/v2/merge"
"github.com/ldez/go-git-cmd-wrapper/v2/push"
Expand All @@ -22,38 +22,55 @@ func MergeMaster(username, password, repoUrl, branchName, master string) error {
if username != "" && password != "" {
parsedUrl, err := url.Parse(repoUrl)
if err != nil {
slog.Error("url.Parse", "err", err)
return err
}
parsedUrl.User = url.UserPassword(username, password)
repoUrl = parsedUrl.String()
}

hasher := md5.New()
hasher.Write([]byte(repoUrl))
dir := hex.EncodeToString(hasher.Sum([]byte(branchName)))

if _, err := os.Stat(dir); !os.IsNotExist(err) {
return errors.New("directory exists")
dir, err := os.MkdirTemp("", "merge-bot")
if err != nil {
slog.Debug("temp dir error")
return err
}

defer os.RemoveAll(dir)

_, err := git.Clone(clone.Repository(repoUrl), clone.Branch(branchName), clone.Directory(dir))
if err != nil {
slog.Error("git.Clone", "err", err)
if _, err := git.Clone(clone.Repository(repoUrl), clone.Directory(dir)); err != nil {
slog.Debug("git clone error", "dir", dir)
return err
}

_, err = git.Merge(merge.NoFf, merge.Commits(master))
if err != nil {
slog.Error("git.Merge", "err", err)
if err := os.Chdir(dir); err != nil {
slog.Debug("chdir error")
return err
}

_, err = git.Push(push.Remote(defaultRemote))
if err != nil {
slog.Error("git.Push", "err", err)
if _, err := git.Config(config.Entry("user.email", fmt.Sprintf("%s@localhost", username))); err != nil {
slog.Debug("git config error", "user.email", fmt.Sprintf("%s@localhost", username))
return err
}

if _, err := git.Config(config.Entry("user.name", username)); err != nil {
slog.Debug("git config error", "user.name", username)
return err
}

if _, err := git.Checkout(checkout.Branch(branchName)); err != nil {
slog.Debug("git checkout error", "branch", branchName)
return err
}

if _, err := git.Merge(merge.Commits(master), merge.M("update from master")); err != nil {
slog.Debug("git merge error")
if _, err := git.Merge(merge.NoFf, merge.Commits(master), merge.M("update from master")); err != nil {
slog.Debug("git merge --noff error")
return err
}
}

if _, err := git.Push(push.Remote(defaultRemote), push.RefSpec(branchName)); err != nil {
slog.Debug("git push error")
return err
}

Expand Down
29 changes: 17 additions & 12 deletions handlers/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ func (r *Request) LoadInfoAndConfig(projectId, id int) error {
}

func (r *Request) IsValid(projectId, id int) (bool, string, error) {
err := r.LoadInfoAndConfig(projectId, id)
if err != nil {
if err := r.LoadInfoAndConfig(projectId, id); err != nil {
return false, "", err
}

Expand Down Expand Up @@ -69,8 +68,7 @@ func (r *Request) ParseConfig(content string) (*Config, error) {
AutoMasterMerge: false,
}

err := yaml.Unmarshal([]byte(content), mrConfig)
if err != nil {
if err := yaml.Unmarshal([]byte(content), mrConfig); err != nil {
return nil, err
}
return mrConfig, nil
Expand All @@ -81,8 +79,7 @@ func (r *Request) LeaveComment(projectId, id int, message string) error {
}

func (r *Request) Greetings(projectId, id int) error {
err := r.LoadInfoAndConfig(projectId, id)
if err != nil {
if err := r.LoadInfoAndConfig(projectId, id); err != nil {
return err
}

Expand All @@ -92,17 +89,15 @@ func (r *Request) Greetings(projectId, id int) error {
}

buf := &bytes.Buffer{}
err = tmpl.Execute(buf, r.config)
if err != nil {
if err = tmpl.Execute(buf, r.config); err != nil {
return err
}

return r.LeaveComment(projectId, id, buf.String())
}

func (r *Request) Merge(projectId, id int) (bool, string, error) {
err := r.LoadInfoAndConfig(projectId, id)
if err != nil {
if err := r.LoadInfoAndConfig(projectId, id); err != nil {
return false, "", err
}

Expand All @@ -112,12 +107,22 @@ func (r *Request) Merge(projectId, id int) (bool, string, error) {
}

if ok, text, err := r.IsValid(projectId, id); ok {
err := r.provider.Merge(projectId, id, fmt.Sprintf("%s\nMerged by MergeApproveBot", r.info.Title))
if err != nil {
if err := r.provider.Merge(projectId, id, fmt.Sprintf("%s\nMerged by MergeApproveBot", r.info.Title)); err != nil {
return false, "", err
}
return true, "", nil
} else {
return false, text, err
}
}

func (r *Request) UpdateFromMaster(projectId, id int) error {
if err := r.LoadInfoAndConfig(projectId, id); err != nil {
return err
}

if err := r.provider.UpdateFromMaster(projectId, id); err != nil {
return err
}
return nil
}
3 changes: 1 addition & 2 deletions webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ func (w *Webhook) ParseRequest(request *http.Request) error {
return &Error{text: "Request is not provided"}
}

err := w.provider.ParseRequest(request)
if err != nil {
if err := w.provider.ParseRequest(request); err != nil {
return err
}

Expand Down
Loading