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
16 changes: 8 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,27 @@ jobs:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.21'
go-version: '1.24.1'
cache: false
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@v8
with:
version: v1.54
version: v2.1

tests:
# needs: [golanglint]
name: Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- run: mkdir build && touch build/index.html
- name: Set up Go 1.x
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: ^1.21
go-version: ^1.24
id: go

- uses: actions/cache@v3
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Log in to the Container registry
uses: docker/login-action@v1
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.22-bookworm AS builder
FROM golang:1.24.3-bookworm AS builder
WORKDIR /code
ADD go.mod /code/
ADD go.sum /code/
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,30 @@ In case you want to test the bot you can use gitlab cloud bot.
1. bot.env:
```
GITLAB_TOKEN="your_token"
#TLS_ENABLED="false"
#TLS_DOMAIN="domain.your-example.com"
```

2. run docker-compose
```
docker-compose up -d
```

you can configure bot using cli args as well:
```bash
Usage of merge-bot:
-debug
whether debug logging is enabled, default is false (also via DEBUG)
-gitlab-token string
in order to communicate with gitlab api, bot needs token (also via GITLAB_TOKEN)
-gitlab-url string
in case of self-hosted gitlab, you need to set this var up (also via GITLAB_URL)
-tls-domain string
which domain is used for ssl certificate (also via TLS_DOMAIN)
-tls-enabled
whether tls enabled or not, bot will use Letsencrypt, default is false (also via TLS_ENABLED)
```

### Setup for Gitlab Cloud
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)
2. Add webhook `https://mergebot.tools/mergebot/webhook/gitlab/your_username_or_company_name/repo-name/` (Comments and merge request events)
Expand Down
18 changes: 16 additions & 2 deletions bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"log/slog"
"mergebot/config"
"mergebot/handlers"
"mergebot/webhook"
"os"
Expand All @@ -15,6 +16,16 @@ import (
"golang.org/x/crypto/acme/autocert"
)

var (
tlsEnabled bool
tlsDomain string
)

func init() {
config.BoolVar(&tlsEnabled, "tls-enabled", false, "whether tls enabled or not, bot will use Letsencrypt (also via TLS_ENABLED)")
config.StringVar(&tlsDomain, "tls-domain", "", "which domain is used for ssl certificate (also via TLS_DOMAIN)")
}

func start() {
e := echo.New()

Expand All @@ -24,10 +35,13 @@ func start() {
e.GET("/healthy", healthcheck)
e.POST("/mergebot/webhook/:provider/:owner/:repo/", Handler)

if os.Getenv("TLS_ENABLED") == "true" {
if tlsEnabled {
tmpDir := path.Join(os.TempDir(), "tls", ".cache")

e.AutoTLSManager.HostPolicy = autocert.HostWhitelist(os.Getenv("TLS_DOMAIN"))
if tlsDomain != "" {
e.AutoTLSManager.HostPolicy = autocert.HostWhitelist(tlsDomain)
}

e.AutoTLSManager.Cache = autocert.DirCache(tmpDir)
e.AutoTLSManager.Prompt = autocert.AcceptTOS
e.Logger.Fatal(e.StartAutoTLS(":443"))
Expand Down
28 changes: 28 additions & 0 deletions config/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package config

import (
"flag"
"fmt"
"os"

"github.com/peterbourgon/ff/v3"
)

var (
fs = flag.NewFlagSet("merge-bot", flag.ContinueOnError)
)

func StringVar(p *string, name string, value string, usage string) {
fs.StringVar(p, name, value, usage)
}

func BoolVar(p *bool, name string, value bool, usage string) {
fs.BoolVar(p, name, value, usage)
}

func Parse() {
if err := ff.Parse(fs, os.Args[1:], ff.WithEnvVars()); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
module mergebot

go 1.22.3
go 1.24.1

require (
github.com/labstack/echo/v4 v4.12.0
github.com/ldez/go-git-cmd-wrapper/v2 v2.7.0
github.com/peterbourgon/ff/v3 v3.4.0
github.com/stretchr/testify v1.9.0
github.com/xanzy/go-gitlab v0.93.2
golang.org/x/crypto v0.22.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc=
github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down
20 changes: 14 additions & 6 deletions handlers/gitlab/gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,27 @@ package gitlab
import (
b64 "encoding/base64"
"log/slog"
"mergebot/config"
"mergebot/handlers"
"os"

"github.com/xanzy/go-gitlab"
)

func init() {
handlers.Register("gitlab", New)

config.StringVar(&gitlabToken, "gitlab-token", "", "in order to communicate with gitlab api, bot needs token (also via GITLAB_TOKEN)")
config.StringVar(&gitlabURL, "gitlab-url", "", "in case of self-hosted gitlab, you need to set this var up (also via GITLAB_URL)")
}

var (
gitlabToken string
gitlabURL string
)

const (
gitlabToken = "GITLAB_TOKEN"
gitlabUrl = "GITLAB_URL"
// gitlabToken = "GITLAB_TOKEN"
// gitlabUrl = "GITLAB_URL"
tokenUsername = "oauth2"
maxRepoSize = 1000 * 1000 * 500 // 500Mb
)
Expand Down Expand Up @@ -58,7 +66,7 @@ func (g *GitlabProvider) UpdateFromMaster(projectId, mergeId int) error {

return handlers.MergeMaster(
tokenUsername,
os.Getenv(gitlabToken),
gitlabToken,
project.HTTPURLToRepo,
g.mr.SourceBranch,
g.mr.TargetBranch,
Expand Down Expand Up @@ -219,13 +227,13 @@ func New() handlers.RequestProvider {
var err error
var p GitlabProvider

token := os.Getenv(gitlabToken)
token := gitlabToken
if token == "" {
slog.Error("gitlab init", "err", "gitlab requires token, please set env variable GITLAB_TOKEN")
return nil
}

urlInstance := os.Getenv(gitlabUrl)
urlInstance := gitlabURL

if urlInstance != "" {
p.client, err = gitlab.NewClient(token, gitlab.WithBaseURL(urlInstance))
Expand Down
1 change: 1 addition & 0 deletions handlers/merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (
defaultRemote = "origin"
)

//nolint:errcheck
func MergeMaster(username, password, repoUrl, branchName, master string) error {
if username != "" && password != "" {
parsedUrl, err := url.Parse(repoUrl)
Expand Down
11 changes: 10 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@ package main

import (
"log/slog"
"mergebot/config"
_ "mergebot/handlers/gitlab"
_ "mergebot/webhook/gitlab"
// _ "mergebot/metrics"
)

func main() {
slog.SetLogLoggerLevel(slog.LevelDebug)
var debug bool
config.BoolVar(&debug, "debug", false, "whether debug logging is enabled (also via DEBUG)")
config.Parse()

if debug {
slog.SetLogLoggerLevel(slog.LevelDebug)
slog.Debug("debug is enabled")
}

start()
}
Loading